203 lines
8.5 KiB
Swift
203 lines
8.5 KiB
Swift
//
|
|
// AbstractRosterViewController.swift
|
|
//
|
|
// Siskin IM
|
|
// Copyright (C) 2019 "Tigase, Inc." <office@tigase.com>
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. Look for COPYING file in the top folder.
|
|
// If not, see https://www.gnu.org/licenses/.
|
|
//
|
|
|
|
import UIKit
|
|
import TigaseSwift
|
|
|
|
class AbstractRosterViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {
|
|
|
|
fileprivate static let UPDATE_NOTIFICATION_NAME = Notification.Name("ROSTER_UPDATE");
|
|
|
|
var searchController: UISearchController!;
|
|
|
|
var allowsMultipleSelection = false
|
|
|
|
var roster: RosterProvider? {
|
|
didSet {
|
|
if let value = oldValue {
|
|
NotificationCenter.default.removeObserver(value);
|
|
}
|
|
if let roster = self.roster {
|
|
NotificationCenter.default.addObserver(roster, selector: #selector(RosterProviderAbstractBase.contactPresenceChanged(_:)), name: XmppService.CONTACT_PRESENCE_CHANGED, object: nil);
|
|
NotificationCenter.default.addObserver(roster, selector: #selector(RosterProviderAbstractBase.rosterItemUpdated(_:)), name: DBRosterStore.ITEM_UPDATED, object: nil);
|
|
}
|
|
}
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
searchController = UISearchController(searchResultsController: nil);
|
|
searchController.dimsBackgroundDuringPresentation = false;
|
|
searchController.hidesNavigationBarDuringPresentation = false;
|
|
searchController.obscuresBackgroundDuringPresentation = false;
|
|
searchController.searchResultsUpdater = self;
|
|
searchController.searchBar.searchBarStyle = .prominent;
|
|
//searchController.searchBar.isOpaque = false;
|
|
// searchController.searchBar.isTranslucent = true;
|
|
refreshControl?.isOpaque = false;
|
|
navigationItem.searchController = self.searchController;
|
|
tableView.rowHeight = 48;//UITableViewAutomaticDimension;
|
|
self.navigationItem.hidesSearchBarWhenScrolling = true;
|
|
}
|
|
|
|
override func didReceiveMemoryWarning() {
|
|
super.didReceiveMemoryWarning()
|
|
// Dispose of any resources that can be recreated.
|
|
if !self.isBeingPresented {
|
|
roster = nil;
|
|
}
|
|
}
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
NotificationCenter.default.addObserver(self, selector: #selector(RosterViewController.rowUpdated), name: RosterViewController.UPDATE_NOTIFICATION_NAME, object: nil);
|
|
initializeRosterProvider();
|
|
reloadData();
|
|
NotificationCenter.default.addObserver(self, selector: #selector(RosterViewController.reloadData), name: AvatarManager.AVATAR_CHANGED, object: nil);
|
|
super.viewWillAppear(animated);
|
|
}
|
|
|
|
func initializeRosterProvider(availableOnly: Bool = false, sortOrder: RosterSortingOrder = .alphabetical) {
|
|
let rosterType = RosterType(rawValue: Settings.RosterType.getString() ?? "") ?? RosterType.flat;
|
|
let displayHiddenGroup = Settings.RosterDisplayHiddenGroup.getBool();
|
|
let dbConnection = (UIApplication.shared.delegate as! AppDelegate).dbConnection!;
|
|
switch rosterType {
|
|
case .flat:
|
|
roster = RosterProviderFlat(dbConnection: dbConnection, order: sortOrder, availableOnly: availableOnly, displayHiddenGroup: displayHiddenGroup, updateNotificationName: RosterViewController.UPDATE_NOTIFICATION_NAME);
|
|
case .grouped:
|
|
roster = RosterProviderGrouped(dbConnection: dbConnection, order: sortOrder, availableOnly: availableOnly, displayHiddenGroup: displayHiddenGroup, updateNotificationName: RosterViewController.UPDATE_NOTIFICATION_NAME);
|
|
}
|
|
}
|
|
|
|
override func viewWillDisappear(_ animated: Bool) {
|
|
super.viewWillDisappear(animated);
|
|
NotificationCenter.default.removeObserver(self);
|
|
roster = nil;
|
|
}
|
|
|
|
override func numberOfSections(in: UITableView) -> Int {
|
|
return roster?.numberOfSections() ?? 0;
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
return roster?.numberOfRows(in: section) ?? 0;
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
|
return roster?.sectionHeader(at: section);
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
let cellIdentifier = "RosterItemTableViewCell";
|
|
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! RosterItemTableViewCell;
|
|
|
|
if let item = roster?.item(at: indexPath) {
|
|
cell.nameLabel.text = item.displayName;
|
|
cell.statusLabel.text = item.account.stringValue;
|
|
cell.avatarStatusView.set(name: item.displayName, avatar: AvatarManager.instance.avatar(for: item.jid.bareJid, on: item.account), orDefault: AvatarManager.instance.defaultAvatar);
|
|
}
|
|
|
|
if allowsMultipleSelection {
|
|
if let selectedRows = tableView.indexPathsForSelectedRows, selectedRows.contains(indexPath) {
|
|
cell.accessoryType = .checkmark
|
|
} else {
|
|
cell.accessoryType = .none
|
|
}
|
|
}
|
|
return cell
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
|
|
if let v = view as? UITableViewHeaderFooterView {
|
|
v.textLabel?.font = UIFont.preferredFont(forTextStyle: .subheadline);
|
|
v.textLabel?.text = v.textLabel?.text?.uppercased();
|
|
v.textLabel?.textColor = UIColor.white;
|
|
v.isOpaque = true;
|
|
v.tintColor = UIColor(named: "chatslistBackground")?.lighter(ratio: 0.1);
|
|
}
|
|
}
|
|
|
|
func updateSearchResults(for searchController: UISearchController) {
|
|
print("searching items containing:", searchController.searchBar.text ?? "");
|
|
roster?.queryItems(contains: searchController.searchBar.text);
|
|
tableView.reloadData();
|
|
}
|
|
|
|
func avatarChanged(_ notification: NSNotification) {
|
|
DispatchQueue.main.async() {
|
|
// let jid = notification.userInfo!["jid"] as! BareJID;
|
|
// let indexPaths = self.indexPaths(for: jid);
|
|
// self.tableView.reloadRows(at: indexPaths, with: .automatic);
|
|
self.tableView.reloadData();
|
|
}
|
|
}
|
|
|
|
@objc func reloadData() {
|
|
DispatchQueue.main.async() {
|
|
self.tableView.reloadData();
|
|
}
|
|
}
|
|
|
|
@objc func rowUpdated(_ notification: NSNotification) {
|
|
guard let info = notification.userInfo else {
|
|
return;
|
|
}
|
|
|
|
guard !(info["refresh"] as? Bool ?? false) else {
|
|
self.tableView.reloadData();
|
|
return;
|
|
}
|
|
|
|
let from = info["from"] as? [IndexPath];
|
|
let to = info["to"] as? [IndexPath];
|
|
|
|
if to == nil {
|
|
self.tableView.deleteRows(at: from!, with: .automatic);
|
|
return;
|
|
}
|
|
if from == nil {
|
|
self.tableView.insertRows(at: to!, with: .automatic);
|
|
return;
|
|
}
|
|
if from! == to! {
|
|
self.tableView.reloadRows(at: from!, with: .automatic);
|
|
} else {
|
|
self.tableView.beginUpdates();
|
|
let x = min(from!.count, to!.count)
|
|
if x < from!.count {
|
|
let toDelete: [IndexPath] = Array(from![x..<from!.count]);
|
|
self.tableView.deleteRows(at: toDelete, with: .automatic);
|
|
}
|
|
if x < to!.count {
|
|
let toAdd: [IndexPath] = Array(to![x..<to!.count]);
|
|
self.tableView.insertRows(at: toAdd, with: .automatic);
|
|
}
|
|
for i in 0..<x {
|
|
self.tableView.moveRow(at: from![i], to: to![i]);
|
|
}
|
|
self.tableView.endUpdates();
|
|
self.tableView.reloadRows(at: to!, with: .automatic);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|