diff --git a/Clocker/AppDelegate.swift b/Clocker/AppDelegate.swift index 73a2517..c56f7e1 100644 --- a/Clocker/AppDelegate.swift +++ b/Clocker/AppDelegate.swift @@ -8,7 +8,7 @@ import FirebaseCrashlytics open class AppDelegate: NSObject, NSApplicationDelegate { 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 panelObserver: NSKeyValueObservation? @@ -84,7 +84,6 @@ open class AppDelegate: NSObject, NSApplicationDelegate { let floatingWindow = FloatingWindowController.shared() floatingWindow.openPreferences(NSButton()) } else { - let panelController = PanelController.shared() panelController.openPreferences(NSButton()) } } @@ -107,6 +106,11 @@ open class AppDelegate: NSObject, NSApplicationDelegate { } 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. 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) { NSApplication.shared.activate(ignoringOtherApps: true) let alert = NSAlert() diff --git a/Clocker/Clocker/Clocker.entitlements b/Clocker/Clocker/Clocker.entitlements index 1754713..9f43c40 100644 --- a/Clocker/Clocker/Clocker.entitlements +++ b/Clocker/Clocker/Clocker.entitlements @@ -2,10 +2,6 @@ - com.apple.developer.icloud-container-identifiers - - com.apple.developer.ubiquity-kvstore-identifier - $(TeamIdentifierPrefix)$(CFBundleIdentifier) com.apple.security.app-sandbox com.apple.security.cs.disable-library-validation diff --git a/Clocker/Clocker/ar.lproj/Localizable.strings b/Clocker/Clocker/ar.lproj/Localizable.strings index 9652a9e..4a0d690 100644 --- a/Clocker/Clocker/ar.lproj/Localizable.strings +++ b/Clocker/Clocker/ar.lproj/Localizable.strings @@ -158,3 +158,6 @@ "New Zealand" = "New Zealand"; "Florida" = "Florida"; "San Francisco" = "San Francisco"; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/Clocker/ca.lproj/Localizable.strings b/Clocker/Clocker/ca.lproj/Localizable.strings index d4d47b5..d64f419 100644 --- a/Clocker/Clocker/ca.lproj/Localizable.strings +++ b/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"; "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."; "Happy Weekend." = "Happy Weekend."; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/Clocker/de.lproj/Localizable.strings b/Clocker/Clocker/de.lproj/Localizable.strings index 30cb9aa..f26777e 100644 --- a/Clocker/Clocker/de.lproj/Localizable.strings +++ b/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"; "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."; "Happy Weekend." = "Happy Weekend."; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/Clocker/en.lproj/Localizable.strings b/Clocker/Clocker/en.lproj/Localizable.strings index 0840228..0df6b45 100644 --- a/Clocker/Clocker/en.lproj/Localizable.strings +++ b/Clocker/Clocker/en.lproj/Localizable.strings @@ -165,6 +165,9 @@ "Copied to Clipboard" = "Copied to Clipboard"; // 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."; "Happy Weekend." = "Happy Weekend."; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/Clocker/es.lproj/Localizable.strings b/Clocker/Clocker/es.lproj/Localizable.strings index 7870ae7..ed8d21f 100644 --- a/Clocker/Clocker/es.lproj/Localizable.strings +++ b/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"; "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."; "Happy Weekend." = "Happy Weekend."; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/Clocker/fr.lproj/Localizable.strings b/Clocker/Clocker/fr.lproj/Localizable.strings index 5182cb7..ec24d45 100644 --- a/Clocker/Clocker/fr.lproj/Localizable.strings +++ b/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"; "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."; "Happy Weekend." = "Happy Weekend."; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/Clocker/hi.lproj/Localizable.strings b/Clocker/Clocker/hi.lproj/Localizable.strings index 13353a5..9ead198 100644 --- a/Clocker/Clocker/hi.lproj/Localizable.strings +++ b/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"; "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."; "Happy Weekend." = "Happy Weekend."; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/Clocker/hr.lproj/Localizable.strings b/Clocker/Clocker/hr.lproj/Localizable.strings index e5d28ac..0beb872 100644 --- a/Clocker/Clocker/hr.lproj/Localizable.strings +++ b/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"; "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."; "Happy Weekend." = "Uživaj vikend."; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/Clocker/it.lproj/Localizable.strings b/Clocker/Clocker/it.lproj/Localizable.strings index e1b4c00..0e61f37 100644 --- a/Clocker/Clocker/it.lproj/Localizable.strings +++ b/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"; "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."; "Happy Weekend." = "Happy Weekend."; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/Clocker/ja.lproj/Localizable.strings b/Clocker/Clocker/ja.lproj/Localizable.strings index 9548555..11500cf 100644 --- a/Clocker/Clocker/ja.lproj/Localizable.strings +++ b/Clocker/Clocker/ja.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 */ "Open Clocker At Login" = "ログイン時に Clocker を開始"; @@ -355,3 +355,6 @@ "start-at-login" = "ログイン時に開始"; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/Clocker/ko.lproj/Localizable.strings b/Clocker/Clocker/ko.lproj/Localizable.strings index d424b7d..15a974a 100644 --- a/Clocker/Clocker/ko.lproj/Localizable.strings +++ b/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"; "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."; "Happy Weekend." = "Happy Weekend."; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/Clocker/nl.lproj/Localizable.strings b/Clocker/Clocker/nl.lproj/Localizable.strings index d4d47b5..d64f419 100644 --- a/Clocker/Clocker/nl.lproj/Localizable.strings +++ b/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"; "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."; "Happy Weekend." = "Happy Weekend."; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/Clocker/pl.lproj/Localizable.strings b/Clocker/Clocker/pl.lproj/Localizable.strings index add8334..f3e319b 100644 --- a/Clocker/Clocker/pl.lproj/Localizable.strings +++ b/Clocker/Clocker/pl.lproj/Localizable.strings @@ -158,3 +158,6 @@ "New Zealand" = "Nowa Zelandia"; "Florida" = "Floryda"; "San Francisco" = "San Francisco"; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/Clocker/pt-BR.lproj/Localizable.strings b/Clocker/Clocker/pt-BR.lproj/Localizable.strings index 6bb78f1..4ab2eed 100644 --- a/Clocker/Clocker/pt-BR.lproj/Localizable.strings +++ b/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"; "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."; "Happy Weekend." = "Happy Weekend."; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/Clocker/ru.lproj/Localizable.strings b/Clocker/Clocker/ru.lproj/Localizable.strings index 864f2b6..c649400 100644 --- a/Clocker/Clocker/ru.lproj/Localizable.strings +++ b/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"; "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."; "Happy Weekend." = "Happy Weekend."; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/Clocker/tr.lproj/Localizable.strings b/Clocker/Clocker/tr.lproj/Localizable.strings index 3a1e01b..a8c05ff 100644 --- a/Clocker/Clocker/tr.lproj/Localizable.strings +++ b/Clocker/Clocker/tr.lproj/Localizable.strings @@ -159,3 +159,6 @@ Takvimleri Görüntüle"; "New Zealand" = "New Zealand"; "Florida" = "Florida"; "San Francisco" = "San Francisco"; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/Clocker/zh-Hans.lproj/Localizable.strings b/Clocker/Clocker/zh-Hans.lproj/Localizable.strings index 1c4358f..3a8872d 100644 --- a/Clocker/Clocker/zh-Hans.lproj/Localizable.strings +++ b/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"; "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."; "Happy Weekend." = "Happy Weekend."; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/Clocker/zh-Hant.lproj/Localizable.strings b/Clocker/Clocker/zh-Hant.lproj/Localizable.strings index 24b5603..3f77dec 100644 --- a/Clocker/Clocker/zh-Hant.lproj/Localizable.strings +++ b/Clocker/Clocker/zh-Hant.lproj/Localizable.strings @@ -192,7 +192,7 @@ "No upcoming event." = "沒有即將到來的行程"; -"No upcoming events for today!" = "No upcoming events for today!"; +"No upcoming events for today!" = "No upcoming events for today 🎉"; /* Onboarding */ "Open Clocker At Login" = "在登入時打開 Clocker"; @@ -355,3 +355,6 @@ "start-at-login" = "在登入時啟動"; + +// iCloud +"Enable iCloud Sync" = "Enable iCloud Sync"; diff --git a/Clocker/ClockerUnitTests/AppDelegateTests.swift b/Clocker/ClockerUnitTests/AppDelegateTests.swift index 6e7784f..4d8e433 100644 --- a/Clocker/ClockerUnitTests/AppDelegateTests.swift +++ b/Clocker/ClockerUnitTests/AppDelegateTests.swift @@ -136,17 +136,4 @@ class AppDelegateTests: XCTestCase { 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) - } } diff --git a/Clocker/ClockerUnitTests/ClockerUnitTests.swift b/Clocker/ClockerUnitTests/ClockerUnitTests.swift index 996f6b3..ab0b26d 100644 --- a/Clocker/ClockerUnitTests/ClockerUnitTests.swift +++ b/Clocker/ClockerUnitTests/ClockerUnitTests.swift @@ -173,7 +173,7 @@ class ClockerUnitTests: XCTestCase { XCTAssertTrue(operations.timeDifference() == ", 9h 30m ahead", "Difference was unexpectedly: \(operations.timeDifference())") XCTAssertTrue(californiaOperations.timeDifference() == ", 3h behind", "Difference was unexpectedly: \(californiaOperations.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())") } @@ -406,10 +406,9 @@ class ClockerUnitTests: XCTestCase { func testToasty() { let view = NSView(frame: CGRect.zero) view.makeToast("Hello, this is a toast") - XCTAssertEqual(view.subviews.first?.accessibilityIdentifier(), "ToastView") - - let expectation = expectation(description: "Toast View should hide after 1 second") - let result = XCTWaiter.wait(for: [expectation], timeout: 1.5) // Set 2 seconds here for a little leeway + XCTAssertEqual(view.subviews.first?.accessibilityIdentifier(), "ToastView") + let toastExpectation = 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 if result == XCTWaiter.Result.timedOut { XCTAssertTrue(view.subviews.isEmpty) } diff --git a/Clocker/ClockerUnitTests/StandardMenubarHandlerTests.swift b/Clocker/ClockerUnitTests/StandardMenubarHandlerTests.swift index 8d8c53e..1eae0a7 100644 --- a/Clocker/ClockerUnitTests/StandardMenubarHandlerTests.swift +++ b/Clocker/ClockerUnitTests/StandardMenubarHandlerTests.swift @@ -13,7 +13,7 @@ class StandardMenubarHandlerTests: XCTestCase { "nextUpdate": "", "latitude": "19.0759837", "longitude": "72.8776559"] - + private func makeMockStore(with menubarMode: Int = 1) -> DataStore { // Wipe all timezones from UserDefaults guard let defaults = UserDefaults(suiteName: "com.abhishek.Clocker.StandardMenubarHandlerTests") else { @@ -25,10 +25,11 @@ class StandardMenubarHandlerTests: XCTestCase { XCTAssertNotEqual(defaults, UserDefaults.standard) return DataStore(with: defaults) } - + private func saveObject(object: TimezoneData, in store: DataStore, - at index: Int = -1) { + at index: Int = -1) + { var defaults = store.timezones() let encodedObject = NSKeyedArchiver.archivedData(withRootObject: object as Any) index == -1 ? defaults.append(encodedObject) : defaults.insert(encodedObject, at: index) @@ -81,14 +82,14 @@ class StandardMenubarHandlerTests: XCTestCase { // Test menubar string is absent XCTAssertTrue(menubarString.count == 0) } - + func testWithEmptyMenubarTimezones() { let store = makeMockStore() store.setTimezones(nil) let menubarHandler = MenubarTitleProvider(with: store) XCTAssertNil(menubarHandler.titleForMenubar()) } - + func testWithStandardMenubarMode() { // Set mode to standard mode let store = makeMockStore(with: 0) @@ -96,11 +97,11 @@ class StandardMenubarHandlerTests: XCTestCase { let dataObject = TimezoneData(with: mumbai) dataObject.isFavourite = 1 saveObject(object: dataObject, in: store) - + let menubarHandler = MenubarTitleProvider(with: store) XCTAssertNil(menubarHandler.titleForMenubar()) } - + func testProviderPassingAllConditions() { // Set mode to standard mode let store = makeMockStore() @@ -108,7 +109,7 @@ class StandardMenubarHandlerTests: XCTestCase { let dataObject = TimezoneData(with: mumbai) dataObject.isFavourite = 1 saveObject(object: dataObject, in: store) - + let menubarHandler = MenubarTitleProvider(with: store) XCTAssertNotNil(menubarHandler.titleForMenubar()) } diff --git a/Clocker/Dependencies/Date Additions/Date+TimeAgo.swift b/Clocker/Dependencies/Date Additions/Date+TimeAgo.swift index 733851d..7fd1bde 100755 --- a/Clocker/Dependencies/Date Additions/Date+TimeAgo.swift +++ b/Clocker/Dependencies/Date Additions/Date+TimeAgo.swift @@ -115,10 +115,10 @@ public extension Date { return DateToolsLocalizedStrings("An hour ago") } 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 { if numericTimes { - return DateToolsLocalizedStrings("1 minute ago") + return DateToolsLocalizedStrings("1m ago") } return DateToolsLocalizedStrings("A minute ago") diff --git a/Clocker/Events and Reminders/CalendarHandler.swift b/Clocker/Events and Reminders/CalendarHandler.swift index f9f7f3b..3b27e4e 100644 --- a/Clocker/Events and Reminders/CalendarHandler.swift +++ b/Clocker/Events and Reminders/CalendarHandler.swift @@ -493,12 +493,12 @@ struct EventInfo { if timeIntervalSinceNowForMeeting < 0, timeIntervalSinceNowForMeeting > -300 { return "started \(event.startDate.shortTimeAgoSinceNow) ago." } 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 withoutAgo = withoutAn.replacingOccurrences(of: "ago", with: CLEmptyString) // If the user has not turned on seconds granularity for one of the timezones, // 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 { let hoursUntil = event.startDate.hoursUntil return "in \(hoursUntil)h" diff --git a/Clocker/Onboarding/OnboardingParentViewController.swift b/Clocker/Onboarding/OnboardingParentViewController.swift index e55de38..e88424d 100644 --- a/Clocker/Onboarding/OnboardingParentViewController.swift +++ b/Clocker/Onboarding/OnboardingParentViewController.swift @@ -218,15 +218,15 @@ class OnboardingParentViewController: NSViewController { func performFinalStepsBeforeFinishing() { 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) } + + // Install the menubar option! + let appDelegate = NSApplication.shared.delegate as? AppDelegate + appDelegate?.continueUsually() } private func addChildIfNeccessary(_ viewController: NSViewController) { diff --git a/Clocker/Onboarding/OnboardingSearchController.swift b/Clocker/Onboarding/OnboardingSearchController.swift index d3a90bc..be1eb12 100644 --- a/Clocker/Onboarding/OnboardingSearchController.swift +++ b/Clocker/Onboarding/OnboardingSearchController.swift @@ -40,8 +40,7 @@ class OnboardingSearchController: NSViewController { super.viewDidLoad() view.wantsLayer = true - searchResultsDataSource = SearchDataSource(with: searchBar, location: .onboarding) - + resultsTableView.isHidden = true resultsTableView.delegate = self resultsTableView.setAccessibility("ResultsTableView") @@ -70,6 +69,16 @@ class OnboardingSearchController: NSViewController { setupUndoButton() } + + override func viewWillAppear() { + super.viewWillAppear() + searchResultsDataSource = SearchDataSource(with: searchBar, location: .onboarding) + } + + override func viewWillDisappear() { + super.viewWillDisappear() + searchResultsDataSource = nil + } deinit { if let themeDidChangeNotif = themeDidChangeNotification { @@ -440,7 +449,7 @@ class OnboardingSearchController: NSViewController { extension OnboardingSearchController: NSTableViewDataSource { func numberOfRows(in _: NSTableView) -> Int { - return searchResultsDataSource.resultsCount() + return searchResultsDataSource != nil ? searchResultsDataSource.resultsCount() : 0 } func tableView(_ tableView: NSTableView, viewFor _: NSTableColumn?, row: Int) -> NSView? { diff --git a/Clocker/Overall App/DataStore.swift b/Clocker/Overall App/DataStore.swift index 8be2c92..57264c6 100644 --- a/Clocker/Overall App/DataStore.swift +++ b/Clocker/Overall App/DataStore.swift @@ -18,12 +18,13 @@ enum ViewType { case dayInMenubar case menubarCompactMode case dstTransitionInfo + case sync } class DataStore: NSObject { private static var sharedStore = DataStore(with: UserDefaults.standard) private var userDefaults: UserDefaults! - private var ubiquitousStore: NSUbiquitousKeyValueStore! + private var ubiquitousStore: NSUbiquitousKeyValueStore? // Since these pref can accessed every second, let's cache this private var shouldDisplayDayInMenubar: Bool = false @@ -41,9 +42,39 @@ class DataStore: NSObject { init(with defaults: UserDefaults) { super.init() userDefaults = defaults - ubiquitousStore = NSUbiquitousKeyValueStore.default shouldDisplayDayInMenubar = shouldDisplay(.dayInMenubar) 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] { @@ -57,7 +88,7 @@ class DataStore: NSObject { func setTimezones(_ timezones: [Data]?) { userDefaults.set(timezones, forKey: CLDefaultPreferenceKey) // iCloud sync - ubiquitousStore.set(timezones, forKey: CLDefaultPreferenceKey) + ubiquitousStore?.set(timezones, forKey: CLDefaultPreferenceKey) } func menubarTimezones() -> [Data]? { @@ -158,6 +189,8 @@ class DataStore: NSObject { } return value == 0 + case .sync: + return shouldDisplayHelper(CLEnableSyncKey) } } @@ -170,3 +203,7 @@ class DataStore: NSObject { return value.isEqual(to: NSNumber(value: 0)) } } + +extension DataStore { + public static let didSyncFromExternalSourceNotification: NSNotification.Name = .init("didSyncFromExternalSourceNotification") +} diff --git a/Clocker/Overall App/Strings.swift b/Clocker/Overall App/Strings.swift index e9fa746..b28c608 100644 --- a/Clocker/Overall App/Strings.swift +++ b/Clocker/Overall App/Strings.swift @@ -37,3 +37,4 @@ let CLInstallHomeIndicatorObject = "installHomeIndicatorObject" let CLSwitchToCompactModeAlert = "com.abhishek.switchToCompactMode" let CLDisplayDSTTransitionInfo = "com.abhishek.showDSTTransitionInfo" let CLAppleInterfaceStyleKey = "AppleInterfaceStyle" +let CLEnableSyncKey = "com.abhishek.enableSync" diff --git a/Clocker/Panel/PanelController.swift b/Clocker/Panel/PanelController.swift index 310ee03..6e3fac3 100644 --- a/Clocker/Panel/PanelController.swift +++ b/Clocker/Panel/PanelController.swift @@ -6,18 +6,12 @@ import CoreLoggerKit class PanelController: ParentPanelController { @objc dynamic var hasActivePanel: Bool = false - static var sharedWindow = PanelController(windowNibName: .panel) - @IBOutlet var backgroundView: BackgroundPanelView! override func windowDidLoad() { super.windowDidLoad() } - class func shared() -> PanelController { - return sharedWindow - } - override func awakeFromNib() { super.awakeFromNib() @@ -80,6 +74,8 @@ class PanelController: ParentPanelController { super.dismissRowActions() updateDefaultPreferences() + + setupUpcomingEventViewCollectionViewIfNeccesary() if DataStore.shared().timezones().isEmpty || DataStore.shared().shouldDisplay(.futureSlider) == false { futureSliderView.isHidden = true @@ -309,6 +305,9 @@ class PanelController: ParentPanelController { window?.orderOut(nil) datasource = nil + upcomingEventsDataSource = nil + parentTimer?.pause() + parentTimer = nil } func setActivePanel(newValue: Bool) { diff --git a/Clocker/Panel/ParentPanelController.swift b/Clocker/Panel/ParentPanelController.swift index d101ed9..960aba5 100644 --- a/Clocker/Panel/ParentPanelController.swift +++ b/Clocker/Panel/ParentPanelController.swift @@ -37,7 +37,7 @@ class ParentPanelController: NSWindowController { var datasource: TimezoneDataSource? - private lazy var feedbackWindow = AppFeedbackWindowController.shared() + private var feedbackWindow: AppFeedbackWindowController? private var notePopover: NotesPopover? @@ -195,7 +195,8 @@ class ParentPanelController: NSWindowController { selector: #selector(systemTimezoneDidChange), name: NSNotification.Name.NSSystemTimeZoneDidChange, object: nil) - NotificationCenter.default.addObserver(forName: NSUbiquitousKeyValueStore.didChangeExternallyNotification, + + NotificationCenter.default.addObserver(forName: DataStore.didSyncFromExternalSourceNotification, object: self, queue: OperationQueue.main) { [weak self] _ in @@ -889,9 +890,9 @@ class ParentPanelController: NSWindowController { } else if sender.title == PanelConstants.yesWithQuestionMark { ReviewController.prompted() updateReviewView() - feedbackWindow = AppFeedbackWindowController.shared() - feedbackWindow.showWindow(nil) + feedbackWindow?.appFeedbackWindowDelegate = self + feedbackWindow?.showWindow(nil) NSApp.activate(ignoringOtherApps: true) } else { updateReviewView() @@ -985,7 +986,9 @@ class ParentPanelController: NSWindowController { } @objc func reportIssue() { - feedbackWindow.showWindow(nil) + feedbackWindow = AppFeedbackWindowController.shared() + feedbackWindow?.appFeedbackWindowDelegate = self + feedbackWindow?.showWindow(nil) NSApp.activate(ignoringOtherApps: true) window?.orderOut(nil) @@ -1146,3 +1149,9 @@ extension ParentPanelController: NSSharingServicePickerDelegate { return clipboardCopy } } + +extension ParentPanelController: AppFeedbackWindowControllerDelegate { + func appFeedbackWindowWillClose() { + feedbackWindow = nil + } +} diff --git a/Clocker/Panel/Upcoming Events/UpcomingEventViewItem.swift b/Clocker/Panel/Upcoming Events/UpcomingEventViewItem.swift index 94fd755..19810f6 100644 --- a/Clocker/Panel/Upcoming Events/UpcomingEventViewItem.swift +++ b/Clocker/Panel/Upcoming Events/UpcomingEventViewItem.swift @@ -90,7 +90,7 @@ class UpcomingEventViewItem: NSCollectionViewItem { 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") - 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, NSColor.systemGreen, nil) diff --git a/Clocker/Preferences/About/AboutViewController.swift b/Clocker/Preferences/About/AboutViewController.swift index d36b872..bd2e01f 100644 --- a/Clocker/Preferences/About/AboutViewController.swift +++ b/Clocker/Preferences/About/AboutViewController.swift @@ -22,7 +22,7 @@ class AboutViewController: ParentViewController { @IBOutlet var versionField: NSTextField! private var themeDidChangeNotification: NSObjectProtocol? - private lazy var feedbackWindow = AppFeedbackWindowController.shared() + private var feedbackWindow: AppFeedbackWindowController? override func viewDidLoad() { super.viewDidLoad() @@ -120,7 +120,9 @@ class AboutViewController: ParentViewController { } @IBAction func reportIssue(_: Any) { - feedbackWindow.showWindow(nil) + feedbackWindow = AppFeedbackWindowController.sharedWindow + feedbackWindow?.appFeedbackWindowDelegate = self + feedbackWindow?.showWindow(nil) NSApp.activate(ignoringOtherApps: true) view.window?.orderOut(nil) @@ -150,3 +152,9 @@ class AboutViewController: ParentViewController { underlineTextForActionButton() } } + +extension AboutViewController: AppFeedbackWindowControllerDelegate { + func appFeedbackWindowWillClose() { + feedbackWindow = nil + } +} diff --git a/Clocker/Preferences/App Feedback/AppFeedbackWindowController.swift b/Clocker/Preferences/App Feedback/AppFeedbackWindowController.swift index 31c72ea..88ddc8b 100644 --- a/Clocker/Preferences/App Feedback/AppFeedbackWindowController.swift +++ b/Clocker/Preferences/App Feedback/AppFeedbackWindowController.swift @@ -5,6 +5,10 @@ import CoreLoggerKit import CoreModelKit import FirebaseDatabase +protocol AppFeedbackWindowControllerDelegate: AnyObject { + func appFeedbackWindowWillClose() +} + extension NSNib.Name { static let appFeedbackWindowIdentifier = NSNib.Name("AppFeedbackWindow") static let onboardingWindowIdentifier = NSNib.Name("OnboardingWindow") @@ -36,6 +40,7 @@ class AppFeedbackWindowController: NSWindowController { @IBOutlet var progressIndicator: NSProgressIndicator! @IBOutlet var quickCommentsLabel: PointingHandCursorButton! + public weak var appFeedbackWindowDelegate: AppFeedbackWindowControllerDelegate? private var themeDidChangeNotification: NSObjectProtocol? private var serialNumber: String? { let platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice")) @@ -307,6 +312,7 @@ extension AppFeedbackWindowController: NSWindowDelegate { emailField.stringValue = CLEmptyString feedbackTextView.string = CLEmptyString isActivityInProgress = false + appFeedbackWindowDelegate?.appFeedbackWindowWillClose() } func bringPreferencesWindowToFront() { diff --git a/Clocker/Preferences/Appearance/AppearanceViewController.swift b/Clocker/Preferences/Appearance/AppearanceViewController.swift index 79558af..9e81982 100644 --- a/Clocker/Preferences/Appearance/AppearanceViewController.swift +++ b/Clocker/Preferences/Appearance/AppearanceViewController.swift @@ -16,6 +16,8 @@ class AppearanceViewController: ParentViewController { @IBOutlet var includePlaceNameControl: NSSegmentedControl! @IBOutlet var appearanceTab: NSTabView! @IBOutlet var appDisplayControl: NSSegmentedControl! + @IBOutlet var syncLabel: NSTextField! + @IBOutlet var syncSegementedControl: NSSegmentedControl! private var themeDidChangeNotification: NSObjectProtocol? @@ -134,6 +136,10 @@ class AppearanceViewController: ParentViewController { // True is Menubar Only and False is Menubar + Dock let appDisplayOptions = DataStore.shared().shouldDisplay(.appDisplayOptions) 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! @@ -150,7 +156,6 @@ class AppearanceViewController: ParentViewController { @IBOutlet var menubarModeLabel: NSTextField! @IBOutlet var previewLabel: NSTextField! @IBOutlet var miscelleaneousLabel: NSTextField! - @IBOutlet var dstTransitionField: NSTextField! // Panel Preview @IBOutlet var previewPanelTableView: NSTableView! @@ -162,6 +167,7 @@ class AppearanceViewController: ParentViewController { showSliderLabel.stringValue = "Time Scroller".localized() showSunriseLabel.stringValue = "Show Sunrise/Sunset".localized() largerTextLabel.stringValue = "Larger Text".localized() + syncLabel.stringValue = "Enable iCloud Sync".localized() futureSliderRangeLabel.stringValue = "Future Slider Range".localized() includeDateLabel.stringValue = "Include Date".localized() includeDayLabel.stringValue = "Include Day".localized() @@ -172,9 +178,9 @@ class AppearanceViewController: ParentViewController { [timeFormatLabel, panelTheme, dayDisplayOptionsLabel, showSliderLabel, - showSunriseLabel, largerTextLabel, futureSliderRangeLabel, + showSunriseLabel, largerTextLabel, syncLabel, futureSliderRangeLabel, includeDayLabel, includeDateLabel, includePlaceLabel, appDisplayLabel, menubarModeLabel, - previewLabel, miscelleaneousLabel, dstTransitionField].forEach { + previewLabel, miscelleaneousLabel].forEach { $0?.textColor = Themer.shared().mainTextColor() } @@ -353,8 +359,9 @@ class AppearanceViewController: ParentViewController { previewPanelTableView.reloadData() } - @IBAction func toggleDSTTransitionOption(_: Any) { - previewPanelTableView.reloadData() + @IBAction func toggleSync(_ sender: NSSegmentedControl) { + NSUbiquitousKeyValueStore.default.set(sender.selectedSegment == 0, forKey: CLEnableSyncKey) + DataStore.shared().setupSyncNotification() } } diff --git a/Clocker/Preferences/Calendar/CalendarViewController.swift b/Clocker/Preferences/Calendar/CalendarViewController.swift index db4a7b2..109c105 100644 --- a/Clocker/Preferences/Calendar/CalendarViewController.swift +++ b/Clocker/Preferences/Calendar/CalendarViewController.swift @@ -5,29 +5,26 @@ import CoreLoggerKit import EventKit class ClockerTextBackgroundView: NSView { - private var themeDidChangeNotification: NSObjectProtocol? override func awakeFromNib() { wantsLayer = true layer?.cornerRadius = 8.0 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 { - if let themeDidChangeNotif = themeDidChangeNotification { - NotificationCenter.default.removeObserver(themeDidChangeNotif) - } + NotificationCenter.default.addObserver(self, + selector: #selector(updateBackgroundColor), + name: .themeDidChangeNotification, + object: nil) } - + override func updateLayer() { super.updateLayer() layer?.backgroundColor = Themer.shared().textBackgroundColor().cgColor } + + @objc func updateBackgroundColor() { + layer?.backgroundColor = Themer.shared().textBackgroundColor().cgColor + } } class CalendarViewController: ParentViewController { diff --git a/Clocker/Preferences/General/PreferencesViewController.swift b/Clocker/Preferences/General/PreferencesViewController.swift index 09a3578..4226215 100644 --- a/Clocker/Preferences/General/PreferencesViewController.swift +++ b/Clocker/Preferences/General/PreferencesViewController.swift @@ -93,7 +93,7 @@ class PreferencesViewController: ParentViewController { name: NSNotification.Name.customLabelChanged, object: nil) - NotificationCenter.default.addObserver(forName: NSUbiquitousKeyValueStore.didChangeExternallyNotification, + NotificationCenter.default.addObserver(forName: DataStore.didSyncFromExternalSourceNotification, object: self, queue: OperationQueue.main) { [weak self] _ in diff --git a/Clocker/Preferences/General/SearchDataSource.swift b/Clocker/Preferences/General/SearchDataSource.swift index 6bc452f..d458d9b 100644 --- a/Clocker/Preferences/General/SearchDataSource.swift +++ b/Clocker/Preferences/General/SearchDataSource.swift @@ -21,7 +21,7 @@ struct TimezoneMetadata { } class SearchDataSource: NSObject { - private var searchField: NSSearchField! + private weak var searchField: NSSearchField! private var finalArray: [RowType] = [] private var location: SearchLocation = .preferences private var dataTask: URLSessionDataTask? = .none diff --git a/Clocker/Preferences/Menu Bar/MenubarTitleProvider.swift b/Clocker/Preferences/Menu Bar/MenubarTitleProvider.swift index 27070de..d88d7a5 100644 --- a/Clocker/Preferences/Menu Bar/MenubarTitleProvider.swift +++ b/Clocker/Preferences/Menu Bar/MenubarTitleProvider.swift @@ -7,12 +7,12 @@ import EventKit class MenubarTitleProvider: NSObject { private let store: DataStore - + init(with dataStore: DataStore) { - self.store = dataStore + store = dataStore super.init() } - + func titleForMenubar() -> String? { if let nextEvent = checkForUpcomingEvents() { return nextEvent diff --git a/Clocker/Preferences/Menu Bar/StatusContainerView.swift b/Clocker/Preferences/Menu Bar/StatusContainerView.swift index bf55dea..b4fee8b 100644 --- a/Clocker/Preferences/Menu Bar/StatusContainerView.swift +++ b/Clocker/Preferences/Menu Bar/StatusContainerView.swift @@ -239,6 +239,7 @@ class StatusContainerView: NSView { // NSView move animation NSAnimationContext.runAnimationGroup({ context in 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) // The view will animate to the new origin self.animator().frame = newFrame diff --git a/Clocker/Preferences/Menu Bar/StatusItemHandler.swift b/Clocker/Preferences/Menu Bar/StatusItemHandler.swift index 3ebed0f..f3055bb 100644 --- a/Clocker/Preferences/Menu Bar/StatusItemHandler.swift +++ b/Clocker/Preferences/Menu Bar/StatusItemHandler.swift @@ -240,23 +240,21 @@ class StatusItemHandler: NSObject { } func updateCompactMenubar() { - if let upcomingEvent = menubarTitleHandler.checkForUpcomingEvents() { - print("Need to construct upcoming event view \(upcomingEvent)") + if menubarTitleHandler.checkForUpcomingEvents() != nil { // Iterate and see if we're showing the calendar item view let upcomingEventView = retrieveUpcomingEventStatusView() // If not, reconstruct Status Container View with another view if upcomingEventView == nil { constructCompactView(with: true) } - } else { - let upcomingEventView = retrieveUpcomingEventStatusView() - upcomingEventView?.removeFromSuperview() + } else if let upcomingEventView = retrieveUpcomingEventStatusView() { + upcomingEventView.removeFromSuperview() 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. statusContainerView?.updateTime() } - + private func removeUpcomingStatusItemView() { NSAnimationContext.runAnimationGroup({ context in context.duration = 0.2 diff --git a/Clocker/Preferences/Preferences.storyboard b/Clocker/Preferences/Preferences.storyboard index f3fe745..8bffd78 100644 --- a/Clocker/Preferences/Preferences.storyboard +++ b/Clocker/Preferences/Preferences.storyboard @@ -791,8 +791,8 @@ - + @@ -924,67 +924,66 @@ - - - + + + - + - + - - - - - - - - - - + + + + - - + + - + - - - + + + - + - + - - - - + + + + + + + + + + - - + - + - + - + @@ -1468,7 +1467,6 @@ - @@ -1487,6 +1485,8 @@ + + @@ -1513,7 +1513,7 @@ - + @@ -1570,13 +1570,13 @@ DQ - + - - + + - + @@ -1666,14 +1666,14 @@ DQ - + - + @@ -1721,7 +1721,7 @@ DQ