Browse Source

Seperate Data source for Search Results

pull/92/head
Abhishek 5 years ago
parent
commit
b99c8cec21
  1. 4
      Clocker/Clocker.xcodeproj/project.pbxproj
  2. 218
      Clocker/Preferences/General/PreferencesViewController.swift
  3. 185
      Clocker/Preferences/General/SearchDataSource.swift

4
Clocker/Clocker.xcodeproj/project.pbxproj

@ -122,6 +122,7 @@
9ACB31401EDA994200F3E1D3 /* ShortcutRecorder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ACB313F1EDA994200F3E1D3 /* ShortcutRecorder.framework */; }; 9ACB31401EDA994200F3E1D3 /* ShortcutRecorder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ACB313F1EDA994200F3E1D3 /* ShortcutRecorder.framework */; };
9ACE03EF1CB0ADE00039FC01 /* Firebase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ACE03EE1CB0ADE00039FC01 /* Firebase.framework */; }; 9ACE03EF1CB0ADE00039FC01 /* Firebase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ACE03EE1CB0ADE00039FC01 /* Firebase.framework */; };
9ACF469D1DCBD45200C49B51 /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ACF469C1DCBD45200C49B51 /* Quartz.framework */; }; 9ACF469D1DCBD45200C49B51 /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ACF469C1DCBD45200C49B51 /* Quartz.framework */; };
9ACF618D231DABAE00F5E51E /* SearchDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ACF618C231DABAE00F5E51E /* SearchDataSource.swift */; };
C20839CA21515C1E00C86589 /* ClockerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C20839C921515C1E00C86589 /* ClockerUnitTests.swift */; }; C20839CA21515C1E00C86589 /* ClockerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C20839C921515C1E00C86589 /* ClockerUnitTests.swift */; };
C213713420B4FD920024D5A4 /* FloatingWindowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C213713320B4FD920024D5A4 /* FloatingWindowTests.swift */; }; C213713420B4FD920024D5A4 /* FloatingWindowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C213713320B4FD920024D5A4 /* FloatingWindowTests.swift */; };
C22F3D802107778A0001D5E1 /* ShortcutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C22F3D7F2107778A0001D5E1 /* ShortcutTests.swift */; }; C22F3D802107778A0001D5E1 /* ShortcutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C22F3D7F2107778A0001D5E1 /* ShortcutTests.swift */; };
@ -346,6 +347,7 @@
9ACB313F1EDA994200F3E1D3 /* ShortcutRecorder.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ShortcutRecorder.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Clocker-ewkrwqfbimlgoicxkolbqepjsbcy/Build/Products/Release/ShortcutRecorder.framework"; sourceTree = "<group>"; }; 9ACB313F1EDA994200F3E1D3 /* ShortcutRecorder.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ShortcutRecorder.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Clocker-ewkrwqfbimlgoicxkolbqepjsbcy/Build/Products/Release/ShortcutRecorder.framework"; sourceTree = "<group>"; };
9ACE03EE1CB0ADE00039FC01 /* Firebase.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Firebase.framework; path = Frameworks/Firebase.framework; sourceTree = "<group>"; }; 9ACE03EE1CB0ADE00039FC01 /* Firebase.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Firebase.framework; path = Frameworks/Firebase.framework; sourceTree = "<group>"; };
9ACF469C1DCBD45200C49B51 /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = System/Library/Frameworks/Quartz.framework; sourceTree = SDKROOT; }; 9ACF469C1DCBD45200C49B51 /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = System/Library/Frameworks/Quartz.framework; sourceTree = SDKROOT; };
9ACF618C231DABAE00F5E51E /* SearchDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchDataSource.swift; sourceTree = "<group>"; };
9AFCC7FC1FD668FF00509B9C /* ClockerHelper.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ClockerHelper.entitlements; sourceTree = "<group>"; }; 9AFCC7FC1FD668FF00509B9C /* ClockerHelper.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ClockerHelper.entitlements; sourceTree = "<group>"; };
C20839C721515C1E00C86589 /* ClockerUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ClockerUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C20839C721515C1E00C86589 /* ClockerUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ClockerUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
C20839C921515C1E00C86589 /* ClockerUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClockerUnitTests.swift; sourceTree = "<group>"; }; C20839C921515C1E00C86589 /* ClockerUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClockerUnitTests.swift; sourceTree = "<group>"; };
@ -673,6 +675,7 @@
9AB6F1682259D26400A44663 /* Models */, 9AB6F1682259D26400A44663 /* Models */,
9AB6F1602259D1B000A44663 /* PreferencesDataSource.swift */, 9AB6F1602259D1B000A44663 /* PreferencesDataSource.swift */,
9AB6F15F2259D1B000A44663 /* PreferencesViewController.swift */, 9AB6F15F2259D1B000A44663 /* PreferencesViewController.swift */,
9ACF618C231DABAE00F5E51E /* SearchDataSource.swift */,
); );
path = General; path = General;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1182,6 +1185,7 @@
35C36F1A225961DA002FA5C6 /* Date+Manipulations.swift in Sources */, 35C36F1A225961DA002FA5C6 /* Date+Manipulations.swift in Sources */,
35C36F572259DD8A002FA5C6 /* TimezoneDataSource.swift in Sources */, 35C36F572259DD8A002FA5C6 /* TimezoneDataSource.swift in Sources */,
35C36F462259D892002FA5C6 /* DataStore.swift in Sources */, 35C36F462259D892002FA5C6 /* DataStore.swift in Sources */,
9ACF618D231DABAE00F5E51E /* SearchDataSource.swift in Sources */,
C2CCCD8220619C4C00F2DFC2 /* LocationController.swift in Sources */, C2CCCD8220619C4C00F2DFC2 /* LocationController.swift in Sources */,
35C36F4B2259D971002FA5C6 /* UnderlinedButton.swift in Sources */, 35C36F4B2259D971002FA5C6 /* UnderlinedButton.swift in Sources */,
9AB6F1562259CF3900A44663 /* CalendarViewController.swift in Sources */, 9AB6F1562259CF3900A44663 /* CalendarViewController.swift in Sources */,

