237 lines
9 KiB
Swift
237 lines
9 KiB
Swift
//
|
|
// BaseChatViewControllerWithDataSourceAndContextMenuAndToolbar.swift
|
|
//
|
|
// Siskin IM
|
|
// Copyright (C) 2017 "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 BaseChatViewControllerWithDataSourceAndContextMenuAndToolbar: BaseChatViewControllerWithDataSource, UITableViewDelegate {
|
|
|
|
fileprivate weak var timestampsSwitch: UIBarButtonItem? = nil;
|
|
|
|
var contextActions: [ContextAction] = [.copy, .reply, .share, .correct, .retract, .more];
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
if #available(iOS 13.0, *) {
|
|
|
|
} else {
|
|
var items: [UIMenuItem] = UIMenuController.shared.menuItems ?? [];
|
|
items.append(UIMenuItem(title: NSLocalizedString("More...", comment: "Menu item: see more message actions"), action: #selector(ChatTableViewCell.actionMore(_:))));
|
|
UIMenuController.shared.menuItems = items;
|
|
}
|
|
|
|
super.viewWillAppear(animated);
|
|
}
|
|
|
|
override func viewDidDisappear(_ animated: Bool) {
|
|
UIMenuController.shared.menuItems = UIMenuController.shared.menuItems?.filter({ it -> Bool in it.action != #selector(ChatTableViewCell.actionMore(_:))});
|
|
|
|
super.viewDidDisappear(animated);
|
|
}
|
|
|
|
override func initialize(tableView: UITableView) {
|
|
super.initialize(tableView: tableView);
|
|
tableView.delegate = self;
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
|
|
if #available(iOS 13.0, *) {
|
|
} else {
|
|
if action == #selector(UIResponderStandardEditActions.copy(_:)) {
|
|
return true;
|
|
}
|
|
// if customToolbar != nil && action == #selector(ChatTableViewCell.actionMore(_:)) {
|
|
// return true;
|
|
// }
|
|
}
|
|
return false;
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool {
|
|
if #available(iOS 13.0, *) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, performAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) {
|
|
if action == #selector(UIResponderStandardEditActions.copy(_:)) {
|
|
conversationLogController?.copyMessageInt(paths: [indexPath]);
|
|
}
|
|
conversationLogController?.hideEditToolbar();
|
|
}
|
|
|
|
@available(iOS 13.0, *)
|
|
func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
|
|
return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: {
|
|
let cell = self.tableView(tableView, cellForRowAt: indexPath);
|
|
cell.contentView.transform = .identity;
|
|
let view = UIViewController();
|
|
let size = self.conversationLogController!.tableView.rectForRow(at: indexPath).size;
|
|
view.view = cell.contentView;
|
|
view.preferredContentSize = size;
|
|
return view;
|
|
}) { suggestedActions -> UIMenu? in
|
|
return self.prepareContextMenu(for: indexPath);
|
|
};
|
|
}
|
|
|
|
@available(iOS 13.0, *)
|
|
func prepareContextMenu(for indexPath: IndexPath) -> UIMenu? {
|
|
guard let item = self.conversationLogController?.dataSource.getItem(at: indexPath.row) as? ChatEntry else {
|
|
return nil;
|
|
}
|
|
|
|
let actions = self.contextActions.filter({ self.canExecuteContext(action: $0, forItem: item, at: indexPath) });
|
|
let items: [UIMenuElement] = actions.map({ action -> UIMenuElement in
|
|
if action.isDesctructive {
|
|
return UIMenu(title: action.title, image: action.image, options: .destructive, children: [
|
|
UIAction(title: NSLocalizedString("No", comment: ""), handler: { _ in }),
|
|
UIAction(title: NSLocalizedString("Yes", comment: ""), attributes: .destructive, handler: { _ in
|
|
self.executeContext(action: action, forItem: item, at: indexPath);
|
|
})
|
|
]);
|
|
} else {
|
|
return UIAction(title: action.title, image: action.image, handler: { _ in
|
|
self.executeContext(action: action, forItem: item, at: indexPath);
|
|
})
|
|
}
|
|
})
|
|
|
|
return UIMenu(title: "", children: items);
|
|
}
|
|
|
|
public func executeContext(action: ContextAction, forItem item: ChatEntry, at indexPath: IndexPath) {
|
|
switch action {
|
|
case .copy:
|
|
self.conversationLogController?.copyMessageInt(paths: [indexPath]);
|
|
case .reply:
|
|
// something to do..
|
|
self.conversationLogController?.getTextOfSelectedRows(paths: [indexPath], withTimestamps: false, handler: { [weak self] texts in
|
|
let text: String = texts.flatMap { $0.split(separator: "\n")}.map {
|
|
if $0.starts(with: ">") {
|
|
return ">\($0)";
|
|
} else {
|
|
return "> \($0)"
|
|
}
|
|
}.joined(separator: "\n");
|
|
|
|
if let current = self?.messageText, !current.isEmpty {
|
|
self?.messageText = "\(current)\n\(text)\n";
|
|
} else {
|
|
self?.messageText = "\(text)\n";
|
|
}
|
|
})
|
|
case .share:
|
|
self.conversationLogController?.shareMessageInt(paths: [indexPath]);
|
|
case .correct:
|
|
DBChatHistoryStore.instance.originId(for: item.account, with: item.jid, id: item.id, completionHandler: { [weak self] originId in
|
|
DispatchQueue.main.async {
|
|
self?.startMessageCorrection(message: (item as! ChatMessage).message, originId: originId)
|
|
}
|
|
});
|
|
case .retract:
|
|
// that is per-chat-type sepecific
|
|
break;
|
|
case .more:
|
|
guard let cell = self.conversationLogController?.tableView.cellForRow(at: indexPath) else {
|
|
return;
|
|
}
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
|
NotificationCenter.default.post(name: Notification.Name("tableViewCellShowEditToolbar"), object: cell);
|
|
}
|
|
}
|
|
}
|
|
|
|
public func canExecuteContext(action: ContextAction, forItem item: ChatEntry, at indexPath: IndexPath) -> Bool {
|
|
switch action {
|
|
case .copy:
|
|
return true;
|
|
case .reply:
|
|
return true;
|
|
case .share:
|
|
return true;
|
|
case .correct:
|
|
return item.state.direction == .outgoing && item is ChatMessage && !dataSource.isAnyMatching({ $0.state.direction == .outgoing && $0 is ChatMessage }, in: 0..<indexPath.row);
|
|
case .retract:
|
|
return false;
|
|
case .more:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public enum ContextAction {
|
|
case copy
|
|
case reply
|
|
case share
|
|
case correct
|
|
case retract
|
|
case more
|
|
|
|
var title: String {
|
|
switch self {
|
|
case .copy:
|
|
return "Copy";
|
|
case .reply:
|
|
return "Reply..";
|
|
case .share:
|
|
return "Share..";
|
|
case .correct:
|
|
return "Correct..";
|
|
case .retract:
|
|
return "Retract";
|
|
case .more:
|
|
return "More..";
|
|
}
|
|
}
|
|
|
|
var image: UIImage? {
|
|
guard #available(iOS 13.0, *) else {
|
|
return nil;
|
|
}
|
|
switch self {
|
|
case .copy:
|
|
return UIImage(systemName: "doc.on.doc");
|
|
case .reply:
|
|
return UIImage(systemName: "arrowshape.turn.up.left");
|
|
case .share:
|
|
return UIImage(systemName: "square.and.arrow.up");
|
|
case .correct:
|
|
return UIImage(systemName: "pencil.and.ellipsis.rectangle");
|
|
case .retract:
|
|
return UIImage(systemName: "trash");
|
|
case .more:
|
|
return UIImage(systemName: "ellipsis");
|
|
}
|
|
}
|
|
|
|
var isDesctructive: Bool {
|
|
switch self {
|
|
case .retract:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|