mirror of
https://github.com/luxfi/safe-ios.git
synced 2026-10-30 15:19:20 +00:00
236 lines
8.1 KiB
Swift
236 lines
8.1 KiB
Swift
//
|
|
// GuardianListViewController.swift
|
|
// Multisig
|
|
//
|
|
// Created by Moaaz on 6/27/22.
|
|
// Copyright © 2022 Gnosis Ltd. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
|
|
|
|
class GuardianListViewController: LoadableViewController {
|
|
private var currentDataTask: URLSessionTask?
|
|
private enum Section {
|
|
case guardiansCount(count: Int)
|
|
case guardians(items: [Guardian])
|
|
}
|
|
|
|
private var sections: [Section] = []
|
|
|
|
private var guardians: [Guardian] = []
|
|
|
|
var onSelected: ((Guardian) -> ())?
|
|
var onReloaded: (() -> ())?
|
|
|
|
private var searchController: UISearchController!
|
|
private var resultsController: GuardianSearchResultController!
|
|
|
|
var controller: ClaimingAppController!
|
|
var safe: Safe!
|
|
var selectedDelegate: Address?
|
|
|
|
convenience init() {
|
|
self.init(namedClass: Self.superclass())
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
ViewControllerFactory.removeNavigationBarBorder(self)
|
|
title = "Choose a delegate"
|
|
|
|
resultsController = GuardianSearchResultController()
|
|
|
|
resultsController.tableView.delegate = self
|
|
|
|
searchController = UISearchController(searchResultsController: resultsController)
|
|
searchController.delegate = self
|
|
searchController.searchResultsUpdater = self
|
|
searchController.searchBar.autocapitalizationType = .none
|
|
searchController.searchBar.delegate = self
|
|
searchController.searchBar.placeholder = "Name, address or ENS"
|
|
searchController.hidesNavigationBarDuringPresentation = false
|
|
|
|
navigationItem.searchController = searchController
|
|
navigationItem.hidesSearchBarWhenScrolling = false
|
|
definesPresentationContext = true
|
|
|
|
tableView.registerCell(GuardianTableViewCell.self)
|
|
tableView.registerCell(GuardianCountTableViewCell.self)
|
|
tableView.delegate = self
|
|
tableView.dataSource = self
|
|
tableView.rowHeight = UITableView.automaticDimension
|
|
tableView.estimatedRowHeight = 200
|
|
tableView.separatorStyle = .none
|
|
|
|
// Empty view is never shown. Instead the list is empty, when no results are found
|
|
emptyView.setTitle("No delegates were found. Try to search again or use a custom address.")
|
|
emptyView.setImage(UIImage(named: "ico-delegate-placeholder")!)
|
|
extendedLayoutIncludesOpaqueBars = true
|
|
}
|
|
|
|
private func makeSections(items: [Guardian]) -> [Section] {
|
|
guard !items.isEmpty else {
|
|
return []
|
|
}
|
|
|
|
var sections = [Section]()
|
|
|
|
sections.append(.guardiansCount(count: items.count))
|
|
sections.append(.guardians(items: items))
|
|
|
|
return sections
|
|
}
|
|
|
|
override var isEmpty: Bool { sections.isEmpty }
|
|
|
|
override func reloadData() {
|
|
super.reloadData()
|
|
|
|
controller.guardians(for: safe.addressValue) { [weak self] result in
|
|
guard let `self` = self else { return }
|
|
do {
|
|
let data = try result.get()
|
|
self.selectedDelegate = data.delegate
|
|
|
|
if let selectedIndex = data.guardians.firstIndex(where: { $0.address.address == data.delegate }) {
|
|
var results = data.guardians
|
|
let selected = results.remove(at: selectedIndex)
|
|
results = results.shuffled()
|
|
results.insert(selected, at: 0)
|
|
self.guardians = results
|
|
} else {
|
|
self.guardians = data.guardians.shuffled()
|
|
}
|
|
|
|
self.sections = self.makeSections(items: self.guardians)
|
|
self.onSuccess()
|
|
} catch {
|
|
if (error as NSError).code == URLError.cancelled.rawValue &&
|
|
(error as NSError).domain == NSURLErrorDomain {
|
|
return
|
|
}
|
|
|
|
self.onError(GSError.error(description: "Failed to load guardians", error: error))
|
|
self.onReloaded?()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension GuardianListViewController: UISearchResultsUpdating, UISearchBarDelegate, UISearchControllerDelegate {
|
|
|
|
func updateSearchResults(for searchController: UISearchController) {
|
|
guard let resultsController = searchController.searchResultsController as? GuardianSearchResultController else {
|
|
return
|
|
}
|
|
if !searchController.searchBar.text!.isEmpty {
|
|
Tracker.trackEvent(.userClaimChdelSearch)
|
|
}
|
|
let terms = searchController.searchBar.text!
|
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
.components(separatedBy: " ") as [String]
|
|
|
|
resultsController.filteredGuardians = guardians.filter { guardian in
|
|
// all terms AND with each other
|
|
let allMatch = terms.allSatisfy { term in
|
|
// - each tearm is OR of 'contains' name, address, ens
|
|
// - using comparison that is case-insensitive, and diacritic-insensitive
|
|
guardian.name?.localizedStandardContains(term) == true ||
|
|
guardian.address.description.localizedStandardContains(term) ||
|
|
guardian.ens?.localizedStandardContains(term) == true
|
|
}
|
|
|
|
return allMatch
|
|
}
|
|
resultsController.selectedDelegate = selectedDelegate
|
|
resultsController.tableView.reloadData()
|
|
if !searchController.searchBar.text!.isEmpty && resultsController.filteredGuardians.isEmpty {
|
|
Tracker.trackEvent(.screenClaimChdelNf)
|
|
}
|
|
}
|
|
|
|
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
|
|
searchBar.resignFirstResponder()
|
|
}
|
|
}
|
|
|
|
extension GuardianListViewController: UITableViewDelegate, UITableViewDataSource {
|
|
|
|
func numberOfSections(in tableView: UITableView) -> Int {
|
|
sections.count
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
switch sections[section] {
|
|
case .guardiansCount: return 1
|
|
case .guardians(items: let items): return items.count
|
|
}
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
|
|
switch sections[indexPath.section] {
|
|
|
|
case .guardiansCount(count: let count):
|
|
let cell = tableView.dequeueCell(GuardianCountTableViewCell.self)
|
|
cell.setCount(count)
|
|
return cell
|
|
|
|
case .guardians(items: let items):
|
|
let cell = tableView.dequeueCell(GuardianTableViewCell.self)
|
|
let item = items[indexPath.row]
|
|
cell.set(guardian: item, selected: item.address.address == selectedDelegate)
|
|
return cell
|
|
}
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
tableView.deselectRow(at: indexPath, animated: true)
|
|
guard let item = self.item(at: indexPath) else { return }
|
|
let vc = GuardianDetailsViewController()
|
|
vc.onSelected = onSelected
|
|
vc.guardian = item
|
|
show(vc, sender: nil)
|
|
}
|
|
|
|
func item(at indexPath: IndexPath) -> Guardian? {
|
|
if searchController.isActive && searchController.showsSearchResultsController {
|
|
return resultsController.filteredGuardians[indexPath.row]
|
|
}
|
|
switch sections[indexPath.section] {
|
|
|
|
case .guardians(items: let items):
|
|
let item = items[indexPath.row]
|
|
return item
|
|
|
|
default: break
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
class GuardianSearchResultController: UITableViewController {
|
|
var filteredGuardians: [Guardian] = []
|
|
var selectedDelegate: Address?
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
tableView.registerCell(GuardianTableViewCell.self)
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
filteredGuardians.count
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
let cell = tableView.dequeueCell(GuardianTableViewCell.self)
|
|
let item = filteredGuardians[indexPath.row]
|
|
cell.set(guardian: item, selected: item.address.address == selectedDelegate)
|
|
return cell
|
|
}
|
|
}
|