blob: 78470a0bf7c54b9701aae0df30e9c5a301083579 [file] [log] [blame]
// Copyright 2016 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
import UIKit
enum Category: Int {
case NearbyContacts
case Nearby
case Contacts
static let allRawValues = [NearbyContacts.rawValue, Nearby.rawValue, Contacts.rawValue]
}
class InviteViewController: UITableViewController {
var searchController: UISearchController!
let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
let personCellId = "personCellId"
let sendEmailCell = "sendEmailCellId"
var shouldDismiss = false
var people: [[Person]] = [[], [], []]
var searchResultsFound = false
var searchResults: [[Person]] = [[], [], []]
let sectionNames: [Category: String] = [
.NearbyContacts: "Nearby Contacts",
.Nearby: "Nearby",
.Contacts: "Contacts",
]
override func viewDidLoad() {
super.viewDidLoad()
initSearchController()
// createFakeData()
// tableView.reloadData()
}
func initSearchController() {
// Cannot be done in IB yet.
searchController = UISearchController(searchResultsController: nil)
searchController.delegate = self
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.sizeToFit()
tableView.tableHeaderView = searchController.searchBar
definesPresentationContext = true
}
// func createFakeData() {
// // TODO(azinman): Remove.
// people.insert([
// Person(name: "Lady Rainicorn", imageName: "profilePhoto"),
// Person(name: "Princess Bubblegum", imageName: "profilePhoto"),
// Person(name: "Ice King", imageName: "profilePhoto")
// ],
// atIndex: Category.NearbyContacts.rawValue
// )
//
// people.insert([
// Person(name: "Gunter", imageName: "profilePhoto"),
// Person(name: "dayang@google.com", imageName: "profilePhoto"),
// Person(name: "Tom", imageName: "profilePhoto"),
// ],
// atIndex: Category.Nearby.rawValue
// )
//
// people.insert([
// Person(name: "Lady Rainicorn", imageName: "profilePhoto"),
// ],
// atIndex: Category.Contacts.rawValue
// )
// }
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if searchController.active && !searchResultsFound {
// We only have one section in the case of the send email cell.
return 1
}
return sectionNames.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.active {
if searchResultsFound {
return searchResults[section].count
} else {
// We only have one row in the case of the send email cell.
return 1
}
}
return people[section].count
}
override func tableView(tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
view.tintColor = UIColor.whiteColor()
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if searchController.active && !searchResultsFound {
// Don't show a section title if we're just showing send an email cell.
return nil
}
return sectionNames[Category(rawValue: section)!]
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// In the case where we have are searching and have found no results, offer the send email cell.
if searchController.active && !searchResultsFound {
// This cell is a prototype inside the Main.storyboard. Cannot fail.
let cell = tableView.dequeueReusableCellWithIdentifier(sendEmailCell, forIndexPath: indexPath) as! SendEmailCell
cell.emailLabel.text = searchController.searchBar.text
return cell
}
// This cell is a prototype inside the Main.storyboard. Cannot fail.
let cell = self.tableView.dequeueReusableCellWithIdentifier(personCellId, forIndexPath: indexPath) as! PersonCell
if searchController.active {
// Show search results.
cell.person = searchResults[indexPath.section][indexPath.row]
} else {
// Not searching, show entire list.
cell.person = people[indexPath.section][indexPath.row]
}
cell.updateView()
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// No-op if can't create a person from index path.
if let personToInvite = personToInvite(indexPath) {
invitePerson(personToInvite)
// Dismiss the view.
if searchController.active {
// The search bar needs to close before we pop, so handle in didDismissSearch callback.
shouldDismiss = true
searchController.active = false
} else {
navigationController?.popViewControllerAnimated(true)
}
}
}
func invitePerson(person: Person) {
NSLog("Inviting \(person)")
// TODO(azinman): fill in.
}
func personToInvite(indexPath: NSIndexPath) -> Person? {
// Determine if we entered an email or not. If email, validate email and construct a Person obj.
var personToInvite: Person?
if let text = searchController.searchBar.text where searchController.active && !searchResultsFound {
let validEmail = NSPredicate(format: "SELF MATCHES %@", emailRegEx).evaluateWithObject(text)
if validEmail {
// Create new person with this email.
personToInvite = Person(name: "", imageRef: "", email: text)
} else {
// Invalid email, show alert.
let alert = UIAlertController(title: "Invalid email",
message: "Please enter a valid email",
preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: nil))
navigationController?.presentViewController(alert, animated: true, completion: nil)
}
} else if people.indices.contains(indexPath.section) &&
people[indexPath.row].indices.contains(indexPath.row) {
// We're inviting an existing Person.
personToInvite = people[indexPath.section][indexPath.row]
}
return personToInvite
}
func didDismissSearchController(searchController: UISearchController) {
// If we were searching when we selected a person, we need to wait for its dismiss animation
// to finish before popping the view.
if shouldDismiss {
navigationController?.popViewControllerAnimated(true)
}
}
}
// Filters people to match the text user searched for.
extension InviteViewController: UISearchControllerDelegate, UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController: UISearchController) {
// Start with all people, then filter.
searchResults = people
if let searchText = searchController.searchBar.text where searchText.characters.count > 0 {
for category in Category.allRawValues {
searchResults[category] = searchResults[category].filter { person -> Bool in
return person.name.rangeOfString(searchText,
options: .CaseInsensitiveSearch,
range: person.name.startIndex ..< person.name.endIndex,
locale: nil) != nil
}
}
}
searchResultsFound = searchResults.flatten().count > 0
tableView.reloadData()
}
}
// Displays a person with name and their profile photo.
class PersonCell: UITableViewCell {
@IBOutlet weak var photoImageView: UIImageView!
@IBOutlet weak var nameLabel: UILabel!
var person: Person?
func updateView() {
nameLabel.text = person?.name
// if let imageName = person?.imageName {
// photoImageView.image = UIImage(named: imageName)
// }
}
}
// Display a cell that shows the email the invite will be sent to.
class SendEmailCell: UITableViewCell {
@IBOutlet weak var emailLabel: UILabel!
}