Browse Source

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

pull/113/head
Abhishek 3 years ago
parent
commit
f23e3f54d2
  1. 28
      Clocker/AppDelegate.swift
  2. 34
      Clocker/ClockerUnitTests/ClockerUnitTests.swift
  3. 2
      Clocker/Events and Reminders/EventCenter.swift
  4. 2
      Clocker/Onboarding/OnboardingParentViewController.swift
  5. 80
      Clocker/Onboarding/OnboardingSearchController.swift
  6. 63
      Clocker/Overall App/AppDefaults.swift
  7. 5
      Clocker/Overall App/ConfigExport.swift
  8. 30
      Clocker/Overall App/DataStore.swift
  9. 4
      Clocker/Overall App/String + Additions.swift
  10. 35
      Clocker/Panel/Data Layer/TimezoneDataOperations.swift
  11. 2
      Clocker/Panel/PanelController.swift
  12. 29
      Clocker/Panel/ParentPanelController.swift
  13. 3
      Clocker/Panel/Rate Controller/UpcomingEventView.swift
  14. 23
      Clocker/Panel/UI/TimezoneDataSource.swift
  15. 3
      Clocker/Preferences/App Feedback/AppFeedbackWindowController.swift
  16. 4
      Clocker/Preferences/Appearance/AppearanceViewController.swift
  17. 10
      Clocker/Preferences/General/PreferencesDataSource.swift
  18. 6
      Clocker/Preferences/General/PreferencesViewController.swift
  19. 2
      Clocker/Preferences/Menu Bar/MenubarTitleProvider.swift
  20. 10
      Clocker/Preferences/Menu Bar/StatusContainerView.swift
  21. 7
      Clocker/Preferences/Menu Bar/StatusItemHandler.swift
  22. 2
      Clocker/Preferences/Menu Bar/StatusItemView.swift
  23. 20
      Clocker/Preferences/Preferences.storyboard

28
Clocker/AppDelegate.swift

