439 lines
16 KiB
Swift
439 lines
16 KiB
Swift
//
|
|
// SeismicNetworksViewController.swift
|
|
// Earthquake Network
|
|
//
|
|
// Created by Busi Andrea on 22/09/2020.
|
|
// Copyright © 2020 Earthquake Network. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import EventKitUI
|
|
import DZNEmptyDataSet
|
|
|
|
class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
|
|
|
|
private enum CellType {
|
|
case seismic(EQNSisma)
|
|
case advertise(GADNativeAd)
|
|
}
|
|
|
|
private static let SegueIdentifierFilters = "ShowFilters"
|
|
private static let SegueIdentifierSettings = "ShowSettings"
|
|
private static let SegueIdentifierSeismicNetworks = "ShowSeismicNetworks"
|
|
private static let SegueIdentifierCardSettings = "ShowCardSettings"
|
|
|
|
// MARK: - Internal
|
|
|
|
@IBOutlet private weak var tableView: UITableView?
|
|
@IBOutlet private weak var expandeCollapseButton: UIBarButtonItem!
|
|
weak var currentMapController: SeismicNetworksMapDetailViewController?
|
|
|
|
/// The ad loader
|
|
private lazy var adLoader: GADAdLoader = {
|
|
let adLoader = GADAdLoader(
|
|
adUnitID: EQNAdMobAppIdNativeBanner, rootViewController: self,
|
|
adTypes: [.native], options: nil)
|
|
adLoader.delegate = self
|
|
return adLoader
|
|
}()
|
|
|
|
/// Cells to display (must be seismics or ad banners)
|
|
private var rows = [CellType]()
|
|
/// Informations to display on a single cell
|
|
private var informations = [SeismicNetworkTableViewCell.InformationType]()
|
|
/// Index path of row with map expanded
|
|
private var openMapIndexPath: IndexPath?
|
|
/// Index path of row with weather expanded
|
|
private var openWeatherIndexPath: IndexPath?
|
|
|
|
// MARK: - View Lifecycle
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
setupUI()
|
|
refreshUI()
|
|
|
|
// only the first time, show the popup for country selection
|
|
let alreadyPresented = UserDefaults.standard.bool(forKey: EQNUserDefaultKeyOneShotShowCountry)
|
|
if !alreadyPresented {
|
|
performSegue(withIdentifier: Self.SegueIdentifierSettings, sender: nil)
|
|
UserDefaults.standard.setValue(true, forKey: EQNUserDefaultKeyOneShotShowCountry)
|
|
}
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(didReceiveDownloadCompleteNotification(_:)), name: .EQNDownloadDataDidComplete, object: nil)
|
|
}
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
super.viewWillAppear(animated)
|
|
|
|
loadData(forced: false)
|
|
}
|
|
|
|
private func setupUI() {
|
|
title = NSLocalizedString("tab_official", comment: "").capitalized
|
|
|
|
tableView?.estimatedRowHeight = 300.0
|
|
tableView?.rowHeight = UITableView.automaticDimension
|
|
tableView?.register(SeismicNetworkTableViewCell.self, forCellReuseIdentifier: SeismicNetworkTableViewCell.Identifier)
|
|
tableView?.register(SeismicNetworkAdvertiseTableViewCell.self, forCellReuseIdentifier: SeismicNetworkAdvertiseTableViewCell.Identifier)
|
|
tableView?.emptyDataSetSource = self
|
|
}
|
|
|
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
|
switch segue.identifier {
|
|
case Self.SegueIdentifierFilters:
|
|
if let controller = segue.destination as? SeismicFiltersViewController {
|
|
controller.delegate = self
|
|
}
|
|
case Self.SegueIdentifierSettings:
|
|
if let controller = segue.destination as? SeismicSettingsViewController {
|
|
controller.delegate = self
|
|
}
|
|
case Self.SegueIdentifierSeismicNetworks:
|
|
if let navController = segue.destination as? UINavigationController, let controller = navController.viewControllers.first as? SeismicSettingsNetworksViewController {
|
|
controller.delegate = self
|
|
}
|
|
case Self.SegueIdentifierCardSettings:
|
|
if let controller = segue.destination as? SeismicCardSettingsViewController {
|
|
controller.delegate = self
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
private func showMapDetail(for seismic: EQNSisma) {
|
|
let seismics = getSeismics()
|
|
let controller = SeismicNetworksMapDetailViewController(seismic: seismic, allSeismics: seismics)
|
|
controller.delegate = self
|
|
present(controller, animated: true, completion: nil)
|
|
|
|
self.currentMapController = controller
|
|
}
|
|
|
|
// MARK: - Notifications
|
|
|
|
@objc func didReceiveDownloadCompleteNotification(_ sender: Notification) {
|
|
self.openMapIndexPath = nil
|
|
self.openWeatherIndexPath = nil
|
|
|
|
DispatchQueue.main.async {
|
|
self.refreshUI()
|
|
}
|
|
}
|
|
|
|
// MARK: - Private
|
|
|
|
private func refreshUI() {
|
|
elaborateData()
|
|
|
|
if let saved = UserDefaults.standard.array(forKey: EQNUserDefaultKeySesmicInformations) as? [Int] {
|
|
informations = saved.compactMap { SeismicNetworkTableViewCell.InformationType(rawValue: $0) }
|
|
}
|
|
|
|
if informations.contains(.buttons) {
|
|
expandeCollapseButton.image = UIImage(named: "navbar-icon-arrow-collapse")
|
|
} else {
|
|
expandeCollapseButton.image = UIImage(named: "navbar-icon-arrow-expand")
|
|
}
|
|
|
|
tableView?.reloadData()
|
|
}
|
|
|
|
private func loadAd() {
|
|
adLoader.load(GADRequest())
|
|
}
|
|
|
|
private func loadData(forced: Bool) {
|
|
EQNManager.manager().refreshSeismicData(forced: forced)
|
|
}
|
|
|
|
private func elaborateData() {
|
|
// show filtered seismic based on user settings
|
|
let allSeismics = EQNManager.manager().retiSismiche
|
|
let filteredSeismics = EQNSeismic.shared.filterSeismicList(allSeismics ?? [])
|
|
rows = filteredSeismics.map { .seismic($0) }
|
|
|
|
#if ADS_ENABLED
|
|
// if is not a pro user, show an advertise
|
|
if !EQNPurchaseUtility.isProVersionEnabled() {
|
|
loadAd()
|
|
}
|
|
#endif
|
|
|
|
// if a map detail is presented, update its data
|
|
if let mapController = currentMapController {
|
|
mapController.updateSeismics(filteredSeismics)
|
|
}
|
|
}
|
|
|
|
private func getSeismics() -> [EQNSisma] {
|
|
let seismics = rows.compactMap { (row) -> EQNSisma? in
|
|
if case .seismic(let seismic) = row {
|
|
return seismic
|
|
}
|
|
return nil
|
|
}
|
|
return seismics
|
|
}
|
|
|
|
// MARK: - Actions
|
|
|
|
@IBAction func refreshDataTapped(_ sender: Any) {
|
|
loadData(forced: true)
|
|
}
|
|
|
|
@IBAction func openFilterTapped(_ sender: Any) {
|
|
performSegue(withIdentifier: Self.SegueIdentifierFilters, sender: nil)
|
|
}
|
|
|
|
@IBAction func openSettingsTapped(_ sender: Any) {
|
|
performSegue(withIdentifier: Self.SegueIdentifierSettings, sender: nil)
|
|
}
|
|
|
|
@IBAction func collapseExpandTapped(_ sender: Any) {
|
|
if informations.contains(.buttons) {
|
|
informations.removeAll(where: { $0 == .buttons })
|
|
} else {
|
|
informations.append(.buttons)
|
|
}
|
|
|
|
UserDefaults.standard.set(informations.map { $0.rawValue }, forKey: EQNUserDefaultKeySesmicInformations)
|
|
refreshUI()
|
|
}
|
|
|
|
// MARK: - Table view delegate and data source
|
|
|
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
rows.count
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
let row = rows[indexPath.row]
|
|
switch row {
|
|
case .seismic(let seismic):
|
|
let cell = tableView.dequeueReusableCell(withIdentifier: SeismicNetworkTableViewCell.Identifier, for: indexPath) as! SeismicNetworkTableViewCell
|
|
|
|
var type = SeismicNetworkTableViewCell.DisplayType.normal
|
|
if openMapIndexPath == indexPath {
|
|
type = .mapExpanded
|
|
} else if openWeatherIndexPath == indexPath {
|
|
type = .weatherExpanded
|
|
}
|
|
|
|
cell.configure(with: seismic, type: type, informations: informations)
|
|
cell.delegate = self
|
|
return cell
|
|
case .advertise(let nativeAd):
|
|
let cell = tableView.dequeueReusableCell(withIdentifier: SeismicNetworkAdvertiseTableViewCell.Identifier, for: indexPath) as! SeismicNetworkAdvertiseTableViewCell
|
|
cell.loadNativeAd(nativeAd)
|
|
return cell
|
|
}
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
let row = rows[indexPath.row]
|
|
|
|
if case .seismic(let seismic) = row {
|
|
tableView.deselectRow(at: indexPath, animated: true)
|
|
showMapDetail(for: seismic)
|
|
}
|
|
}
|
|
|
|
// MARK: - Private
|
|
|
|
private func openCalendar(for seismic: EQNSisma) {
|
|
checkCalendarPermission {
|
|
self.createCalendarEvent(for: seismic)
|
|
} failure: {
|
|
let alert = UIAlertController(title: NSLocalizedString("attention", comment: ""),
|
|
message: NSLocalizedString("calendar_missing_permission", comment: ""),
|
|
preferredStyle: .alert)
|
|
alert.addAction(UIAlertAction(title: NSLocalizedString("ok", comment: ""), style: .default))
|
|
self.present(alert, animated: true)
|
|
}
|
|
}
|
|
|
|
private func checkCalendarPermission(success: @escaping () -> Void, failure: @escaping () -> Void) {
|
|
let authorization = EKEventStore.authorizationStatus(for: .event)
|
|
switch authorization {
|
|
case .notDetermined:
|
|
let eventStore = EKEventStore()
|
|
eventStore.requestAccess(to: .event) { (granted, error) in
|
|
DispatchQueue.main.async {
|
|
if granted {
|
|
success()
|
|
} else {
|
|
failure()
|
|
}
|
|
}
|
|
}
|
|
case .authorized:
|
|
success()
|
|
default:
|
|
failure()
|
|
}
|
|
}
|
|
|
|
private func createCalendarEvent(for seismic: EQNSisma) {
|
|
let eventStore = EKEventStore()
|
|
|
|
// calendar event
|
|
let event = EKEvent(eventStore: eventStore)
|
|
event.title = seismic.place
|
|
event.startDate = seismic.date
|
|
event.endDate = seismic.date
|
|
event.notes = String(format: NSLocalizedString("calendar_description_nogeo_km", comment: ""), seismic.magnitude, seismic.depth)
|
|
|
|
// controller to present
|
|
let eventVC = EKEventEditViewController()
|
|
eventVC.editViewDelegate = self
|
|
eventVC.eventStore = eventStore
|
|
eventVC.event = event
|
|
|
|
present(eventVC, animated: true)
|
|
}
|
|
}
|
|
|
|
extension SeismicNetworksViewController: GADNativeAdLoaderDelegate {
|
|
func adLoader(_ adLoader: GADAdLoader, didReceive nativeAd: GADNativeAd) {
|
|
print("[GADAdLoader] didReceive")
|
|
|
|
let adPosition = min(3, rows.count)
|
|
rows.insert(.advertise(nativeAd), at: adPosition)
|
|
tableView?.reloadData()
|
|
}
|
|
|
|
func adLoader(_ adLoader: GADAdLoader, didFailToReceiveAdWithError error: Error) {
|
|
// nope
|
|
print("[GADAdLoader] didFailToReceiveAdWithError: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
|
|
extension SeismicNetworksViewController: SeismicNetworkTableViewCellDelegate {
|
|
|
|
func seismicNetworkCellDidTapShare(_ cell: SeismicNetworkTableViewCell) {
|
|
guard let index = tableView?.indexPath(for: cell), case let .seismic(seismic) = rows[index.row] else { return }
|
|
|
|
// create a snapshot of the cell and share with default share sheet
|
|
let snapshot = cell.contentView.createSnapshot()
|
|
|
|
// text to share with the snapshot
|
|
let shareHashtag = NSLocalizedString("share_hashtag", comment: "")
|
|
let magnitude = String(format: "%.1f", seismic.magnitude.doubleValue)
|
|
let location = seismic.place
|
|
let notified = NSLocalizedString("share_notified", comment: "")
|
|
let shareMessage = "\(shareHashtag) M\(magnitude), \(location). \(notified)"
|
|
|
|
let controller = UIActivityViewController(activityItems: [snapshot, shareMessage], applicationActivities: [])
|
|
present(controller, animated: true)
|
|
}
|
|
|
|
func seismicNetworkCellDidTapWeather(_ cell: SeismicNetworkTableViewCell, hasValidWeatherData: Bool) {
|
|
guard let index = tableView?.indexPath(for: cell) else { return }
|
|
|
|
if !hasValidWeatherData {
|
|
let alert = UIAlertController(title: NSLocalizedString("attention", comment: ""),
|
|
message: NSLocalizedString("weather_nodata", comment: ""),
|
|
preferredStyle: .alert)
|
|
alert.addAction(UIAlertAction(title: NSLocalizedString("ok", comment: ""), style: .default))
|
|
present(alert, animated: true)
|
|
return
|
|
}
|
|
|
|
let indexToReloads = [openMapIndexPath, openWeatherIndexPath, index].compactMap { $0 }
|
|
|
|
openWeatherIndexPath = index
|
|
openMapIndexPath = nil
|
|
tableView?.reloadRows(at: indexToReloads, with: .automatic)
|
|
}
|
|
|
|
func seismicNetworkCellDidTapMap(_ cell: SeismicNetworkTableViewCell) {
|
|
guard let index = tableView?.indexPath(for: cell) else { return }
|
|
|
|
let indexToReloads = [openMapIndexPath, openWeatherIndexPath, index].compactMap { $0 }
|
|
|
|
openMapIndexPath = index
|
|
openWeatherIndexPath = nil
|
|
tableView?.reloadRows(at: indexToReloads, with: .automatic)
|
|
}
|
|
|
|
func seismicNetworkCellDidTapMapDetail(_ cell: SeismicNetworkTableViewCell) {
|
|
guard let index = tableView?.indexPath(for: cell), case let .seismic(seismic) = rows[index.row] else { return }
|
|
|
|
showMapDetail(for: seismic)
|
|
}
|
|
|
|
func seismicNetworkCellDidTapCalendar(_ cell: SeismicNetworkTableViewCell) {
|
|
guard let index = tableView?.indexPath(for: cell), case let .seismic(seismic) = rows[index.row] else { return }
|
|
|
|
openCalendar(for: seismic)
|
|
}
|
|
|
|
func seismicNetworkCellDidTapSettings(_ cell: SeismicNetworkTableViewCell) {
|
|
performSegue(withIdentifier: Self.SegueIdentifierCardSettings, sender: nil)
|
|
}
|
|
|
|
func seismicNetworkCellDidTapClose(_ cell: SeismicNetworkTableViewCell) {
|
|
guard let index = tableView?.indexPath(for: cell) else { return }
|
|
|
|
let indexToReloads = [openMapIndexPath, openWeatherIndexPath, index].compactMap { $0 }
|
|
|
|
openMapIndexPath = nil
|
|
openWeatherIndexPath = nil
|
|
tableView?.reloadRows(at: indexToReloads, with: .automatic)
|
|
}
|
|
}
|
|
|
|
|
|
extension SeismicNetworksViewController: EKEventEditViewDelegate {
|
|
func eventEditViewController(_ controller: EKEventEditViewController, didCompleteWith action: EKEventEditViewAction) {
|
|
dismiss(animated: true, completion: nil)
|
|
}
|
|
}
|
|
|
|
extension SeismicNetworksViewController: SeismicFiltersViewControllerDelegate {
|
|
func seismicFiltersControllerDidUpdateFilters(_ controller: SeismicFiltersViewController) {
|
|
loadData(forced: controller.needsDataUpdate)
|
|
refreshUI()
|
|
}
|
|
}
|
|
|
|
extension SeismicNetworksViewController: SeismicSettingsViewControllerDelegate {
|
|
func seismicSettingsControllerDidComplete(_ controller: SeismicSettingsViewController) {
|
|
refreshUI()
|
|
}
|
|
|
|
func seismicSettingsControllerWillOpenProviders(_ controller: SeismicSettingsViewController) {
|
|
performSegue(withIdentifier: Self.SegueIdentifierSeismicNetworks, sender: nil)
|
|
}
|
|
}
|
|
|
|
extension SeismicNetworksViewController: SeismicSettingsNetworksViewControllerDelegate {
|
|
func seismicSettingsNetworksControllerDidComplete(_ controller: SeismicSettingsNetworksViewController) {
|
|
refreshUI()
|
|
}
|
|
}
|
|
|
|
extension SeismicNetworksViewController: SeismicCardSettingsViewControllerDelegate {
|
|
func seismicCardSettingsDidComplete(_ controller: SeismicCardSettingsViewController) {
|
|
refreshUI()
|
|
}
|
|
}
|
|
|
|
extension SeismicNetworksViewController: DZNEmptyDataSetSource {
|
|
func title(forEmptyDataSet scrollView: UIScrollView!) -> NSAttributedString! {
|
|
let attributes = [ NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .body) ]
|
|
let string = NSAttributedString(string: NSLocalizedString("filter_empty", comment: ""), attributes: attributes)
|
|
return string
|
|
}
|
|
}
|
|
|
|
extension SeismicNetworksViewController: SeismicNetworksMapDetailViewControllerDelegate {
|
|
func seismicNetworksMapDetailControllerWillUpdateData(_ controller: SeismicNetworksMapDetailViewController, needsDataUpdate: Bool) {
|
|
loadData(forced: needsDataUpdate)
|
|
refreshUI()
|
|
}
|
|
}
|