Skip to content

Commit

Permalink
Add timezones to search results during Onboarding!
Browse files Browse the repository at this point in the history
  • Loading branch information
n0shake committed Feb 7, 2021
1 parent 27a609a commit 54a55c1
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 102 deletions.
169 changes: 107 additions & 62 deletions Clocker/Onboarding/OnboardingSearchController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class OnboardingSearchController: NSViewController {
@IBOutlet private var accessoryLabel: NSTextField!
@IBOutlet var undoButton: NSButton!

private var results: [TimezoneData] = []
private var searchResultsDataSource: SearchDataSource!
private var dataTask: URLSessionDataTask? = .none
private var themeDidChangeNotification: NSObjectProtocol?

Expand All @@ -31,6 +31,9 @@ class OnboardingSearchController: NSViewController {

view.wantsLayer = true

searchResultsDataSource = SearchDataSource(with: searchBar, location: .onboarding)

resultsTableView.isHidden = true
resultsTableView.delegate = self
resultsTableView.setAccessibility("ResultsTableView")
resultsTableView.dataSource = self
Expand Down Expand Up @@ -68,23 +71,68 @@ class OnboardingSearchController: NSViewController {
@objc func doubleClickAction(_: NSTableView?) {
[accessoryLabel].forEach { $0?.isHidden = false }

if resultsTableView.selectedRow >= 0, resultsTableView.selectedRow < results.count {
let selectedTimezone = results[resultsTableView.selectedRow]
if resultsTableView.selectedRow >= 0, resultsTableView.selectedRow < searchResultsDataSource.resultsCount() {
let selectedType = searchResultsDataSource.placeForRow(resultsTableView.selectedRow)
switch selectedType {
case .city:
let filteredGoogleResult = searchResultsDataSource.retrieveFilteredResultFromGoogleAPI(resultsTableView.selectedRow)
addTimezoneToDefaults(filteredGoogleResult!)
return
case .timezone:
cleanupAfterInstallingTimezone()
return
}
}
}

private func cleanupAfterInstallingTimezone() {
let data = TimezoneData()
data.setLabel(CLEmptyString)

let currentSelection = searchResultsDataSource.retrieveSelectedTimezone(resultsTableView.selectedRow)

let metaInfo = metadata(for: currentSelection)
data.timezoneID = metaInfo.0.name
data.formattedAddress = metaInfo.1.formattedName
data.selectionType = .timezone
data.isSystemTimezone = metaInfo.0.name == NSTimeZone.system.identifier

let operationObject = TimezoneDataOperations(with: data)
operationObject.saveObject()

searchResultsDataSource.cleanupFilterArray()
searchResultsDataSource.timezoneFilteredArray = []
searchResultsDataSource.calculateChangesets()
searchBar.stringValue = CLEmptyString

accessoryLabel.stringValue = "Added \(metaInfo.1.formattedName)."
undoButton.isHidden = false
setupLabelHidingTimer()

resultsTableView.reloadData()
resultsTableView.isHidden = true
}

addTimezoneToDefaults(selectedTimezone)
private func metadata(for selection: TimezoneMetadata) -> (NSTimeZone, TimezoneMetadata) {
if selection.formattedName == "Anywhere on Earth" {
return (NSTimeZone(name: "GMT-1200")!, selection)
} else if selection.formattedName == "UTC" {
return (NSTimeZone(name: "GMT")!, selection)
} else {
return (selection.timezone, selection)
}
}

private func addTimezoneToDefaults(_ timezone: TimezoneData) {
func setupLabelHidingTimer() {
Timer.scheduledTimer(withTimeInterval: 5,
repeats: false) { _ in
OperationQueue.main.addOperation {
self.accessoryLabel.stringValue = CLEmptyString
}
private func setupLabelHidingTimer() {
Timer.scheduledTimer(withTimeInterval: 5,
repeats: false) { _ in
OperationQueue.main.addOperation {
self.setInfoLabel(CLEmptyString)
}
}
}

private func addTimezoneToDefaults(_ timezone: TimezoneData) {
if resultsTableView.selectedRow == -1 {
setInfoLabel(PreferencesConstants.noTimezoneSelectedErrorMessage)
setupLabelHidingTimer()
Expand Down Expand Up @@ -131,22 +179,10 @@ class OnboardingSearchController: NSViewController {
return false
}

// Extracting this out for tests
private func decodeTimezone(from data: Data) -> Timezone? {
let jsonDecoder = JSONDecoder()
do {
let decodedObject = try jsonDecoder.decode(Timezone.self, from: data)
return decodedObject
} catch {
Logger.info("decodedObject error: \n\(error)")
return nil
}
}

private func fetchTimezone(for latitude: Double, and longitude: Double, _ dataObject: TimezoneData) {
if NetworkManager.isConnected() == false || ProcessInfo.processInfo.arguments.contains("mockTimezoneDown") {
setInfoLabel(PreferencesConstants.noInternetConnectivityError)
results = []
searchResultsDataSource.cleanupFilterArray()
resultsTableView.reloadData()
return
}
Expand All @@ -166,8 +202,8 @@ class OnboardingSearchController: NSViewController {
return
}

if error == nil, let json = response, let response = self.decodeTimezone(from: json) {
if self.resultsTableView.selectedRow >= 0, self.resultsTableView.selectedRow < self.results.count {
if error == nil, let json = response, let response = json.decodeTimezone() {
if self.resultsTableView.selectedRow >= 0, self.resultsTableView.selectedRow < self.searchResultsDataSource.resultsCount() {
var filteredAddress = "Error"

if let address = dataObject.formattedAddress {
Expand Down Expand Up @@ -250,11 +286,6 @@ class OnboardingSearchController: NSViewController {

accessoryLabel.isHidden = false

if NetworkManager.isConnected() == false {
setInfoLabel(PreferencesConstants.noInternetConnectivityError)
return
}

NSObject.cancelPreviousPerformRequests(withTarget: self)
perform(#selector(OnboardingSearchController.actualSearch), with: nil, afterDelay: 0.2)
}
Expand All @@ -268,6 +299,7 @@ class OnboardingSearchController: NSViewController {

@objc func actualSearch() {
func setupForError() {
searchResultsDataSource.calculateChangesets()
resultsTableView.isHidden = true
}

Expand Down Expand Up @@ -300,11 +332,18 @@ class OnboardingSearchController: NSViewController {
return
}

self.results = []
self.searchResultsDataSource.cleanupFilterArray()
self.searchResultsDataSource.timezoneFilteredArray = []

if let errorPresent = error {
self.presentErrorMessage(errorPresent.localizedDescription)
setupForError()
self.findLocalSearchResultsForTimezones()
if self.searchResultsDataSource.timezoneFilteredArray.count == 0 {
self.presentErrorMessage(errorPresent.localizedDescription)
setupForError()
return
}

self.prepareUIForPresentingResults()
return
}

Expand All @@ -314,7 +353,7 @@ class OnboardingSearchController: NSViewController {
return
}

let searchResults = self.decode(from: data)
let searchResults = data.decode()

if searchResults?.status == "ZERO_RESULTS" {
self.setInfoLabel("No results! 😔 Try entering the exact name.")
Expand All @@ -323,10 +362,8 @@ class OnboardingSearchController: NSViewController {
}

self.appendResultsToFilteredArray(searchResults!.results)

self.setInfoLabel(CLEmptyString)

self.resultsTableView.reloadData()
self.findLocalSearchResultsForTimezones()
self.prepareUIForPresentingResults()
}
})
}
Expand All @@ -339,40 +376,43 @@ class OnboardingSearchController: NSViewController {
}
}

private func findLocalSearchResultsForTimezones() {
let lowercasedSearchString = searchBar.stringValue.lowercased()
searchResultsDataSource.searchTimezones(lowercasedSearchString)
}

private func prepareUIForPresentingResults() {
setInfoLabel(CLEmptyString)
if searchResultsDataSource.calculateChangesets() {
resultsTableView.isHidden = false
resultsTableView.reloadData()
}
}

private func appendResultsToFilteredArray(_ results: [SearchResult.Result]) {
results.forEach {
let location = $0.geometry.location
let finalTimezones: [TimezoneData] = results.map { (result) -> TimezoneData in
let location = result.geometry.location
let latitude = location.lat
let longitude = location.lng
let formattedAddress = $0.formattedAddress
let formattedAddress = result.formattedAddress

let totalPackage = [
"latitude": latitude,
"longitude": longitude,
CLTimezoneName: formattedAddress,
CLCustomLabel: formattedAddress,
CLTimezoneID: CLEmptyString,
CLPlaceIdentifier: $0.placeId,
CLPlaceIdentifier: result.placeId,
] as [String: Any]

self.results.append(TimezoneData(with: totalPackage))
return TimezoneData(with: totalPackage)
}
}

// Extracting this out for tests
private func decode(from data: Data) -> SearchResult? {
let jsonDecoder = JSONDecoder()
do {
let decodedObject = try jsonDecoder.decode(SearchResult.self, from: data)
return decodedObject
} catch {
Logger.info("decodedObject error: \n\(error)")
return nil
}
searchResultsDataSource.setFilteredArrayValue(finalTimezones)
}

private func resetSearchView() {
results = []
searchResultsDataSource.cleanupFilterArray()
resultsTableView.reloadData()
searchBar.stringValue = CLEmptyString
searchBar.placeholderString = "Press Enter to Search"
Expand All @@ -386,13 +426,18 @@ class OnboardingSearchController: NSViewController {

extension OnboardingSearchController: NSTableViewDataSource {
func numberOfRows(in _: NSTableView) -> Int {
return results.count
return searchResultsDataSource.resultsCount()
}

func tableView(_ tableView: NSTableView, viewFor _: NSTableColumn?, row: Int) -> NSView? {
if let result = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "resultCellView"), owner: self) as? ResultTableViewCell, row >= 0, row < results.count {
let currentTimezone = results[row]
result.result.stringValue = " \(currentTimezone.formattedAddress ?? "Place Name")"
if let result = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "resultCellView"), owner: self) as? ResultTableViewCell, row >= 0, row < searchResultsDataSource.resultsCount() {
let currentSelection = searchResultsDataSource.retrieveResult(row)
if let timezone = currentSelection as? TimezoneMetadata {
result.result.stringValue = " \(timezone.formattedName)"
} else if let location = currentSelection as? TimezoneData {
result.result.stringValue = " \(location.formattedAddress ?? "Place Name")"
}

result.result.textColor = Themer.shared().mainTextColor()
return result
}
Expand All @@ -403,15 +448,15 @@ extension OnboardingSearchController: NSTableViewDataSource {

extension OnboardingSearchController: NSTableViewDelegate {
func tableView(_: NSTableView, heightOfRow row: Int) -> CGFloat {
if row == 0, results.isEmpty {
if row == 0, searchResultsDataSource.resultsCount() == 0 {
return 30
}

return 36
}

func tableView(_: NSTableView, shouldSelectRow row: Int) -> Bool {
return results.isEmpty ? row != 0 : true
return searchResultsDataSource.resultsCount() == 0 ? row != 0 : true
}

func tableView(_: NSTableView, rowViewForRow _: Int) -> NSTableRowView? {
Expand Down
37 changes: 5 additions & 32 deletions Clocker/Preferences/General/PreferencesViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ extension PreferencesViewController {
return
}

let searchResults = self.decode(from: data)
let searchResults = data.decode()

if searchResults?.status == "ZERO_RESULTS" {
self.findLocalSearchResultsForTimezones()
Expand All @@ -460,8 +460,6 @@ extension PreferencesViewController {
private func findLocalSearchResultsForTimezones() {
let lowercasedSearchString = searchField.stringValue.lowercased()
searchResultsDataSource.searchTimezones(lowercasedSearchString)

Logger.info(searchResultsDataSource.timezoneFilteredArray.debugDescription)
}

private func generateSearchURL() -> String {
Expand Down Expand Up @@ -520,30 +518,6 @@ extension PreferencesViewController {
}
}

// Extracting this out for tests
private func decode(from data: Data) -> SearchResult? {
let jsonDecoder = JSONDecoder()
do {
let decodedObject = try jsonDecoder.decode(SearchResult.self, from: data)
return decodedObject
} catch {
Logger.info("decodedObject error: \n\(error)")
return nil
}
}

// Extracting this out for tests
private func decodeTimezone(from data: Data) -> Timezone? {
let jsonDecoder = JSONDecoder()
do {
let decodedObject = try jsonDecoder.decode(Timezone.self, from: data)
return decodedObject
} catch {
Logger.info("decodedObject error: \n\(error)")
return nil
}
}

private func resetSearchView() {
if dataTask?.state == .running {
dataTask?.cancel()
Expand Down Expand Up @@ -576,7 +550,7 @@ extension PreferencesViewController {
return
}

if error == nil, let json = response, let timezone = strongSelf.decodeTimezone(from: json) {
if error == nil, let json = response, let timezone = json.decodeTimezone() {
if strongSelf.availableTimezoneTableView.selectedRow >= 0 {
strongSelf.installTimezone(timezone)
}
Expand All @@ -597,7 +571,7 @@ extension PreferencesViewController {
}

private func installTimezone(_ timezone: Timezone) {
guard let dataObject = searchResultsDataSource.retrieveFilteredResult(availableTimezoneTableView.selectedRow) else {
guard let dataObject = searchResultsDataSource.retrieveFilteredResultFromGoogleAPI(availableTimezoneTableView.selectedRow) else {
assertionFailure("Data was unexpectedly nil")
return
}
Expand Down Expand Up @@ -742,7 +716,7 @@ extension PreferencesViewController {
}

private func cleanupAfterInstallingCity() {
guard let dataObject = searchResultsDataSource.retrieveFilteredResult(availableTimezoneTableView.selectedRow) else {
guard let dataObject = searchResultsDataSource.retrieveFilteredResultFromGoogleAPI(availableTimezoneTableView.selectedRow) else {
assertionFailure("Data was unexpectedly nil")
return
}
Expand All @@ -763,8 +737,7 @@ extension PreferencesViewController {
let data = TimezoneData()
data.setLabel(CLEmptyString)

let currentSelection = searchResultsDataSource.retrieveSelectedTimezone(searchField.stringValue,
availableTimezoneTableView.selectedRow)
let currentSelection = searchResultsDataSource.retrieveSelectedTimezone(availableTimezoneTableView.selectedRow)

let metaInfo = metadata(for: currentSelection)
data.timezoneID = metaInfo.0.name
Expand Down
Loading

0 comments on commit 54a55c1

Please sign in to comment.