117 lines
3.8 KiB
Swift
117 lines
3.8 KiB
Swift
//
|
|
// PasswordNavigationDataSource.swift
|
|
// pass
|
|
//
|
|
// Created by Sun, Mingshen on 1/16/21.
|
|
// Copyright © 2021 Bob Sun. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import passKit
|
|
|
|
struct Section {
|
|
var title: String
|
|
var entries: [PasswordTableEntry]
|
|
}
|
|
|
|
class PasswordNavigationDataSource: NSObject, UITableViewDataSource {
|
|
var sections: [Section]
|
|
var filteredSections: [Section]
|
|
|
|
var isSearchActive = false
|
|
var cellGestureRecognizer: UIGestureRecognizer?
|
|
|
|
let hideSectionHeaderThreshold = 6
|
|
|
|
init(entries: [PasswordTableEntry] = []) {
|
|
sections = buildSections(from: entries)
|
|
filteredSections = sections
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
filteredSections[section].entries.count
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
|
showSectionTitles() ? filteredSections[section].title : nil
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
|
|
index
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath) as! PasswordTableViewCell
|
|
let entry = getPasswordTableEntry(at: indexPath)
|
|
cell.configure(with: entry)
|
|
|
|
if let gestureRecognizer = cellGestureRecognizer, !entry.isDir {
|
|
cell.addGestureRecognizer(gestureRecognizer)
|
|
}
|
|
return cell
|
|
}
|
|
|
|
func numberOfSections(in tableView: UITableView) -> Int {
|
|
filteredSections.count
|
|
}
|
|
|
|
func sectionIndexTitles(for _: UITableView) -> [String]? {
|
|
showSectionTitles() ? filteredSections.map(\.title) : nil
|
|
}
|
|
|
|
func showSectionTitles() -> Bool {
|
|
!isSearchActive && filteredSections.count > hideSectionHeaderThreshold
|
|
}
|
|
|
|
func getPasswordTableEntry(at indexPath: IndexPath) -> PasswordTableEntry {
|
|
filteredSections[indexPath.section].entries[indexPath.row]
|
|
}
|
|
|
|
func showTableEntries(matching text: String) {
|
|
guard !text.isEmpty else {
|
|
filteredSections = sections
|
|
return
|
|
}
|
|
|
|
filteredSections = sections.map { section in
|
|
let entries = section.entries.filter { $0.match(text) }
|
|
return Section(title: section.title, entries: entries)
|
|
}
|
|
.filter { !$0.entries.isEmpty }
|
|
}
|
|
|
|
func showUnsyncedTableEntries() {
|
|
filteredSections = sections.map { section in
|
|
let entries = section.entries.filter { !$0.synced }
|
|
return Section(title: section.title, entries: entries)
|
|
}
|
|
.filter { !$0.entries.isEmpty }
|
|
}
|
|
}
|
|
|
|
private func buildSections(from entries: [PasswordTableEntry]) -> [Section] {
|
|
let collation = UILocalizedIndexedCollation.current()
|
|
let sectionTitles = collation.sectionIndexTitles
|
|
var sections = [Section]()
|
|
|
|
// initialize all sections
|
|
for titleNumber in 0 ..< sectionTitles.count {
|
|
sections.append(Section(title: sectionTitles[titleNumber], entries: [PasswordTableEntry]()))
|
|
}
|
|
|
|
// put entries into sections
|
|
for entry in entries {
|
|
let sectionNumber = collation.section(for: entry, collationStringSelector: #selector(getter: PasswordTableEntry.title))
|
|
sections[sectionNumber].entries.append(entry)
|
|
}
|
|
|
|
// sort each list and set sectionTitles
|
|
for titleNumber in 0 ..< sectionTitles.count {
|
|
let entriesToSort = sections[titleNumber].entries
|
|
let sortedEntries = collation.sortedArray(from: entriesToSort, collationStringSelector: #selector(getter: PasswordTableEntry.title))
|
|
sections[titleNumber].entries = sortedEntries as! [PasswordTableEntry]
|
|
}
|
|
|
|
// only keep non-empty sections
|
|
return sections.filter { !$0.entries.isEmpty }
|
|
}
|