@ -43,7 +43,7 @@ open class AppDelegate: NSObject, NSApplicationDelegate {
// Required for migrating our model type to CoreModelKit // Required for migrating our model type to CoreModelKit
NSKeyedUnarchiver.setClass(CoreModelKit.TimezoneData.classForKeyedUnarchiver(), forClassName: "Clocker.TimezoneData") NSKeyedUnarchiver.setClass(CoreModelKit.TimezoneData.classForKeyedUnarchiver(), forClassName: "Clocker.TimezoneData")
AppDefaults.initialize() AppDefaults.initialize(with: DataStore.shared(), defaults: UserDefaults.standard)
// Check if we can show the onboarding flow! // Check if we can show the onboarding flow!
showOnboardingFlowIfEligible() showOnboardingFlowIfEligible()
@ -73,9 +73,9 @@ open class AppDelegate: NSObject, NSApplicationDelegate {
} }
@objc private func openPreferencesWindow() { @objc private func openPreferencesWindow() {
let displayMode = UserDefaults.standard.integer(forKey: CLShowAppInForeground) let displayMode = DataStore.shared().shouldDisplay(.showAppInForeground)
if displayMode == 1 { if displayMode {
let floatingWindow = FloatingWindowController.shared() let floatingWindow = FloatingWindowController.shared()
floatingWindow.openPreferences(NSButton()) floatingWindow.openPreferences(NSButton())
} else { } else {
@ -88,16 +88,22 @@ open class AppDelegate: NSObject, NSApplicationDelegate {
NSApp.setActivationPolicy(.accessory) NSApp.setActivationPolicy(.accessory)
} }
private lazy var controller: OnboardingController? = { private var controller: OnboardingController?
let onboardingStoryboard = NSStoryboard(name: NSStoryboard.Name("Onboarding"), bundle: nil)
return onboardingStoryboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("onboardingFlow")) as? OnboardingController
}()
private func showOnboardingFlowIfEligible() { private func showOnboardingFlowIfEligible() {
let shouldLaunchOnboarding = (DataStore.shared().retrieve(key: CLShowOnboardingFlow) == nil && DataStore.shared().timezones().isEmpty) let isTestInProgress = ProcessInfo.processInfo.arguments.contains(CLOnboaringTestsLaunchArgument)
|| ProcessInfo.processInfo.arguments.contains(CLOnboaringTestsLaunchArgument) let shouldLaunchOnboarding =
(DataStore.shared().retrieve(key: CLShowOnboardingFlow) == nil
shouldLaunchOnboarding ? controller?.launch() : continueUsually() && DataStore.shared().timezones().isEmpty)
|| isTestInProgress
if shouldLaunchOnboarding {
let onboardingStoryboard = NSStoryboard(name: NSStoryboard.Name("Onboarding"), bundle: nil)
controller = onboardingStoryboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("onboardingFlow")) as? OnboardingController
controller?.launch()
} else {
continueUsually()
}
} }
func continueUsually() { func continueUsually() {

34
Clocker/ClockerUnitTests/ClockerUnitTests.swift

@ -53,23 +53,23 @@ class ClockerUnitTests: XCTestCase {
"longitude": "-95.9345034"] "longitude": "-95.9345034"]
private var operations: TimezoneDataOperations { private var operations: TimezoneDataOperations {
return TimezoneDataOperations(with: TimezoneData(with: mumbai)) return TimezoneDataOperations(with: TimezoneData(with: mumbai), store: DataStore.shared())
} }
private var californiaOperations: TimezoneDataOperations { private var californiaOperations: TimezoneDataOperations {
return TimezoneDataOperations(with: TimezoneData(with: california)) return TimezoneDataOperations(with: TimezoneData(with: california), store: DataStore.shared())
} }
private var floridaOperations: TimezoneDataOperations { private var floridaOperations: TimezoneDataOperations {
return TimezoneDataOperations(with: TimezoneData(with: florida)) return TimezoneDataOperations(with: TimezoneData(with: florida), store: DataStore.shared())
} }
private var aucklandOperations: TimezoneDataOperations { private var aucklandOperations: TimezoneDataOperations {
return TimezoneDataOperations(with: TimezoneData(with: auckland)) return TimezoneDataOperations(with: TimezoneData(with: auckland), store: DataStore.shared())
} }
private var omahaOperations: TimezoneDataOperations { private var omahaOperations: TimezoneDataOperations {
return TimezoneDataOperations(with: TimezoneData(with: omaha)) return TimezoneDataOperations(with: TimezoneData(with: omaha), store: DataStore.shared())
} }
func testOverridingSecondsComponent_shouldHideSeconds() { func testOverridingSecondsComponent_shouldHideSeconds() {
@ -81,7 +81,7 @@ class ClockerUnitTests: XCTestCase {
TimezoneData(with: california)] TimezoneData(with: california)]
timezoneObjects.forEach { timezoneObjects.forEach {
let operationsObject = TimezoneDataOperations(with: $0) let operationsObject = TimezoneDataOperations(with: $0, store: DataStore.shared())
let currentTime = operationsObject.time(with: 0) let currentTime = operationsObject.time(with: 0)
XCTAssert(currentTime.count == 8) // 8 includes 2 colons XCTAssert(currentTime.count == 8) // 8 includes 2 colons
@ -97,7 +97,7 @@ class ClockerUnitTests: XCTestCase {
let currentFavourites = DataStore.shared().timezones() let currentFavourites = DataStore.shared().timezones()
let oldCount = currentFavourites.count let oldCount = currentFavourites.count
let operationsObject = TimezoneDataOperations(with: timezoneData) let operationsObject = TimezoneDataOperations(with: timezoneData, store: DataStore.shared())
operationsObject.saveObject() operationsObject.saveObject()
let newDefaults = DataStore.shared().timezones() let newDefaults = DataStore.shared().timezones()
@ -151,7 +151,7 @@ class ClockerUnitTests: XCTestCase {
// California is absent. Add it! // California is absent. Add it!
if filteredCount.count == 0 { if filteredCount.count == 0 {
let timezoneData = TimezoneData(with: california) let timezoneData = TimezoneData(with: california)
let operationsObject = TimezoneDataOperations(with: timezoneData) let operationsObject = TimezoneDataOperations(with: timezoneData, store: DataStore.shared())
operationsObject.saveObject() operationsObject.saveObject()
} }
@ -179,7 +179,7 @@ class ClockerUnitTests: XCTestCase {
func testSunriseSunset() { func testSunriseSunset() {
let dataObject = TimezoneData(with: mumbai) let dataObject = TimezoneData(with: mumbai)
let operations = TimezoneDataOperations(with: dataObject) let operations = TimezoneDataOperations(with: dataObject, store: DataStore.shared())
XCTAssertNotNil(operations.formattedSunriseTime(with: 0)) XCTAssertNotNil(operations.formattedSunriseTime(with: 0))
XCTAssertNotNil(dataObject.sunriseTime) XCTAssertNotNil(dataObject.sunriseTime)
@ -187,7 +187,7 @@ class ClockerUnitTests: XCTestCase {
let timezoneObject = TimezoneData(with: onlyTimezone) let timezoneObject = TimezoneData(with: onlyTimezone)
timezoneObject.selectionType = .timezone timezoneObject.selectionType = .timezone
let timezoneOperations = TimezoneDataOperations(with: timezoneObject) let timezoneOperations = TimezoneDataOperations(with: timezoneObject, store: DataStore.shared())
XCTAssertTrue(timezoneOperations.formattedSunriseTime(with: 0) == "") XCTAssertTrue(timezoneOperations.formattedSunriseTime(with: 0) == "")
XCTAssertNil(timezoneObject.sunriseTime) XCTAssertNil(timezoneObject.sunriseTime)
@ -196,7 +196,7 @@ class ClockerUnitTests: XCTestCase {
func testDateWithSliderValue() { func testDateWithSliderValue() {
let dataObject = TimezoneData(with: mumbai) let dataObject = TimezoneData(with: mumbai)
let operations = TimezoneDataOperations(with: dataObject) let operations = TimezoneDataOperations(with: dataObject, store: DataStore.shared())
XCTAssertNotNil(operations.date(with: 0, displayType: .menu)) XCTAssertNotNil(operations.date(with: 0, displayType: .menu))
} }
@ -359,7 +359,7 @@ class ClockerUnitTests: XCTestCase {
func testWithAllLocales() { func testWithAllLocales() {
let dataObject1 = TimezoneData(with: mumbai) let dataObject1 = TimezoneData(with: mumbai)
let operations = TimezoneDataOperations(with: dataObject1) let operations = TimezoneDataOperations(with: dataObject1, store: DataStore.shared())
for locale in Locale.availableIdentifiers { for locale in Locale.availableIdentifiers {
let currentLocale = Locale(identifier: locale) let currentLocale = Locale(identifier: locale)
@ -430,4 +430,14 @@ class ClockerUnitTests: XCTestCase {
XCTAssertEqual(subject.subviews.count, 2) // Two textfields XCTAssertEqual(subject.subviews.count, 2) // Two textfields
XCTAssertEqual(subject.subviews.first?.layer?.animationKeys(), ["notimezone.emoji"]) XCTAssertEqual(subject.subviews.first?.layer?.animationKeys(), ["notimezone.emoji"])
} }
func testDefaultsWiping() {
let defaultsDict: [String: Any] = ["test1": "testString", "test2": 24]
let domainName = "com.test.clocker"
let defaults = UserDefaults(suiteName: domainName)
defaults?.setPersistentDomain(defaultsDict, forName: domainName)
defaults?.wipe(for: domainName)
XCTAssertNil(defaults?.object(forKey: "test1"))
XCTAssertNil(defaults?.object(forKey: "test2"))
}
} }

2
Clocker/Events and Reminders/EventCenter.swift

@ -42,7 +42,7 @@ class EventCenter: NSObject {
} }
private func refetchAll() { private func refetchAll() {
Logger.info("\nRefetching events from the store") Logger.info("Refetching events from the store")
eventsForDate = [:] eventsForDate = [:]
filteredEvents = [:] filteredEvents = [:]

2
Clocker/Onboarding/OnboardingParentViewController.swift

@ -189,7 +189,7 @@ class OnboardingParentViewController: NSViewController {
currentTimezone.isSystemTimezone = true currentTimezone.isSystemTimezone = true
currentTimezone.placeID = "Home" currentTimezone.placeID = "Home"
let operations = TimezoneDataOperations(with: currentTimezone) let operations = TimezoneDataOperations(with: currentTimezone, store: DataStore.shared())
operations.saveObject(at: 0) operations.saveObject(at: 0)
} }

80
Clocker/Onboarding/OnboardingSearchController.swift

@ -21,7 +21,7 @@ class OnboardingSearchController: NSViewController {
@IBOutlet private var accessoryLabel: NSTextField! @IBOutlet private var accessoryLabel: NSTextField!
@IBOutlet var undoButton: NSButton! @IBOutlet var undoButton: NSButton!
private var searchResultsDataSource: SearchDataSource! private var searchResultsDataSource: SearchDataSource?
private var dataTask: URLSessionDataTask? = .none private var dataTask: URLSessionDataTask? = .none
private var themeDidChangeNotification: NSObjectProtocol? private var themeDidChangeNotification: NSObjectProtocol?
@ -81,17 +81,19 @@ class OnboardingSearchController: NSViewController {
@objc func doubleClickAction(_ tableView: NSTableView) { @objc func doubleClickAction(_ tableView: NSTableView) {
[accessoryLabel].forEach { $0?.isHidden = false } [accessoryLabel].forEach { $0?.isHidden = false }
if tableView.selectedRow >= 0, tableView.selectedRow < searchResultsDataSource.resultsCount() { if tableView.selectedRow >= 0, tableView.selectedRow < (searchResultsDataSource?.resultsCount() ?? 0) {
let selectedType = searchResultsDataSource.placeForRow(resultsTableView.selectedRow) let selectedType = searchResultsDataSource?.placeForRow(resultsTableView.selectedRow)
switch selectedType { switch selectedType {
case .city: case .city:
if let filteredGoogleResult = searchResultsDataSource.retrieveFilteredResultFromGoogleAPI(resultsTableView.selectedRow) { if let filteredGoogleResult = searchResultsDataSource?.retrieveFilteredResultFromGoogleAPI(resultsTableView.selectedRow) {
addTimezoneToDefaults(filteredGoogleResult) addTimezoneToDefaults(filteredGoogleResult)
} }
return return
case .timezone: case .timezone:
cleanupAfterInstallingTimezone() cleanupAfterInstallingTimezone()
return return
case .none:
return
} }
} }
} }
@ -100,28 +102,28 @@ class OnboardingSearchController: NSViewController {
let data = TimezoneData() let data = TimezoneData()
data.setLabel(CLEmptyString) data.setLabel(CLEmptyString)
let currentSelection = searchResultsDataSource.retrieveSelectedTimezone(resultsTableView.selectedRow) if let currentSelection = searchResultsDataSource?.retrieveSelectedTimezone(resultsTableView.selectedRow) {
let metaInfo = metadata(for: currentSelection)
let metaInfo = metadata(for: currentSelection) data.timezoneID = metaInfo.0.name
data.timezoneID = metaInfo.0.name data.formattedAddress = metaInfo.1.formattedName
data.formattedAddress = metaInfo.1.formattedName data.selectionType = .timezone
data.selectionType = .timezone data.isSystemTimezone = metaInfo.0.name == NSTimeZone.system.identifier
data.isSystemTimezone = metaInfo.0.name == NSTimeZone.system.identifier
let operationObject = TimezoneDataOperations(with: data) let operationObject = TimezoneDataOperations(with: data, store: DataStore.shared())
operationObject.saveObject() operationObject.saveObject()
searchResultsDataSource.cleanupFilterArray() searchResultsDataSource?.cleanupFilterArray()
searchResultsDataSource.timezoneFilteredArray = [] searchResultsDataSource?.timezoneFilteredArray = []
searchResultsDataSource.calculateChangesets() searchResultsDataSource?.calculateChangesets()
searchBar.stringValue = CLEmptyString searchBar.stringValue = CLEmptyString
accessoryLabel.stringValue = "Added \(metaInfo.1.formattedName)." accessoryLabel.stringValue = "Added \(metaInfo.1.formattedName)."
undoButton.isHidden = false undoButton.isHidden = false
setupLabelHidingTimer() setupLabelHidingTimer()
resultsTableView.reloadData() resultsTableView.reloadData()
resultsTableView.isHidden = true resultsTableView.isHidden = true
}
} }
private func metadata(for selection: TimezoneMetadata) -> (NSTimeZone, TimezoneMetadata) { private func metadata(for selection: TimezoneMetadata) -> (NSTimeZone, TimezoneMetadata) {
@ -194,7 +196,7 @@ class OnboardingSearchController: NSViewController {
private func fetchTimezone(for latitude: Double, and longitude: Double, _ dataObject: TimezoneData) { private func fetchTimezone(for latitude: Double, and longitude: Double, _ dataObject: TimezoneData) {
if NetworkManager.isConnected() == false || ProcessInfo.processInfo.arguments.contains("mockTimezoneDown") { if NetworkManager.isConnected() == false || ProcessInfo.processInfo.arguments.contains("mockTimezoneDown") {
setInfoLabel(PreferencesConstants.noInternetConnectivityError) setInfoLabel(PreferencesConstants.noInternetConnectivityError)
searchResultsDataSource.cleanupFilterArray() searchResultsDataSource?.cleanupFilterArray()
resultsTableView.reloadData() resultsTableView.reloadData()
return return
} }
@ -215,7 +217,7 @@ class OnboardingSearchController: NSViewController {
} }
if error == nil, let json = response, let response = json.decodeTimezone() { if error == nil, let json = response, let response = json.decodeTimezone() {
if self.resultsTableView.selectedRow >= 0, self.resultsTableView.selectedRow < self.searchResultsDataSource.resultsCount() { if self.resultsTableView.selectedRow >= 0, self.resultsTableView.selectedRow < (self.searchResultsDataSource?.resultsCount()) ?? 0 {
var filteredAddress = "Error" var filteredAddress = "Error"
if let address = dataObject.formattedAddress { if let address = dataObject.formattedAddress {
@ -312,7 +314,7 @@ class OnboardingSearchController: NSViewController {
@objc func actualSearch() { @objc func actualSearch() {
func setupForError() { func setupForError() {
searchResultsDataSource.calculateChangesets() searchResultsDataSource?.calculateChangesets()
resultsTableView.isHidden = true resultsTableView.isHidden = true
} }
@ -345,12 +347,12 @@ class OnboardingSearchController: NSViewController {
return return
} }
self.searchResultsDataSource.cleanupFilterArray() self.searchResultsDataSource?.cleanupFilterArray()
self.searchResultsDataSource.timezoneFilteredArray = [] self.searchResultsDataSource?.timezoneFilteredArray = []
if let errorPresent = error { if let errorPresent = error {
self.findLocalSearchResultsForTimezones() self.findLocalSearchResultsForTimezones()
if self.searchResultsDataSource.timezoneFilteredArray.count == 0 { if self.searchResultsDataSource?.timezoneFilteredArray.count == 0 {
self.presentErrorMessage(errorPresent.localizedDescription) self.presentErrorMessage(errorPresent.localizedDescription)
setupForError() setupForError()
return return
@ -391,12 +393,12 @@ class OnboardingSearchController: NSViewController {
private func findLocalSearchResultsForTimezones() { private func findLocalSearchResultsForTimezones() {
let lowercasedSearchString = searchBar.stringValue.lowercased() let lowercasedSearchString = searchBar.stringValue.lowercased()
searchResultsDataSource.searchTimezones(lowercasedSearchString) searchResultsDataSource?.searchTimezones(lowercasedSearchString)
} }
private func prepareUIForPresentingResults() { private func prepareUIForPresentingResults() {
setInfoLabel(CLEmptyString) setInfoLabel(CLEmptyString)
if searchResultsDataSource.calculateChangesets() { if let dataSource = searchResultsDataSource, dataSource.calculateChangesets() {
resultsTableView.isHidden = false resultsTableView.isHidden = false
resultsTableView.reloadData() resultsTableView.reloadData()
} }
@ -421,13 +423,13 @@ class OnboardingSearchController: NSViewController {
return TimezoneData(with: totalPackage) return TimezoneData(with: totalPackage)
} }
searchResultsDataSource.setFilteredArrayValue(finalTimezones) searchResultsDataSource?.setFilteredArrayValue(finalTimezones)
} }
private func resetSearchView() { private func resetSearchView() {
searchResultsDataSource.cleanupFilterArray() searchResultsDataSource?.cleanupFilterArray()
searchResultsDataSource.timezoneFilteredArray = [] searchResultsDataSource?.timezoneFilteredArray = []
searchResultsDataSource.calculateChangesets() searchResultsDataSource?.calculateChangesets()
resultsTableView.reloadData() resultsTableView.reloadData()
searchBar.stringValue = CLEmptyString searchBar.stringValue = CLEmptyString
searchBar.placeholderString = "Press Enter to Search" searchBar.placeholderString = "Press Enter to Search"
@ -441,15 +443,15 @@ class OnboardingSearchController: NSViewController {
extension OnboardingSearchController: NSTableViewDataSource { extension OnboardingSearchController: NSTableViewDataSource {
func numberOfRows(in _: NSTableView) -> Int { func numberOfRows(in _: NSTableView) -> Int {
return searchResultsDataSource != nil ? searchResultsDataSource.resultsCount() : 0 return searchResultsDataSource?.resultsCount() ?? 0
} }
func tableView(_ tableView: NSTableView, viewFor _: NSTableColumn?, row: Int) -> NSView? { func tableView(_ tableView: NSTableView, viewFor _: NSTableColumn?, row: Int) -> NSView? {
if let result = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "resultCellView"), owner: self) as? ResultTableViewCell, if let result = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "resultCellView"), owner: self) as? ResultTableViewCell,
row >= 0, row >= 0,
row < searchResultsDataSource.resultsCount() row < (searchResultsDataSource?.resultsCount() ?? 0)
{ {
let currentSelection = searchResultsDataSource.retrieveResult(row) let currentSelection = searchResultsDataSource?.retrieveResult(row)
if let timezone = currentSelection as? TimezoneMetadata { if let timezone = currentSelection as? TimezoneMetadata {
result.result.stringValue = " \(timezone.formattedName)" result.result.stringValue = " \(timezone.formattedName)"
} else if let location = currentSelection as? TimezoneData { } else if let location = currentSelection as? TimezoneData {
@ -466,7 +468,7 @@ extension OnboardingSearchController: NSTableViewDataSource {
extension OnboardingSearchController: NSTableViewDelegate { extension OnboardingSearchController: NSTableViewDelegate {
func tableView(_: NSTableView, heightOfRow row: Int) -> CGFloat { func tableView(_: NSTableView, heightOfRow row: Int) -> CGFloat {
if row == 0, searchResultsDataSource.resultsCount() == 0 { if row == 0, searchResultsDataSource?.resultsCount() == 0 {
return 30 return 30
} }
@ -474,7 +476,7 @@ extension OnboardingSearchController: NSTableViewDelegate {
} }
func tableView(_: NSTableView, shouldSelectRow row: Int) -> Bool { func tableView(_: NSTableView, shouldSelectRow row: Int) -> Bool {
return searchResultsDataSource.resultsCount() == 0 ? row != 0 : true return searchResultsDataSource?.resultsCount() == 0 ? row != 0 : true
} }
func tableView(_: NSTableView, rowViewForRow _: Int) -> NSTableRowView? { func tableView(_: NSTableView, rowViewForRow _: Int) -> NSTableRowView? {

63
Clocker/Overall App/AppDefaults.swift

@ -4,46 +4,19 @@ import Cocoa
import CoreLoggerKit import CoreLoggerKit
class AppDefaults { class AppDefaults {
class func initialize() { class func initialize(with store: DataStore, defaults: UserDefaults) {
initializeDefaults() initializeDefaults(with: store, defaults: defaults)
} }
private class func deleteOldUserDefaults() { private class func initializeDefaults(with store: DataStore, defaults: UserDefaults) {
let userDefaults = UserDefaults.standard let timezones = store.timezones()
let selectedCalendars = defaults.object(forKey: CLSelectedCalendars)
// Now delete the old preferences
if let bundleID = Bundle.main.bundleIdentifier, userDefaults.object(forKey: "PreferencesHaveBeenWiped") == nil {
userDefaults.removePersistentDomain(forName: bundleID)
userDefaults.set(true, forKey: "PreferencesHaveBeenWiped")
}
}
private class func initializeDefaults() {
let userDefaults = UserDefaults.standard
let dataStore = DataStore.shared()
let timezones = dataStore.timezones()
let selectedCalendars = userDefaults.object(forKey: CLSelectedCalendars)
// Now delete the old preferences
userDefaults.wipeIfNeccesary()
// Register the usual suspects // Register the usual suspects
userDefaults.register(defaults: defaultsDictionary()) defaults.register(defaults: defaultsDictionary())
dataStore.setTimezones(timezones)
userDefaults.set(selectedCalendars, forKey: CLSelectedCalendars)
// Set the theme default as Light!
setDefaultTheme()
}
private class func setDefaultTheme() {
let defaults = UserDefaults.standard
if defaults.object(forKey: CLThemeKey) == nil { store.setTimezones(timezones)
Themer.shared().set(theme: 0) defaults.set(selectedCalendars, forKey: CLSelectedCalendars)
}
} }
private class func defaultsDictionary() -> [String: Any] { private class func defaultsDictionary() -> [String: Any] {
@ -69,25 +42,9 @@ class AppDefaults {
} }
} }
extension String {
func localized() -> String {
return NSLocalizedString(self, comment: "Title for \(self)")
}
}
extension UserDefaults { extension UserDefaults {
// Use this with caution. Exposing this for debugging purposes only. // Use this with caution. Exposing this for debugging purposes only.
func wipe() { func wipe(for bundleID: String) {
if let bundleID = Bundle.main.bundleIdentifier { removePersistentDomain(forName: bundleID)
removePersistentDomain(forName: bundleID)
}
}
func wipeIfNeccesary() {
if let bundleID = Bundle.main.bundleIdentifier, object(forKey: "PreferencesHaveBeenWiped") == nil {
Logger.info("Wiping all user defaults")
removePersistentDomain(forName: bundleID)
set(true, forKey: "PreferencesHaveBeenWiped")
}
} }
} }

5
Clocker/Overall App/ConfigExport.swift

@ -1,6 +1,7 @@
// Copyright © 2015 Abhishek Banthia // Copyright © 2015 Abhishek Banthia
import CoreModelKit import CoreModelKit
import CoreLoggerKit
import Foundation import Foundation
struct ConfigExport { struct ConfigExport {
@ -34,7 +35,7 @@ struct ConfigExport {
var clockerPrefs: [String: Any] = [:] var clockerPrefs: [String: Any] = [:]
for (key, value) in dictionaryRep { for (key, value) in dictionaryRep {
if selectedKeys.contains(key) { if selectedKeys.contains(key) {
print("Key is \(key) and value is \(value)") Logger.info("Config Export: Key is \(key) and value is \(value)")
clockerPrefs[key] = value clockerPrefs[key] = value
} }
} }
@ -73,7 +74,7 @@ struct ConfigExport {
let json = try JSONSerialization.data(withJSONObject: clockerPrefs, options: .prettyPrinted) let json = try JSONSerialization.data(withJSONObject: clockerPrefs, options: .prettyPrinted)
print(json) print(json)
} catch { } catch {
print("Failure Observed \(error.localizedDescription)") Logger.info("Failure Observed \(error.localizedDescription)")
} }
} }
} }

30
Clocker/Overall App/DataStore.swift

@ -41,8 +41,6 @@ class DataStore: NSObject {
init(with defaults: UserDefaults) { init(with defaults: UserDefaults) {
super.init() super.init()
userDefaults = defaults userDefaults = defaults
shouldDisplayDayInMenubar = shouldDisplay(.dayInMenubar)
shouldDisplayDateInMenubar = shouldDisplay(.dateInMenubar)
setupSyncNotification() setupSyncNotification()
} }
@ -66,7 +64,7 @@ class DataStore: NSObject {
@objc func ubiquitousKeyValueStoreChanged(_ notification: Notification) { @objc func ubiquitousKeyValueStoreChanged(_ notification: Notification) {
let userInfo = notification.userInfo ?? [:] let userInfo = notification.userInfo ?? [:]
let ubiquitousStore = notification.object as? NSUbiquitousKeyValueStore let ubiquitousStore = notification.object as? NSUbiquitousKeyValueStore
print("--- User Info is \(userInfo)") Logger.info("Ubiquitous Store Changed: User Info is \(userInfo)")
let currentTimezones = userDefaults.object(forKey: CLDefaultPreferenceKey) as? [Data] let currentTimezones = userDefaults.object(forKey: CLDefaultPreferenceKey) as? [Data]
let cloudTimezones = ubiquitousStore?.object(forKey: CLDefaultPreferenceKey) as? [Data] let cloudTimezones = ubiquitousStore?.object(forKey: CLDefaultPreferenceKey) as? [Data]
let cloudLastUpdateDate = (ubiquitousStore?.object(forKey: CLUbiquitousStoreLastUpdateKey) as? Date) ?? Date() let cloudLastUpdateDate = (ubiquitousStore?.object(forKey: CLUbiquitousStoreLastUpdateKey) as? Date) ?? Date()
@ -113,20 +111,14 @@ class DataStore: NSObject {
} }
} }
func updateDayPreference() { // MARK: Date (May 8th) in Compact Menubar
shouldDisplayDayInMenubar = shouldDisplay(.dayInMenubar) func shouldShowDateInMenubar() -> Bool {
} return shouldDisplay(.dateInMenubar)
func updateDateInPreference() {
shouldDisplayDateInMenubar = shouldDisplay(.dateInMenubar)
} }
// MARK: Day (Sun, Mon etc.) in Compact Menubar
func shouldShowDayInMenubar() -> Bool { func shouldShowDayInMenubar() -> Bool {
return shouldDisplayDayInMenubar return shouldDisplay(.dayInMenubar)
}
func shouldShowDateInMenubar() -> Bool {
return shouldDisplayDateInMenubar
} }
func retrieve(key: String) -> Any? { func retrieve(key: String) -> Any? {
@ -189,11 +181,11 @@ class DataStore: NSObject {
} }
return value.isEqual(to: NSNumber(value: 1)) return value.isEqual(to: NSNumber(value: 1))
case .dateInMenubar: case .dateInMenubar:
return shouldDisplayHelper(CLShowDateInMenu) return shouldDisplayNonObjectHelper(CLShowDateInMenu)
case .placeInMenubar: case .placeInMenubar:
return shouldDisplayHelper(CLShowPlaceInMenu) return shouldDisplayHelper(CLShowPlaceInMenu)
case .dayInMenubar: case .dayInMenubar:
return shouldDisplayHelper(CLShowDayInMenu) return shouldDisplayNonObjectHelper(CLShowDayInMenu)
case .appDisplayOptions: case .appDisplayOptions:
return shouldDisplayHelper(CLAppDisplayOptions) return shouldDisplayHelper(CLAppDisplayOptions)
case .menubarCompactMode: case .menubarCompactMode:
@ -215,6 +207,12 @@ class DataStore: NSObject {
} }
return value.isEqual(to: NSNumber(value: 0)) return value.isEqual(to: NSNumber(value: 0))
} }
// MARK: Some values are stored as plain integers; objectForKey: will return nil, so using integerForKey:
private func shouldDisplayNonObjectHelper(_ key: String) -> Bool {
let value = userDefaults.integer(forKey: key)
return value == 0
}
} }
extension DataStore { extension DataStore {

4
Clocker/Overall App/String + Additions.swift

@ -14,4 +14,8 @@ extension String {
return filteredAddress return filteredAddress
} }
func localized() -> String {
return NSLocalizedString(self, comment: "Title for \(self)")
}
} }

35
Clocker/Panel/Data Layer/TimezoneDataOperations.swift

@ -6,14 +6,17 @@ import CoreLoggerKit
import CoreModelKit import CoreModelKit
class TimezoneDataOperations: NSObject { class TimezoneDataOperations: NSObject {
private var dataObject: TimezoneData! private let dataObject: TimezoneData
private let store: DataStore
private lazy var nsCalendar = Calendar.autoupdatingCurrent private lazy var nsCalendar = Calendar.autoupdatingCurrent
private static var gregorianCalendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian) private static var gregorianCalendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian)
private static var swiftyCalendar = Calendar(identifier: .gregorian) private static var swiftyCalendar = Calendar(identifier: .gregorian)
private static let currentLocale = Locale.current.identifier private static let currentLocale = Locale.current.identifier
init(with timezone: TimezoneData) { init(with timezone: TimezoneData, store: DataStore) {
dataObject = timezone dataObject = timezone
self.store = store
super.init()
} }
} }
@ -28,7 +31,7 @@ extension TimezoneDataOperations {
return CLEmptyString return CLEmptyString
} }
if dataObject.timezoneFormat(DataStore.shared().timezoneFormat()) == DateFormat.epochTime { if dataObject.timezoneFormat(store.timezoneFormat()) == DateFormat.epochTime {
let timezone = TimeZone(identifier: dataObject.timezone()) let timezone = TimeZone(identifier: dataObject.timezone())
let offset = timezone?.secondsFromGMT(for: newDate) ?? 0 let offset = timezone?.secondsFromGMT(for: newDate) ?? 0
let value = Int(Date().timeIntervalSince1970 + Double(offset)) let value = Int(Date().timeIntervalSince1970 + Double(offset))
@ -36,7 +39,7 @@ extension TimezoneDataOperations {
} }
let dateFormatter = DateFormatterManager.dateFormatterWithFormat(with: .none, let dateFormatter = DateFormatterManager.dateFormatterWithFormat(with: .none,
format: dataObject.timezoneFormat(DataStore.shared().timezoneFormat()), format: dataObject.timezoneFormat(store.timezoneFormat()),
timezoneIdentifier: dataObject.timezone(), timezoneIdentifier: dataObject.timezone(),
locale: Locale.autoupdatingCurrent) locale: Locale.autoupdatingCurrent)
@ -80,15 +83,15 @@ extension TimezoneDataOperations {
func compactMenuTitle() -> String { func compactMenuTitle() -> String {
var subtitle = CLEmptyString var subtitle = CLEmptyString
let shouldDayBeShown = DataStore.shared().shouldShowDayInMenubar() let shouldDayBeShown = store.shouldShowDayInMenubar()
let shouldLabelBeShownAlongWithTime = !DataStore.shared().shouldDisplay(.placeInMenubar) let shouldLabelBeShownAlongWithTime = !store.shouldDisplay(.placeInMenubar)
if shouldDayBeShown, shouldLabelBeShownAlongWithTime { if shouldDayBeShown, shouldLabelBeShownAlongWithTime {
let substring = date(with: 0, displayType: .menu) let substring = date(with: 0, displayType: .menu)
subtitle.append(substring) subtitle.append(substring)
} }
let shouldDateBeShown = DataStore.shared().shouldShowDateInMenubar() let shouldDateBeShown = store.shouldShowDateInMenubar()
if shouldDateBeShown, shouldLabelBeShownAlongWithTime { if shouldDateBeShown, shouldLabelBeShownAlongWithTime {
let date = Date().formatter(with: "MMM d", timeZone: dataObject.timezone()) let date = Date().formatter(with: "MMM d", timeZone: dataObject.timezone())
subtitle.isEmpty ? subtitle.append("\(date)") : subtitle.append(" \(date)") subtitle.isEmpty ? subtitle.append("\(date)") : subtitle.append(" \(date)")
@ -100,15 +103,15 @@ extension TimezoneDataOperations {
func compactMenuSubtitle() -> String { func compactMenuSubtitle() -> String {
var subtitle = CLEmptyString var subtitle = CLEmptyString
let shouldDayBeShown = DataStore.shared().shouldShowDayInMenubar() let shouldDayBeShown = store.shouldShowDayInMenubar()
let shouldLabelsNotBeShownAlongWithTime = DataStore.shared().shouldDisplay(.placeInMenubar) let shouldLabelsNotBeShownAlongWithTime = store.shouldDisplay(.placeInMenubar)
if shouldDayBeShown, shouldLabelsNotBeShownAlongWithTime { if shouldDayBeShown, shouldLabelsNotBeShownAlongWithTime {
let substring = date(with: 0, displayType: .menu) let substring = date(with: 0, displayType: .menu)
subtitle.append(substring) subtitle.append(substring)
} }
let shouldDateBeShown = DataStore.shared().shouldShowDateInMenubar() let shouldDateBeShown = store.shouldShowDateInMenubar()
if shouldDateBeShown, shouldLabelsNotBeShownAlongWithTime { if shouldDateBeShown, shouldLabelsNotBeShownAlongWithTime {
let date = Date().formatter(with: "MMM d", timeZone: dataObject.timezone()) let date = Date().formatter(with: "MMM d", timeZone: dataObject.timezone())
subtitle.isEmpty ? subtitle.append("\(date)") : subtitle.append(" \(date)") subtitle.isEmpty ? subtitle.append("\(date)") : subtitle.append(" \(date)")
@ -122,7 +125,7 @@ extension TimezoneDataOperations {
func menuTitle() -> String { func menuTitle() -> String {
var menuTitle = CLEmptyString var menuTitle = CLEmptyString
let dataStore = DataStore.shared() let dataStore = store
let shouldCityBeShown = dataStore.shouldDisplay(.placeInMenubar) let shouldCityBeShown = dataStore.shouldDisplay(.placeInMenubar)
let shouldDayBeShown = dataStore.shouldShowDayInMenubar() let shouldDayBeShown = dataStore.shouldShowDayInMenubar()
@ -199,7 +202,7 @@ extension TimezoneDataOperations {
} }
func date(with sliderValue: Int, displayType: TimezoneData.DateDisplayType) -> String { func date(with sliderValue: Int, displayType: TimezoneData.DateDisplayType) -> String {
guard let relativeDayPreference = DataStore.shared().retrieve(key: CLRelativeDateKey) as? NSNumber else { guard let relativeDayPreference = store.retrieve(key: CLRelativeDateKey) as? NSNumber else {
assertionFailure("Data was unexpectedly nil") assertionFailure("Data was unexpectedly nil")
return CLEmptyString return CLEmptyString
} }
@ -332,7 +335,7 @@ extension TimezoneDataOperations {
let minuteDifference = calculateTimeDifference(with: local as NSDate, timezoneDate: timezoneDate as NSDate) let minuteDifference = calculateTimeDifference(with: local as NSDate, timezoneDate: timezoneDate as NSDate)
minuteDifference == 0 ? replaceAgo.append("behind") : replaceAgo.append("\(minuteDifference) mins behind") minuteDifference == 0 ? replaceAgo.append("behind") : replaceAgo.append("\(minuteDifference)m behind")
return replaceAgo.lowercased() return replaceAgo.lowercased()
} }
@ -388,7 +391,7 @@ extension TimezoneDataOperations {
let dateFormatter = DateFormatter() let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US") dateFormatter.locale = Locale(identifier: "en_US")
dateFormatter.timeZone = TimeZone(identifier: dataObject.timezone()) dateFormatter.timeZone = TimeZone(identifier: dataObject.timezone())
dateFormatter.dateFormat = dataObject.timezoneFormat(DataStore.shared().timezoneFormat()) dateFormatter.dateFormat = dataObject.timezoneFormat(store.timezoneFormat())
return dateFormatter.string(from: correct) return dateFormatter.string(from: correct)
} }
@ -417,10 +420,10 @@ extension TimezoneDataOperations {
} }
func saveObject(at index: Int = -1) { func saveObject(at index: Int = -1) {
var defaults = DataStore.shared().timezones() var defaults = store.timezones()
let encodedObject = NSKeyedArchiver.archivedData(withRootObject: dataObject as Any) let encodedObject = NSKeyedArchiver.archivedData(withRootObject: dataObject as Any)
index == -1 ? defaults.append(encodedObject) : defaults.insert(encodedObject, at: index) index == -1 ? defaults.append(encodedObject) : defaults.insert(encodedObject, at: index)
DataStore.shared().setTimezones(defaults) store.setTimezones(defaults)
} }
} }

2
Clocker/Panel/PanelController.swift

@ -268,7 +268,7 @@ class PanelController: ParentPanelController {
if count >= 1 || DataStore.shared().shouldDisplay(.showMeetingInMenubar) { if count >= 1 || DataStore.shared().shouldDisplay(.showMeetingInMenubar) {
if let delegate = NSApplication.shared.delegate as? AppDelegate { if let delegate = NSApplication.shared.delegate as? AppDelegate {
Logger.info("\nWe will be invalidating the menubar timer as we want the parent timer to take care of both panel and menubar ") Logger.info("We will be invalidating the menubar timer as we want the parent timer to take care of both panel and menubar ")
delegate.invalidateMenubarTimer(false) delegate.invalidateMenubarTimer(false)
} }

29
Clocker/Panel/ParentPanelController.swift

@ -89,7 +89,7 @@ class ParentPanelController: NSWindowController {
// Upcoming Events // Upcoming Events
@IBOutlet var upcomingEventCollectionView: NSCollectionView! @IBOutlet var upcomingEventCollectionView: NSCollectionView!
@IBOutlet var upcomingEventContainerView: NSView! @IBOutlet var upcomingEventContainerView: NSView!
public var upcomingEventsDataSource: UpcomingEventsDataSource! public var upcomingEventsDataSource: UpcomingEventsDataSource?
var defaultPreferences: [Data] { var defaultPreferences: [Data] {
return DataStore.shared().timezones() return DataStore.shared().timezones()
@ -426,7 +426,7 @@ class ParentPanelController: NSWindowController {
if let note = object?.note, note.isEmpty == false { if let note = object?.note, note.isEmpty == false {
newHeight += 20 newHeight += 20
} else if let obj = object, } else if let obj = object,
TimezoneDataOperations(with: obj).nextDaylightSavingsTransitionIfAvailable(with: futureSliderValue) != nil TimezoneDataOperations(with: obj, store: DataStore.shared()).nextDaylightSavingsTransitionIfAvailable(with: futureSliderValue) != nil
{ {
newHeight += 20 newHeight += 20
} }
@ -436,7 +436,7 @@ class ParentPanelController: NSWindowController {
// Set it to 90 expicity in case the row height is calculated be higher. // Set it to 90 expicity in case the row height is calculated be higher.
newHeight = 88.0 newHeight = 88.0
if let note = object?.note, note.isEmpty, let obj = object, TimezoneDataOperations(with: obj).nextDaylightSavingsTransitionIfAvailable(with: futureSliderValue) == nil { if let note = object?.note, note.isEmpty, let obj = object, TimezoneDataOperations(with: obj, store: DataStore.shared()).nextDaylightSavingsTransitionIfAvailable(with: futureSliderValue) == nil {
newHeight -= 20.0 newHeight -= 20.0
} }
} }
@ -509,12 +509,13 @@ class ParentPanelController: NSWindowController {
updatePanelColor() updatePanelColor()
let defaults = DataStore.shared().timezones() let store = DataStore.shared()
let defaults = store.timezones()
let convertedTimezones = defaults.map { data -> TimezoneData in let convertedTimezones = defaults.map { data -> TimezoneData in
TimezoneData.customObject(from: data)! TimezoneData.customObject(from: data)!
} }
datasource = TimezoneDataSource(items: convertedTimezones) datasource = TimezoneDataSource(items: convertedTimezones, store: store)
mainTableView.dataSource = datasource mainTableView.dataSource = datasource
mainTableView.delegate = datasource mainTableView.delegate = datasource
mainTableView.panelDelegate = datasource mainTableView.panelDelegate = datasource
@ -586,6 +587,9 @@ class ParentPanelController: NSWindowController {
private lazy var menubarTitleHandler = MenubarTitleProvider(with: DataStore.shared(), eventStore: EventCenter.sharedCenter()) private lazy var menubarTitleHandler = MenubarTitleProvider(with: DataStore.shared(), eventStore: EventCenter.sharedCenter())
static private let attributes: [NSAttributedString.Key : Any] = [NSAttributedString.Key.font: NSFont.monospacedDigitSystemFont(ofSize: 13.0, weight: NSFont.Weight.regular),
NSAttributedString.Key.baselineOffset : 0.1]
@objc func updateTime() { @objc func updateTime() {
let store = DataStore.shared() let store = DataStore.shared()
@ -596,7 +600,8 @@ class ParentPanelController: NSWindowController {
if store.shouldDisplay(.menubarCompactMode) { if store.shouldDisplay(.menubarCompactMode) {
status.updateCompactMenubar() status.updateCompactMenubar()
} else { } else {
status.statusItem.title = menubarTitleHandler.titleForMenubar() status.statusItem.button?.attributedTitle = NSAttributedString(string: menubarTitleHandler.titleForMenubar() ?? "",
attributes: ParentPanelController.attributes)
} }
} }
} }
@ -623,7 +628,7 @@ class ParentPanelController: NSWindowController {
if modernContainerView != nil, modernSlider.isHidden == false, modernContainerView.currentlyInFocus { if modernContainerView != nil, modernSlider.isHidden == false, modernContainerView.currentlyInFocus {
return return
} }
let dataOperation = TimezoneDataOperations(with: model) let dataOperation = TimezoneDataOperations(with: model, store: DataStore.shared())
cellView.time.stringValue = dataOperation.time(with: futureSliderValue) cellView.time.stringValue = dataOperation.time(with: futureSliderValue)
cellView.sunriseSetTime.stringValue = dataOperation.formattedSunriseTime(with: futureSliderValue) cellView.sunriseSetTime.stringValue = dataOperation.formattedSunriseTime(with: futureSliderValue)
cellView.sunriseSetTime.lineBreakMode = .byClipping cellView.sunriseSetTime.lineBreakMode = .byClipping
@ -635,7 +640,7 @@ class ParentPanelController: NSWindowController {
} }
if let note = model.note, !note.isEmpty { if let note = model.note, !note.isEmpty {
cellView.noteLabel.stringValue = note cellView.noteLabel.stringValue = note
} else if let value = TimezoneDataOperations(with: model).nextDaylightSavingsTransitionIfAvailable(with: futureSliderValue) { } else if let value = TimezoneDataOperations(with: model, store: DataStore.shared()).nextDaylightSavingsTransitionIfAvailable(with: futureSliderValue) {
cellView.noteLabel.stringValue = value cellView.noteLabel.stringValue = value
} else { } else {
cellView.noteLabel.stringValue = CLEmptyString cellView.noteLabel.stringValue = CLEmptyString
@ -829,7 +834,7 @@ class ParentPanelController: NSWindowController {
if self.upcomingEventCollectionView != nil, if self.upcomingEventCollectionView != nil,
let upcomingEvents = eventCenter.upcomingEventsForDay(events) let upcomingEvents = eventCenter.upcomingEventsForDay(events)
{ {
self.upcomingEventsDataSource.updateEventsDataSource(upcomingEvents) self.upcomingEventsDataSource?.updateEventsDataSource(upcomingEvents)
self.upcomingEventCollectionView.reloadData() self.upcomingEventCollectionView.reloadData()
return return
} }
@ -840,7 +845,7 @@ class ParentPanelController: NSWindowController {
} }
} else { } else {
if upcomingEventCollectionView != nil { if upcomingEventCollectionView != nil {
upcomingEventsDataSource.updateEventsDataSource([]) upcomingEventsDataSource?.updateEventsDataSource([])
upcomingEventCollectionView.reloadData() upcomingEventCollectionView.reloadData()
return return
} }
@ -1124,7 +1129,7 @@ extension ParentPanelController: NSSharingServicePickerDelegate {
return clipboardCopy return clipboardCopy
} }
let timezoneOperations = TimezoneDataOperations(with: earliestTimezone) let timezoneOperations = TimezoneDataOperations(with: earliestTimezone, store: DataStore.shared())
var sectionTitle = timezoneOperations.todaysDate(with: 0) // TODO: Take slider value into consideration var sectionTitle = timezoneOperations.todaysDate(with: 0) // TODO: Take slider value into consideration
clipboardCopy.append("\(sectionTitle)\n") clipboardCopy.append("\(sectionTitle)\n")
@ -1132,7 +1137,7 @@ extension ParentPanelController: NSSharingServicePickerDelegate {
if $0 < sortedByTime.count, if $0 < sortedByTime.count,
let dataModel = TimezoneData.customObject(from: sortedByTime[$0]) let dataModel = TimezoneData.customObject(from: sortedByTime[$0])
{ {
let dataOperations = TimezoneDataOperations(with: dataModel) let dataOperations = TimezoneDataOperations(with: dataModel, store: DataStore.shared())
let date = dataOperations.todaysDate(with: 0) let date = dataOperations.todaysDate(with: 0)
let time = dataOperations.time(with: 0) let time = dataOperations.time(with: 0)
if date != sectionTitle { if date != sectionTitle {

3
Clocker/Panel/Rate Controller/UpcomingEventView.swift

@ -1,6 +1,7 @@
// Copyright © 2015 Abhishek Banthia // Copyright © 2015 Abhishek Banthia
import Cocoa import Cocoa
import CoreLoggerKit
class ModernSliderContainerView: NSView { class ModernSliderContainerView: NSView {
private var trackingArea: NSTrackingArea? private var trackingArea: NSTrackingArea?
@ -67,7 +68,7 @@ class DraggableClipView: NSClipView {
clickPoint = nil clickPoint = nil
gestureInProgress = false gestureInProgress = false
default: default:
print("Default case is happening \(event.type)") Logger.info("Default mouse event occurred for \(event.type)")
} }
} }
} }

23
Clocker/Panel/UI/TimezoneDataSource.swift

@ -6,10 +6,13 @@ import CoreModelKit
class TimezoneDataSource: NSObject { class TimezoneDataSource: NSObject {
var timezones: [TimezoneData] = [] var timezones: [TimezoneData] = []
var sliderValue: Int = 0 var sliderValue: Int = 0
var dataStore: DataStore
init(items: [TimezoneData]) { init(items: [TimezoneData], store: DataStore) {
sliderValue = 0 sliderValue = 0
timezones = Array(items) timezones = Array(items)
dataStore = store
super.init()
} }
} }
@ -51,7 +54,7 @@ extension TimezoneDataSource: NSTableViewDataSource, NSTableViewDelegate {
} }
let currentModel = timezones[row] let currentModel = timezones[row]
let operation = TimezoneDataOperations(with: currentModel) let operation = TimezoneDataOperations(with: currentModel, store: dataStore)
cellView.sunriseSetTime.stringValue = operation.formattedSunriseTime(with: sliderValue) cellView.sunriseSetTime.stringValue = operation.formattedSunriseTime(with: sliderValue)
cellView.sunriseImage.image = currentModel.isSunriseOrSunset ? Themer.shared().sunriseImage() : Themer.shared().sunsetImage() cellView.sunriseImage.image = currentModel.isSunriseOrSunset ? Themer.shared().sunriseImage() : Themer.shared().sunsetImage()
@ -85,12 +88,12 @@ extension TimezoneDataSource: NSTableViewDataSource, NSTableViewDelegate {
return 100 return 100
} }
if let userFontSize = DataStore.shared().retrieve(key: CLUserFontSizePreference) as? NSNumber, if let userFontSize = dataStore.retrieve(key: CLUserFontSizePreference) as? NSNumber,
timezones.count > row, timezones.count > row,
let relativeDisplay = DataStore.shared().retrieve(key: CLRelativeDateKey) as? NSNumber let relativeDisplay = dataStore.retrieve(key: CLRelativeDateKey) as? NSNumber
{ {
let model = timezones[row] let model = timezones[row]
let shouldShowSunrise = DataStore.shared().shouldDisplay(.sunrise) let shouldShowSunrise = dataStore.shouldDisplay(.sunrise)
var rowHeight: Int = userFontSize == 4 ? 60 : 65 var rowHeight: Int = userFontSize == 4 ? 60 : 65
@ -104,7 +107,7 @@ extension TimezoneDataSource: NSTableViewDataSource, NSTableViewDelegate {
if let note = model.note, !note.isEmpty { if let note = model.note, !note.isEmpty {
rowHeight += userFontSize.intValue + 15 rowHeight += userFontSize.intValue + 15
} else if TimezoneDataOperations(with: model).nextDaylightSavingsTransitionIfAvailable(with: sliderValue) != nil { } else if TimezoneDataOperations(with: model, store: dataStore).nextDaylightSavingsTransitionIfAvailable(with: sliderValue) != nil {
rowHeight += userFontSize.intValue + 15 rowHeight += userFontSize.intValue + 15
} }
@ -140,7 +143,7 @@ extension TimezoneDataSource: NSTableViewDataSource, NSTableViewDelegate {
tableView.removeRows(at: indexSet, withAnimation: NSTableView.AnimationOptions()) tableView.removeRows(at: indexSet, withAnimation: NSTableView.AnimationOptions())
if DataStore.shared().shouldDisplay(ViewType.showAppInForeground) { if self.dataStore.shouldDisplay(ViewType.showAppInForeground) {
windowController.deleteTimezone(at: row) windowController.deleteTimezone(at: row)
} else { } else {
guard let panelController = PanelController.panel() else { return } guard let panelController = PanelController.panel() else { return }
@ -173,12 +176,14 @@ extension TimezoneDataSource: NSTableViewDataSource, NSTableViewDelegate {
let response = alert.runModal() let response = alert.runModal()
if response.rawValue == 1000 { if response.rawValue == 1000 {
OperationQueue.main.addOperation { OperationQueue.main.addOperation { [weak self] in
guard let sSelf = self else { return }
let indexSet = IndexSet(integer: row) let indexSet = IndexSet(integer: row)
tableView.removeRows(at: indexSet, withAnimation: NSTableView.AnimationOptions.slideUp) tableView.removeRows(at: indexSet, withAnimation: NSTableView.AnimationOptions.slideUp)
if DataStore.shared().shouldDisplay(ViewType.showAppInForeground) { if sSelf.dataStore.shouldDisplay(ViewType.showAppInForeground) {
let windowController = FloatingWindowController.shared() let windowController = FloatingWindowController.shared()
windowController.deleteTimezone(at: row) windowController.deleteTimezone(at: row)
} else { } else {

3
Clocker/Preferences/App Feedback/AppFeedbackWindowController.swift

@ -18,7 +18,6 @@ extension NSNib.Name {
} }
enum AppFeedbackConstants { enum AppFeedbackConstants {
static let CLAppFeedbackNibIdentifier = "AppFeedbackWindow"
static let CLAppFeedbackNoResponseString = "Not Provided" static let CLAppFeedbackNoResponseString = "Not Provided"
static let CLAppFeedbackNameProperty = "name" static let CLAppFeedbackNameProperty = "name"
static let CLAppFeedbackEmailProperty = "email" static let CLAppFeedbackEmailProperty = "email"
@ -256,7 +255,7 @@ class AppFeedbackWindowController: NSWindowController {
isActivityInProgress = false isActivityInProgress = false
let alert = NSAlert() let alert = NSAlert()
alert.messageText = "Thank you for helping make Clocker even better!" alert.messageText = AppFeedbackConstants.CLFeedbackAlertTitle
alert.informativeText = AppFeedbackConstants.CLFeedbackAlertInformativeText alert.informativeText = AppFeedbackConstants.CLFeedbackAlertInformativeText
alert.addButton(withTitle: AppFeedbackConstants.CLFeedbackAlertButtonTitle) alert.addButton(withTitle: AppFeedbackConstants.CLFeedbackAlertButtonTitle)
alert.beginSheetModal(for: feedbackWindow) { _ in alert.beginSheetModal(for: feedbackWindow) { _ in

4
Clocker/Preferences/Appearance/AppearanceViewController.swift

@ -316,12 +316,10 @@ class AppearanceViewController: ParentViewController {
} }
@IBAction func displayDayInMenubarAction(_: Any) { @IBAction func displayDayInMenubarAction(_: Any) {
DataStore.shared().updateDayPreference()
updateStatusItem() updateStatusItem()
} }
@IBAction func displayDateInMenubarAction(_: Any) { @IBAction func displayDateInMenubarAction(_: Any) {
DataStore.shared().updateDateInPreference()
updateStatusItem() updateStatusItem()
} }
@ -381,7 +379,7 @@ extension AppearanceViewController: NSTableViewDataSource, NSTableViewDelegate {
} }
let currentModel = previewTimezones[row] let currentModel = previewTimezones[row]
let operation = TimezoneDataOperations(with: currentModel) let operation = TimezoneDataOperations(with: currentModel, store: DataStore.shared())
cellView.sunriseSetTime.stringValue = operation.formattedSunriseTime(with: 0) cellView.sunriseSetTime.stringValue = operation.formattedSunriseTime(with: 0)
cellView.sunriseImage.image = currentModel.isSunriseOrSunset ? Themer.shared().sunriseImage() : Themer.shared().sunsetImage() cellView.sunriseImage.image = currentModel.isSunriseOrSunset ? Themer.shared().sunriseImage() : Themer.shared().sunsetImage()

10
Clocker/Preferences/General/PreferencesDataSource.swift

@ -22,11 +22,13 @@ protocol PreferenceSelectionUpdates: AnyObject {
class PreferencesDataSource: NSObject { class PreferencesDataSource: NSObject {
private weak var updateDelegate: PreferenceSelectionUpdates? private weak var updateDelegate: PreferenceSelectionUpdates?
private let store: DataStore
var selectedTimezones: [Data] { var selectedTimezones: [Data] {
return DataStore.shared().timezones() return store.timezones()
} }
init(callbackDelegate delegate: PreferenceSelectionUpdates) { init(with store: DataStore, callbackDelegate delegate: PreferenceSelectionUpdates) {
self.store = store
updateDelegate = delegate updateDelegate = delegate
super.init() super.init()
} }
@ -75,7 +77,7 @@ extension PreferencesDataSource: NSTableViewDelegate {
newOrder.insert(currentObject, at: destination) newOrder.insert(currentObject, at: destination)
DataStore.shared().setTimezones(newOrder) store.setTimezones(newOrder)
tableView.reloadData() tableView.reloadData()
@ -185,7 +187,7 @@ extension PreferencesDataSource: NSTableViewDataSource {
let encodedObject = NSKeyedArchiver.archivedData(withRootObject: timezone) let encodedObject = NSKeyedArchiver.archivedData(withRootObject: timezone)
var newDefaults = selectedTimezones var newDefaults = selectedTimezones
newDefaults[index] = encodedObject newDefaults[index] = encodedObject
DataStore.shared().setTimezones(newDefaults) store.setTimezones(newDefaults)
} }
private func updateMenubarTitles() { private func updateMenubarTitles() {

6
Clocker/Preferences/General/PreferencesViewController.swift

@ -116,7 +116,7 @@ class PreferencesViewController: ParentViewController {
searchField.placeholderString = "Enter city, state, country or timezone name" searchField.placeholderString = "Enter city, state, country or timezone name"
selectionsDataSource = PreferencesDataSource(callbackDelegate: self) selectionsDataSource = PreferencesDataSource(with: DataStore.shared(), callbackDelegate: self)
timezoneTableView.dataSource = selectionsDataSource timezoneTableView.dataSource = selectionsDataSource
timezoneTableView.delegate = selectionsDataSource timezoneTableView.delegate = selectionsDataSource
@ -612,7 +612,7 @@ extension PreferencesViewController {
// Mark if the timezone is same as local timezone // Mark if the timezone is same as local timezone
let timezoneObject = TimezoneData(with: newTimeZone) let timezoneObject = TimezoneData(with: newTimeZone)
let operationsObject = TimezoneDataOperations(with: timezoneObject) let operationsObject = TimezoneDataOperations(with: timezoneObject, store: DataStore.shared())
operationsObject.saveObject() operationsObject.saveObject()
Logger.log(object: ["PlaceName": filteredAddress, "Timezone": timezone.timeZoneId], for: "Filtered Address") Logger.log(object: ["PlaceName": filteredAddress, "Timezone": timezone.timeZoneId], for: "Filtered Address")
@ -746,7 +746,7 @@ extension PreferencesViewController {
data.selectionType = .timezone data.selectionType = .timezone
data.isSystemTimezone = metaInfo.0.name == NSTimeZone.system.identifier data.isSystemTimezone = metaInfo.0.name == NSTimeZone.system.identifier
let operationObject = TimezoneDataOperations(with: data) let operationObject = TimezoneDataOperations(with: data, store: DataStore.shared())
operationObject.saveObject() operationObject.saveObject()
searchResultsDataSource.cleanupFilterArray() searchResultsDataSource.cleanupFilterArray()

2
Clocker/Preferences/Menu Bar/MenubarTitleProvider.swift

@ -34,7 +34,7 @@ class MenubarTitleProvider: NSObject {
if menubarTitles.isEmpty == false { if menubarTitles.isEmpty == false {
let titles = menubarTitles.map { data -> String? in let titles = menubarTitles.map { data -> String? in
let timezone = TimezoneData.customObject(from: data) let timezone = TimezoneData.customObject(from: data)
let operationsObject = TimezoneDataOperations(with: timezone!) let operationsObject = TimezoneDataOperations(with: timezone!, store: store)
return "\(operationsObject.menuTitle().trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines))" return "\(operationsObject.menuTitle().trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines))"
} }

10
Clocker/Preferences/Menu Bar/StatusContainerView.swift

@ -47,6 +47,12 @@ protocol StatusItemViewConforming {
func statusItemViewIdentifier() -> String func statusItemViewIdentifier() -> String
} }
/// Observe for User Default changes for timezones in App Delegate and reconstruct the Status View if neccesary
/// We'll inject the menubar timezones into Status Container View which'll pass it to StatusItemView
/// The benefit of doing so is reducing time-spent calculating menubar timezones and deserialization through `TimezoneData.customObject`
/// Also inject, `shouldDisplaySecondsInMenubar`
///
class StatusContainerView: NSView { class StatusContainerView: NSView {
private var previousX: Int = 0 private var previousX: Int = 0
private let store: DataStore private let store: DataStore
@ -96,7 +102,7 @@ class StatusContainerView: NSView {
if let timezoneObject = TimezoneData.customObject(from: timezone) { if let timezoneObject = TimezoneData.customObject(from: timezone) {
let precalculatedWidth = Double(compactWidth(for: timezoneObject, with: store)) let precalculatedWidth = Double(compactWidth(for: timezoneObject, with: store))
let operationObject = TimezoneDataOperations(with: timezoneObject) let operationObject = TimezoneDataOperations(with: timezoneObject, store: store)
let calculatedSubtitleSize = compactModeTimeFont.size(for: operationObject.compactMenuSubtitle(), let calculatedSubtitleSize = compactModeTimeFont.size(for: operationObject.compactMenuSubtitle(),
width: precalculatedWidth, width: precalculatedWidth,
attributes: timeBasedAttributes) attributes: timeBasedAttributes)
@ -158,7 +164,7 @@ class StatusContainerView: NSView {
NSAttributedString.Key.paragraphStyle: defaultParagraphStyle, NSAttributedString.Key.paragraphStyle: defaultParagraphStyle,
] ]
let operation = TimezoneDataOperations(with: timezone) let operation = TimezoneDataOperations(with: timezone, store: store)
let bestSize = compactModeTimeFont.size(for: operation.compactMenuSubtitle(), let bestSize = compactModeTimeFont.size(for: operation.compactMenuSubtitle(),
width: Double(compactWidth(for: timezone, with: store)), width: Double(compactWidth(for: timezone, with: store)),
attributes: timeBasedAttributes) attributes: timeBasedAttributes)

7
Clocker/Preferences/Menu Bar/StatusItemHandler.swift

@ -61,7 +61,7 @@ class StatusItemHandler: NSObject {
setClockerIcon() setClockerIcon()
} }
Logger.info("\nStatus Bar Current State changed: \(currentState)\n") Logger.info("Status Bar Current State changed: \(currentState)\n")
} }
} }
@ -366,8 +366,9 @@ class StatusItemHandler: NSObject {
return return
} }
statusItem.button?.title = menubarText let attributes = [NSAttributedString.Key.font: NSFont.monospacedDigitSystemFont(ofSize: 13.0, weight: NSFont.Weight.regular),
statusItem.button?.font = NSFont.monospacedDigitSystemFont(ofSize: 14.0, weight: NSFont.Weight.regular) NSAttributedString.Key.baselineOffset : 0.1] as [NSAttributedString.Key : Any]
statusItem.button?.attributedTitle = NSAttributedString(string: menubarText, attributes: attributes)
statusItem.button?.image = nil statusItem.button?.image = nil
statusItem.button?.imagePosition = .imageLeft statusItem.button?.imagePosition = .imageLeft
} }

2
Clocker/Preferences/Menu Bar/StatusItemView.swift

@ -40,7 +40,7 @@ class StatusItemView: NSView {
private let locationView = NSTextField(labelWithString: "Hello") private let locationView = NSTextField(labelWithString: "Hello")
private let timeView = NSTextField(labelWithString: "Mon 19:14 PM") private let timeView = NSTextField(labelWithString: "Mon 19:14 PM")
private var operationsObject: TimezoneDataOperations { private var operationsObject: TimezoneDataOperations {
return TimezoneDataOperations(with: dataObject) return TimezoneDataOperations(with: dataObject, store: DataStore.shared())
} }
private var timeAttributes: [NSAttributedString.Key: AnyObject] { private var timeAttributes: [NSAttributedString.Key: AnyObject] {

20
Clocker/Preferences/Preferences.storyboard

@ -283,7 +283,7 @@
<rect key="frame" x="8" y="13" width="317" height="45"/> <rect key="frame" x="8" y="13" width="317" height="45"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" id="nKQ-6v-Ka2"> <textFieldCell key="cell" sendsActionOnEndEditing="YES" id="nKQ-6v-Ka2">
<font key="font" size="11" name="Avenir-Book"/> <font key="font" size="11" name="Avenir-Book"/>
<string key="title">When selected, your upcoming meeting title will appear in the menubar 30 mins before it starts. All Day Events won't be shown in the menubar!</string> <string key="title">When selected, your upcoming meeting title will appear in the menubar 30m before it starts. All Day Events won't be shown in the menubar!</string>
<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>
@ -411,7 +411,7 @@
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/> <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews> <prototypeCellViews>
<tableCellView identifier="sourceCellView" misplaced="YES" id="yOO-gI-yD1" customClass="SourceTableViewCell" customModule="Clocker" customModuleProvider="target"> <tableCellView identifier="sourceCellView" misplaced="YES" id="yOO-gI-yD1" customClass="SourceTableViewCell" customModule="Clocker" customModuleProvider="target">
<rect key="frame" x="11" y="1" width="270" height="33"/> <rect key="frame" x="1" y="1" width="270" height="33"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="AIA-oD-PWu"> <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="AIA-oD-PWu">
@ -434,7 +434,7 @@
</connections> </connections>
</tableCellView> </tableCellView>
<tableCellView identifier="calendarCellView" misplaced="YES" id="Guw-Ai-ICX" customClass="CalendarTableViewCell" customModule="Clocker" customModuleProvider="target"> <tableCellView identifier="calendarCellView" misplaced="YES" id="Guw-Ai-ICX" customClass="CalendarTableViewCell" customModule="Clocker" customModuleProvider="target">
<rect key="frame" x="11" y="36" width="270" height="33"/> <rect key="frame" x="1" y="36" width="270" height="33"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="4c9-Nn-Umd"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="4c9-Nn-Umd">
@ -984,7 +984,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<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"> <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="98"/>
<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" red="0.50196078431372548" green="0.50196078431372548" blue="0.50196078431372548" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.50196078431372548" green="0.50196078431372548" blue="0.50196078431372548" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
@ -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="121" width="320" height="23"/> <rect key="frame" x="8" y="97" 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="81"/> <rect key="frame" x="8" y="30" width="320" height="57"/>
<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="81"/> <rect key="frame" x="0.0" y="0.0" width="320" height="57"/>
<autoresizingMask key="autoresizingMask"/> <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="81"/> <rect key="frame" x="0.0" y="0.0" width="320" height="57"/>
<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="90" width="16" height="16"/> <rect key="frame" x="160" y="78" 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="60" width="300" height="22"/> <rect key="frame" x="18" y="48" 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>

Loading…
Cancel
Save