218
Clocker/Preferences/General/PreferencesViewController.swift

@ -12,19 +12,6 @@ struct PreferencesConstants {
static let hotKeyPathIdentifier = "values.globalPing" static let hotKeyPathIdentifier = "values.globalPing"
} }
enum RowType {
case timezoneHeader
case cityHeader
case city
case timezone
}
struct TimezoneMetadata {
let timezone: NSTimeZone
let tags: Set<String>
let formattedName: String
}
class PreferencesViewController: ParentViewController { class PreferencesViewController: ParentViewController {
private var isActivityInProgress = false { private var isActivityInProgress = false {
didSet { didSet {
@ -40,11 +27,7 @@ class PreferencesViewController: ParentViewController {
return DataStore.shared().timezones() return DataStore.shared().timezones()
} }
private var timezoneMetadataDictionary: [String: [String]] = [:]
private lazy var startupManager = StartupManager() private lazy var startupManager = StartupManager()
private var filteredArray: [Any] = []
private var timezoneArray: [TimezoneMetadata] = []
private var timezoneFilteredArray: [TimezoneMetadata] = []
private var dataTask: URLSessionDataTask? = .none private var dataTask: URLSessionDataTask? = .none
private lazy var notimezoneView: NoTimezoneView? = { private lazy var notimezoneView: NoTimezoneView? = {
@ -87,7 +70,9 @@ class PreferencesViewController: ParentViewController {
@IBOutlet var sortToggle: NSButton! @IBOutlet var sortToggle: NSButton!
private var themeDidChangeNotification: NSObjectProtocol? private var themeDidChangeNotification: NSObjectProtocol?
private var selectionsDataSource: PreferencesDataSource! private var selectionsDataSource: PreferencesDataSource!
private var finalArray: [RowType] = []
// Search Results Data Source Handler
private var searchResultsDataSource: SearchDataSource!
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
@ -110,38 +95,14 @@ class PreferencesViewController: ParentViewController {
} }
searchField.placeholderString = "Enter city, state, country or timezone name" searchField.placeholderString = "Enter city, state, country or timezone name"
setupTimezoneDatasource()
selectionsDataSource = PreferencesDataSource(callbackDelegate: self) selectionsDataSource = PreferencesDataSource(callbackDelegate: self)
timezoneTableView.dataSource = selectionsDataSource timezoneTableView.dataSource = selectionsDataSource
timezoneTableView.delegate = selectionsDataSource timezoneTableView.delegate = selectionsDataSource
reloadSearchResults() searchResultsDataSource = SearchDataSource(with: searchField)
} availableTimezoneTableView.dataSource = searchResultsDataSource
availableTimezoneTableView.delegate = searchResultsDataSource
private func calculateArray() {
finalArray = []
func addTimezonesIfNeeded(_ data: [TimezoneMetadata]) {
if !data.isEmpty {
finalArray.append(.timezoneHeader)
}
data.forEach { _ in
finalArray.append(.timezone)
}
}
if searchField.stringValue.isEmpty {
addTimezonesIfNeeded(timezoneArray)
} else {
if !filteredArray.isEmpty {
finalArray.append(.cityHeader)
}
filteredArray.forEach { _ in
finalArray.append(.city)
}
addTimezonesIfNeeded(timezoneFilteredArray)
}
} }
deinit { deinit {
@ -353,75 +314,6 @@ class PreferencesViewController: ParentViewController {
} }
extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate { extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate {
func numberOfRows(in _: NSTableView) -> Int {
return numberOfSearchResults()
}
func tableView(_: NSTableView, isGroupRow row: Int) -> Bool {
let currentRowType = finalArray[row]
return
currentRowType == .timezoneHeader ||
currentRowType == .cityHeader
}
func tableView(_: NSTableView, shouldSelectRow row: Int) -> Bool {
let currentRowType = finalArray[row]
return !(currentRowType == .timezoneHeader || currentRowType == .cityHeader)
}
func tableView(_: NSTableView, selectionIndexesForProposedSelection proposedSelectionIndexes: IndexSet) -> IndexSet {
return proposedSelectionIndexes
}
func tableView(_ tableView: NSTableView, viewFor _: NSTableColumn?, row: Int) -> NSView? {
let currentRowType = finalArray[row]
switch currentRowType {
case .timezoneHeader, .cityHeader:
return headerCell(tableView, currentRowType)
case .timezone:
return timezoneCell(tableView, currentRowType, row)
case .city:
return cityCell(tableView, currentRowType, row)
}
}
private func timezoneCell(_ tableView: NSTableView, _: RowType, _ row: Int) -> NSView? {
if let message = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "resultCell"), owner: self) as? SearchResultTableViewCell {
let datasource = searchField.stringValue.isEmpty ? timezoneArray : timezoneFilteredArray
guard !datasource.isEmpty else {
return nil
}
let index = searchField.stringValue.isEmpty ? row - 1 : row
message.sourceName.stringValue = datasource[index % datasource.count].formattedName
return message
}
return nil
}
private func cityCell(_ tableView: NSTableView, _: RowType, _ row: Int) -> NSView? {
if let cityCell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "resultCell"), owner: self) as? SearchResultTableViewCell {
guard let timezoneData = filteredArray[row % filteredArray.count] as? TimezoneData else {
assertionFailure()
return nil
}
cityCell.sourceName.stringValue = timezoneData.formattedAddress ?? "Error"
return cityCell
}
return nil
}
private func headerCell(_ tableView: NSTableView, _ headerType: RowType) -> NSView? {
if let message = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "headerCell"), owner: self) as? HeaderTableViewCell {
message.headerField.stringValue = headerType == .timezoneHeader ? "Timezones" : "Places"
return message
}
return nil
}
private func _markAsFavorite(_ dataObject: TimezoneData) { private func _markAsFavorite(_ dataObject: TimezoneData) {
guard let menubarTitles = DataStore.shared().retrieve(key: CLMenubarFavorites) as? [Data] else { guard let menubarTitles = DataStore.shared().retrieve(key: CLMenubarFavorites) as? [Data] else {
return return
@ -555,6 +447,8 @@ extension PreferencesViewController {
self.placeholderLabel.placeholderString = "Searching for \(searchString)" self.placeholderLabel.placeholderString = "Searching for \(searchString)"
print(self.placeholderLabel.placeholderString ?? "")
self.dataTask = NetworkManager.task(with: self.generateSearchURL(), self.dataTask = NetworkManager.task(with: self.generateSearchURL(),
completionHandler: { [weak self] response, error in completionHandler: { [weak self] response, error in
@ -563,7 +457,7 @@ extension PreferencesViewController {
OperationQueue.main.addOperation { OperationQueue.main.addOperation {
if let errorPresent = error { if let errorPresent = error {
self.findLocalSearchResultsForTimezones() self.findLocalSearchResultsForTimezones()
if self.timezoneFilteredArray.isEmpty { if self.searchResultsDataSource.timezoneFilteredArray.isEmpty {
self.presentError(errorPresent.localizedDescription) self.presentError(errorPresent.localizedDescription)
return return
} }
@ -596,10 +490,10 @@ extension PreferencesViewController {
} }
private func findLocalSearchResultsForTimezones() { private func findLocalSearchResultsForTimezones() {
timezoneFilteredArray = [] searchResultsDataSource.timezoneFilteredArray = []
let lowercasedSearchString = searchField.stringValue.lowercased() let lowercasedSearchString = searchField.stringValue.lowercased()
timezoneFilteredArray = timezoneArray.filter { (timezoneMetadata) -> Bool in searchResultsDataSource.timezoneFilteredArray = searchResultsDataSource.timezoneArray.filter { (timezoneMetadata) -> Bool in
let tags = timezoneMetadata.tags let tags = timezoneMetadata.tags
for tag in tags where tag.contains(lowercasedSearchString) { for tag in tags where tag.contains(lowercasedSearchString) {
return true return true
@ -607,7 +501,7 @@ extension PreferencesViewController {
return false return false
} }
print(timezoneFilteredArray) print(searchResultsDataSource.timezoneFilteredArray)
} }
private func generateSearchURL() -> String { private func generateSearchURL() -> String {
@ -632,6 +526,7 @@ extension PreferencesViewController {
} }
private func appendResultsToFilteredArray(_ results: [SearchResult.Result]) { private func appendResultsToFilteredArray(_ results: [SearchResult.Result]) {
var finalResults: [TimezoneData] = []
results.forEach { results.forEach {
let location = $0.geometry.location let location = $0.geometry.location
let latitude = location.lat let latitude = location.lat
@ -647,8 +542,9 @@ extension PreferencesViewController {
CLPlaceIdentifier: $0.placeId, CLPlaceIdentifier: $0.placeId,
] as [String: Any] ] as [String: Any]
self.filteredArray.append(TimezoneData(with: totalPackage)) finalResults.append(TimezoneData(with: totalPackage))
} }
searchResultsDataSource.setFilteredArrayValue(finalResults)
} }
private func prepareUIForPresentingResults() { private func prepareUIForPresentingResults() {
@ -659,7 +555,7 @@ extension PreferencesViewController {
private func reloadSearchResults() { private func reloadSearchResults() {
print("Reloading Search Results") print("Reloading Search Results")
calculateArray() searchResultsDataSource.calculateArray()
availableTimezoneTableView.reloadData() availableTimezoneTableView.reloadData()
} }
@ -701,11 +597,6 @@ extension PreferencesViewController {
placeholderLabel.isHidden = false placeholderLabel.isHidden = false
} }
// if NetworkManager.isConnected() == false || ProcessInfo.processInfo.arguments.contains("mockTimezoneDown") {
// resetStateAndShowDisconnectedMessage()
// return
// }
searchField.placeholderString = "Fetching data might take some time!" searchField.placeholderString = "Fetching data might take some time!"
placeholderLabel.placeholderString = "Retrieving timezone data" placeholderLabel.placeholderString = "Retrieving timezone data"
availableTimezoneTableView.isHidden = true availableTimezoneTableView.isHidden = true
@ -745,7 +636,7 @@ extension PreferencesViewController {
} }
private func installTimezone(_ timezone: Timezone) { private func installTimezone(_ timezone: Timezone) {
guard let dataObject = self.filteredArray[self.availableTimezoneTableView.selectedRow % filteredArray.count] as? TimezoneData else { guard let dataObject = self.searchResultsDataSource.filteredArray[self.availableTimezoneTableView.selectedRow % searchResultsDataSource.filteredArray.count] as? TimezoneData else {
assertionFailure("Data was unexpectedly nil") assertionFailure("Data was unexpectedly nil")
return return
} }
@ -782,7 +673,7 @@ extension PreferencesViewController {
private func showMessage() { private func showMessage() {
placeholderLabel.placeholderString = PreferencesConstants.noInternetConnectivityError placeholderLabel.placeholderString = PreferencesConstants.noInternetConnectivityError
isActivityInProgress = false isActivityInProgress = false
filteredArray = [] searchResultsDataSource.cleanupFilterArray()
reloadSearchResults() reloadSearchResults()
} }
@ -807,7 +698,7 @@ extension PreferencesViewController {
} }
private func updateViewState() { private func updateViewState() {
filteredArray = [] searchResultsDataSource.cleanupFilterArray()
reloadSearchResults() reloadSearchResults()
refreshTimezoneTableView() refreshTimezoneTableView()
refreshMainTable() refreshMainTable()
@ -816,47 +707,10 @@ extension PreferencesViewController {
searchField.placeholderString = "Enter a city, state or country name" searchField.placeholderString = "Enter a city, state or country name"
availableTimezoneTableView.isHidden = false availableTimezoneTableView.isHidden = false
isActivityInProgress = false isActivityInProgress = false
timezoneMetadataDictionary =
["IST": ["india", "indian", "kolkata", "calcutta", "mumbai", "delhi", "hyderabad", "noida"],
"PST": ["los", "los angeles", "california", "san francisco", "bay area", "pacific standard time"],
"UTC": ["utc", "universal"],
"EST": ["florida", "new york"]]
}
private func setupTimezoneDatasource() {
timezoneArray = []
let anywhereOnEarth = TimezoneMetadata(timezone: NSTimeZone(abbreviation: "GMT-1200")!,
tags: ["aoe", "anywhere on earth"],
formattedName: "Anywhere on Earth")
timezoneArray.append(anywhereOnEarth)
for (abbreviation, timezone) in TimeZone.abbreviationDictionary {
var tags: Set<String> = [abbreviation.lowercased(), timezone.lowercased()]
var extraTags: [String] = []
if let tagsPresent = timezoneMetadataDictionary[abbreviation] {
extraTags = tagsPresent
}
extraTags.forEach { tag in
tags.insert(tag)
}
let timezoneIdentifier = NSTimeZone(name: timezone)!
let formattedName = timezone + " (\(abbreviation))"
let timezoneMetadata = TimezoneMetadata(timezone: timezoneIdentifier,
tags: tags,
formattedName: formattedName)
timezoneArray.append(timezoneMetadata)
}
print(TimeZone.knownTimeZoneIdentifiers.count)
print(timezoneArray.count)
} }
@IBAction func addTimeZone(_: NSButton) { @IBAction func addTimeZone(_: NSButton) {
filteredArray = [] searchResultsDataSource.cleanupFilterArray()
view.window?.beginSheet(timezonePanel, view.window?.beginSheet(timezonePanel,
completionHandler: nil) completionHandler: nil)
} }
@ -899,7 +753,7 @@ extension PreferencesViewController {
} }
private func addTimezoneIfSearchStringIsEmpty() { private func addTimezoneIfSearchStringIsEmpty() {
let currentRowType = finalArray[availableTimezoneTableView.selectedRow] let currentRowType = searchResultsDataSource.placeForRow(availableTimezoneTableView.selectedRow)
switch currentRowType { switch currentRowType {
case .timezoneHeader, .cityHeader: case .timezoneHeader, .cityHeader:
@ -913,7 +767,7 @@ extension PreferencesViewController {
} }
private func addTimezoneIfSearchStringIsNotEmpty() { private func addTimezoneIfSearchStringIsNotEmpty() {
let currentRowType = finalArray[availableTimezoneTableView.selectedRow] let currentRowType = searchResultsDataSource.placeForRow(availableTimezoneTableView.selectedRow)
switch currentRowType { switch currentRowType {
case .timezoneHeader, .cityHeader: case .timezoneHeader, .cityHeader:
@ -927,7 +781,7 @@ extension PreferencesViewController {
} }
private func cleanupAfterInstallingCity() { private func cleanupAfterInstallingCity() {
guard let dataObject = filteredArray[availableTimezoneTableView.selectedRow % filteredArray.count] as? TimezoneData else { guard let dataObject = searchResultsDataSource.filteredArray[availableTimezoneTableView.selectedRow % searchResultsDataSource.filteredArray.count] as? TimezoneData else {
assertionFailure("Data was unexpectedly nil") assertionFailure("Data was unexpectedly nil")
return return
} }
@ -949,14 +803,14 @@ extension PreferencesViewController {
data.setLabel(CLEmptyString) data.setLabel(CLEmptyString)
if searchField.stringValue.isEmpty == false { if searchField.stringValue.isEmpty == false {
let currentSelection = timezoneFilteredArray[availableTimezoneTableView.selectedRow % timezoneFilteredArray.count] let currentSelection = searchResultsDataSource.timezoneFilteredArray[availableTimezoneTableView.selectedRow % searchResultsDataSource.timezoneFilteredArray.count]
let metaInfo = metadata(for: currentSelection) let metaInfo = metadata(for: currentSelection)
data.timezoneID = metaInfo.0 data.timezoneID = metaInfo.0
data.formattedAddress = metaInfo.1.formattedName data.formattedAddress = metaInfo.1.formattedName
} else { } else {
let currentSelection = timezoneArray[availableTimezoneTableView.selectedRow - 1] let currentSelection = searchResultsDataSource.timezoneArray[availableTimezoneTableView.selectedRow - 1]
let metaInfo = metadata(for: currentSelection) let metaInfo = metadata(for: currentSelection)
data.timezoneID = metaInfo.0 data.timezoneID = metaInfo.0
@ -968,8 +822,8 @@ extension PreferencesViewController {
let operationObject = TimezoneDataOperations(with: data) let operationObject = TimezoneDataOperations(with: data)
operationObject.saveObject() operationObject.saveObject()
filteredArray = [] searchResultsDataSource.cleanupFilterArray()
timezoneFilteredArray = [] searchResultsDataSource.timezoneFilteredArray = []
placeholderLabel.placeholderString = CLEmptyString placeholderLabel.placeholderString = CLEmptyString
searchField.stringValue = CLEmptyString searchField.stringValue = CLEmptyString
@ -994,8 +848,8 @@ extension PreferencesViewController {
} }
@IBAction func closePanel(_: NSButton) { @IBAction func closePanel(_: NSButton) {
filteredArray = [] searchResultsDataSource.cleanupFilterArray()
timezoneFilteredArray = [] searchResultsDataSource.timezoneFilteredArray = []
searchField.stringValue = CLEmptyString searchField.stringValue = CLEmptyString
placeholderLabel.placeholderString = CLEmptyString placeholderLabel.placeholderString = CLEmptyString
searchField.placeholderString = "Enter a city, state or country name" searchField.placeholderString = "Enter a city, state or country name"
@ -1096,7 +950,7 @@ extension PreferencesViewController {
@IBAction func filterArray(_: Any?) { @IBAction func filterArray(_: Any?) {
messageLabel.stringValue = CLEmptyString messageLabel.stringValue = CLEmptyString
filteredArray = [] searchResultsDataSource.cleanupFilterArray()
if searchField.stringValue.count > 50 { if searchField.stringValue.count > 50 {
isActivityInProgress = false isActivityInProgress = false
@ -1220,10 +1074,6 @@ extension PreferencesViewController: SRRecorderControlDelegate {}
// Helpers // Helpers
extension PreferencesViewController { extension PreferencesViewController {
private func numberOfSearchResults() -> Int {
return finalArray.count
}
private func insert(timezone: TimezoneData, at index: Int) { private func insert(timezone: TimezoneData, at index: Int) {
let encodedObject = NSKeyedArchiver.archivedData(withRootObject: timezone) let encodedObject = NSKeyedArchiver.archivedData(withRootObject: timezone)
var newDefaults = selectedTimeZones var newDefaults = selectedTimeZones
@ -1232,14 +1082,6 @@ extension PreferencesViewController {
} }
} }
class SearchResultTableViewCell: NSTableCellView {
@IBOutlet var sourceName: NSTextField!
}
class HeaderTableViewCell: NSTableCellView {
@IBOutlet var headerField: NSTextField!
}
extension PreferencesViewController: PreferenceSelectionUpdates { extension PreferencesViewController: PreferenceSelectionUpdates {
func markAsFavorite(_ dataObject: TimezoneData) { func markAsFavorite(_ dataObject: TimezoneData) {
_markAsFavorite(dataObject) _markAsFavorite(dataObject)

185
Clocker/Preferences/General/SearchDataSource.swift

@ -0,0 +1,185 @@
// Copyright © 2015 Abhishek Banthia
import Cocoa
enum RowType {
case timezoneHeader
case cityHeader
case city
case timezone
}
struct TimezoneMetadata {
let timezone: NSTimeZone
let tags: Set<String>
let formattedName: String
}
class SearchDataSource: NSObject {
private var searchField: NSSearchField!
private var finalArray: [RowType] = []
private var dataTask: URLSessionDataTask? = .none
private var timezoneMetadataDictionary: [String: [String]] =
["IST": ["india", "indian", "kolkata", "calcutta", "mumbai", "delhi", "hyderabad", "noida"],
"PST": ["los", "los angeles", "california", "san francisco", "bay area", "pacific standard time"],
"UTC": ["utc", "universal"],
"EST": ["florida", "new york"]]
var filteredArray: [Any] = []
var timezoneArray: [TimezoneMetadata] = []
var timezoneFilteredArray: [TimezoneMetadata] = []
init(with searchField: NSSearchField) {
super.init()
self.searchField = searchField
setupTimezoneDatasource()
calculateArray()
}
func cleanupFilterArray() {
filteredArray = []
}
func setFilteredArrayValue(_ newArray: [Any]) {
filteredArray = newArray
}
func placeForRow(_ row: Int) -> RowType {
return finalArray[row]
}
private func setupTimezoneDatasource() {
timezoneArray = []
let anywhereOnEarth = TimezoneMetadata(timezone: NSTimeZone(abbreviation: "GMT-1200")!,
tags: ["aoe", "anywhere on earth"],
formattedName: "Anywhere on Earth")
timezoneArray.append(anywhereOnEarth)
for (abbreviation, timezone) in TimeZone.abbreviationDictionary {
var tags: Set<String> = [abbreviation.lowercased(), timezone.lowercased()]
var extraTags: [String] = []
if let tagsPresent = timezoneMetadataDictionary[abbreviation] {
extraTags = tagsPresent
}
extraTags.forEach { tag in
tags.insert(tag)
}
let timezoneIdentifier = NSTimeZone(name: timezone)!
let formattedName = timezone + " (\(abbreviation))"
let timezoneMetadata = TimezoneMetadata(timezone: timezoneIdentifier,
tags: tags,
formattedName: formattedName)
timezoneArray.append(timezoneMetadata)
}
print(TimeZone.knownTimeZoneIdentifiers.count)
print(timezoneArray.count)
}
func calculateArray() {
finalArray = []
func addTimezonesIfNeeded(_ data: [TimezoneMetadata]) {
if !data.isEmpty {
finalArray.append(.timezoneHeader)
}
data.forEach { _ in
finalArray.append(.timezone)
}
}
if searchField.stringValue.isEmpty {
addTimezonesIfNeeded(timezoneArray)
} else {
if !filteredArray.isEmpty {
finalArray.append(.cityHeader)
}
filteredArray.forEach { _ in
finalArray.append(.city)
}
addTimezonesIfNeeded(timezoneFilteredArray)
}
}
}
extension SearchDataSource: NSTableViewDataSource {
func numberOfRows(in _: NSTableView) -> Int {
return finalArray.count
}
func tableView(_ tableView: NSTableView, viewFor _: NSTableColumn?, row: Int) -> NSView? {
let currentRowType = finalArray[row]
switch currentRowType {
case .timezoneHeader, .cityHeader:
return headerCell(tableView, currentRowType)
case .timezone:
return timezoneCell(tableView, currentRowType, row)
case .city:
return cityCell(tableView, currentRowType, row)
}
}
}
extension SearchDataSource: NSTableViewDelegate {
func tableView(_: NSTableView, isGroupRow row: Int) -> Bool {
let currentRowType = finalArray[row]
return
currentRowType == .timezoneHeader ||
currentRowType == .cityHeader
}
func tableView(_: NSTableView, shouldSelectRow row: Int) -> Bool {
let currentRowType = finalArray[row]
return !(currentRowType == .timezoneHeader || currentRowType == .cityHeader)
}
}
extension SearchDataSource {
private func timezoneCell(_ tableView: NSTableView, _: RowType, _ row: Int) -> NSView? {
if let message = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "resultCell"), owner: self) as? SearchResultTableViewCell {
let datasource = searchField.stringValue.isEmpty ? timezoneArray : timezoneFilteredArray
guard !datasource.isEmpty else {
return nil
}
let index = searchField.stringValue.isEmpty ? row - 1 : row
message.sourceName.stringValue = datasource[index % datasource.count].formattedName
return message
}
return nil
}
private func cityCell(_ tableView: NSTableView, _: RowType, _ row: Int) -> NSView? {
if let cityCell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "resultCell"), owner: self) as? SearchResultTableViewCell {
guard let timezoneData = filteredArray[row % filteredArray.count] as? TimezoneData else {
assertionFailure()
return nil
}
cityCell.sourceName.stringValue = timezoneData.formattedAddress ?? "Error"
return cityCell
}
return nil
}
private func headerCell(_ tableView: NSTableView, _ headerType: RowType) -> NSView? {
if let message = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "headerCell"), owner: self) as? HeaderTableViewCell {
message.headerField.stringValue = headerType == .timezoneHeader ? "Timezones" : "Places"
return message
}
return nil
}
}
class SearchResultTableViewCell: NSTableCellView {
@IBOutlet var sourceName: NSTextField!
}
class HeaderTableViewCell: NSTableCellView {
@IBOutlet var headerField: NSTextField!
}
Loading…
Cancel
Save