Added support for private messages in MUC #siskinim-197
This commit is contained in:
parent
747dfef04a
commit
c5797d237f
|
@ -25,7 +25,7 @@ import TigaseSwift
|
|||
|
||||
public class DBSchemaManager {
|
||||
|
||||
static let CURRENT_VERSION = 9;
|
||||
static let CURRENT_VERSION = 10;
|
||||
|
||||
fileprivate let dbConnection: DBConnection;
|
||||
|
||||
|
|
9
Shared/db-schema-10.sql
Normal file
9
Shared/db-schema-10.sql
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE chat_history ADD COLUMN recipient_nickname TEXT;
|
||||
|
||||
COMMIT;
|
||||
|
||||
PRAGMA user_version = 10;
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
FE00157D2017617B00490340 /* StreamFeaturesCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE00157C2017617B00490340 /* StreamFeaturesCache.swift */; };
|
||||
FE00157F2019090300490340 /* ExperimentalSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE00157E2019090300490340 /* ExperimentalSettingsViewController.swift */; };
|
||||
FE01ADA91E224CF400FA7E65 /* SiskinPushNotificationsModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE01ADA81E224CF400FA7E65 /* SiskinPushNotificationsModule.swift */; };
|
||||
FE10BCF323FD4EF000E214F3 /* db-schema-10.sql in Resources */ = {isa = PBXBuildFile; fileRef = FE10BCF223FD4EF000E214F3 /* db-schema-10.sql */; };
|
||||
FE137A4821F6464D006B7F7C /* UIColor_mix.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE137A4721F6464D006B7F7C /* UIColor_mix.swift */; };
|
||||
FE137A4A21F72AEA006B7F7C /* Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE137A4921F72AEA006B7F7C /* Appearance.swift */; };
|
||||
FE137A4C21F75660006B7F7C /* ChatBottomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE137A4B21F75660006B7F7C /* ChatBottomView.swift */; };
|
||||
|
@ -259,6 +260,7 @@
|
|||
FE00157C2017617B00490340 /* StreamFeaturesCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StreamFeaturesCache.swift; sourceTree = "<group>"; };
|
||||
FE00157E2019090300490340 /* ExperimentalSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExperimentalSettingsViewController.swift; sourceTree = "<group>"; };
|
||||
FE01ADA81E224CF400FA7E65 /* SiskinPushNotificationsModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SiskinPushNotificationsModule.swift; sourceTree = "<group>"; };
|
||||
FE10BCF223FD4EF000E214F3 /* db-schema-10.sql */ = {isa = PBXFileReference; lastKnownFileType = text; path = "db-schema-10.sql"; sourceTree = "<group>"; };
|
||||
FE137A4721F6464D006B7F7C /* UIColor_mix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColor_mix.swift; sourceTree = "<group>"; };
|
||||
FE137A4921F72AEA006B7F7C /* Appearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Appearance.swift; sourceTree = "<group>"; };
|
||||
FE137A4B21F75660006B7F7C /* ChatBottomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatBottomView.swift; sourceTree = "<group>"; };
|
||||
|
@ -694,6 +696,7 @@
|
|||
FE759FC82370B2A4001E78D9 /* Info.plist */,
|
||||
FE759FF823742AC1001E78D9 /* NotificationCategory.swift */,
|
||||
FECEF29723B7B838007EC323 /* db-schema-9.sql */,
|
||||
FE10BCF223FD4EF000E214F3 /* db-schema-10.sql */,
|
||||
);
|
||||
path = Shared;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1019,6 +1022,7 @@
|
|||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
FE10BCF323FD4EF000E214F3 /* db-schema-10.sql in Resources */,
|
||||
FE759FF22371F21C001E78D9 /* db-schema-6.sql in Resources */,
|
||||
FE759FEF2371F21C001E78D9 /* db-schema-3.sql in Resources */,
|
||||
FE759FED2371F213001E78D9 /* db-schema-2.sql in Resources */,
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<key>Shared.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>6</integer>
|
||||
<integer>7</integer>
|
||||
</dict>
|
||||
<key>SiskinIM - Share.xcscheme</key>
|
||||
<dict>
|
||||
|
|
|
@ -27,10 +27,10 @@ public class ChatAttachment: ChatEntry {
|
|||
let url: String;
|
||||
var appendix: ChatAttachmentAppendix;
|
||||
|
||||
init(id: Int, timestamp: Date, account: BareJID, jid: BareJID, state: MessageState, url: String, authorNickname: String?, authorJid: BareJID?, encryption: MessageEncryption, encryptionFingerprint: String?, appendix: ChatAttachmentAppendix, error: String?) {
|
||||
init(id: Int, timestamp: Date, account: BareJID, jid: BareJID, state: MessageState, url: String, authorNickname: String?, authorJid: BareJID?, recipientNickname: String?, encryption: MessageEncryption, encryptionFingerprint: String?, appendix: ChatAttachmentAppendix, error: String?) {
|
||||
self.url = url;
|
||||
self.appendix = appendix;
|
||||
super.init(id: id, timestamp: timestamp, account: account, jid: jid, state: state, authorNickname: authorNickname, authorJid: authorJid, encryption: encryption, encryptionFingerprint: encryptionFingerprint, error: error);
|
||||
super.init(id: id, timestamp: timestamp, account: account, jid: jid, state: state, authorNickname: authorNickname, authorJid: authorJid, recipientNickname: recipientNickname, encryption: encryption, encryptionFingerprint: encryptionFingerprint, error: error);
|
||||
}
|
||||
|
||||
override public func copyText(withTimestamp: Bool, withSender: Bool) -> String? {
|
||||
|
|
|
@ -40,13 +40,14 @@ public class ChatEntry: ChatViewItemProtocol {
|
|||
// for MUC only but any chat may be a MUC chat...
|
||||
public let authorNickname: String?;
|
||||
public let authorJid: BareJID?;
|
||||
public let recipientNickname: String?;
|
||||
|
||||
public let error: String?;
|
||||
|
||||
public let encryption: MessageEncryption;
|
||||
public let encryptionFingerprint: String?;
|
||||
|
||||
init(id: Int, timestamp: Date, account: BareJID, jid: BareJID, state: MessageState, authorNickname: String?, authorJid: BareJID?, encryption: MessageEncryption, encryptionFingerprint: String?, error: String?) {
|
||||
init(id: Int, timestamp: Date, account: BareJID, jid: BareJID, state: MessageState, authorNickname: String?, authorJid: BareJID?, recipientNickname: String?, encryption: MessageEncryption, encryptionFingerprint: String?, error: String?) {
|
||||
self.id = id;
|
||||
self.timestamp = timestamp;
|
||||
self.account = account;
|
||||
|
@ -54,6 +55,7 @@ public class ChatEntry: ChatViewItemProtocol {
|
|||
self.state = state;
|
||||
self.authorNickname = authorNickname;
|
||||
self.authorJid = authorJid;
|
||||
self.recipientNickname = recipientNickname;
|
||||
self.encryption = encryption;
|
||||
self.encryptionFingerprint = encryptionFingerprint;
|
||||
self.error = error;
|
||||
|
@ -63,7 +65,7 @@ public class ChatEntry: ChatViewItemProtocol {
|
|||
guard let item = chatItem as? ChatEntry else {
|
||||
return false;
|
||||
}
|
||||
return self.account == item.account && self.jid == item.jid && self.state.direction == item.state.direction && self.authorNickname == item.authorNickname && self.authorJid == item.authorJid && abs(self.timestamp.timeIntervalSince(item.timestamp)) < allowedTimeDiff() && self.encryption == item.encryption && self.encryptionFingerprint == item.encryptionFingerprint;
|
||||
return self.account == item.account && self.jid == item.jid && self.state.direction == item.state.direction && self.authorNickname == item.authorNickname && self.authorJid == item.authorJid && self.recipientNickname == item.recipientNickname && abs(self.timestamp.timeIntervalSince(item.timestamp)) < allowedTimeDiff() && self.encryption == item.encryption && self.encryptionFingerprint == item.encryptionFingerprint;
|
||||
}
|
||||
|
||||
public func copyText(withTimestamp: Bool, withSender: Bool) -> String? {
|
||||
|
|
|
@ -26,9 +26,9 @@ class ChatLinkPreview: ChatEntry {
|
|||
|
||||
let url: String;
|
||||
|
||||
init(id: Int, timestamp: Date, account: BareJID, jid: BareJID, state: MessageState, url: String, authorNickname: String?, authorJid: BareJID?, encryption: MessageEncryption, encryptionFingerprint: String?, error: String?) {
|
||||
init(id: Int, timestamp: Date, account: BareJID, jid: BareJID, state: MessageState, url: String, authorNickname: String?, authorJid: BareJID?, recipientNickname: String?, encryption: MessageEncryption, encryptionFingerprint: String?, error: String?) {
|
||||
self.url = url;
|
||||
super.init(id: id, timestamp: timestamp, account: account, jid: jid, state: state, authorNickname: authorNickname, authorJid: authorJid, encryption: encryption, encryptionFingerprint: encryptionFingerprint, error: error);
|
||||
super.init(id: id, timestamp: timestamp, account: account, jid: jid, state: state, authorNickname: authorNickname, authorJid: authorJid, recipientNickname: recipientNickname, encryption: encryption, encryptionFingerprint: encryptionFingerprint, error: error);
|
||||
}
|
||||
|
||||
override func copyText(withTimestamp: Bool, withSender: Bool) -> String? {
|
||||
|
|
|
@ -26,9 +26,9 @@ class ChatMessage: ChatEntry {
|
|||
|
||||
let message: String;
|
||||
|
||||
init(id: Int, timestamp: Date, account: BareJID, jid: BareJID, state: MessageState, message: String, authorNickname: String?, authorJid: BareJID?, encryption: MessageEncryption, encryptionFingerprint: String?, error: String?) {
|
||||
init(id: Int, timestamp: Date, account: BareJID, jid: BareJID, state: MessageState, message: String, authorNickname: String?, authorJid: BareJID?, recipientNickname: String?, encryption: MessageEncryption, encryptionFingerprint: String?, error: String?) {
|
||||
self.message = message;
|
||||
super.init(id: id, timestamp: timestamp, account: account, jid: jid, state: state, authorNickname: authorNickname, authorJid: authorJid, encryption: encryption, encryptionFingerprint: encryptionFingerprint, error: error);
|
||||
super.init(id: id, timestamp: timestamp, account: account, jid: jid, state: state, authorNickname: authorNickname, authorJid: authorJid, recipientNickname: recipientNickname, encryption: encryption, encryptionFingerprint: encryptionFingerprint, error: error);
|
||||
}
|
||||
|
||||
override func copyText(withTimestamp: Bool, withSender: Bool) -> String? {
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
import UIKit
|
||||
import TigaseSwift
|
||||
|
||||
class MucChatOccupantsTableViewController: CustomTableViewController {
|
||||
class MucChatOccupantsTableViewController: CustomTableViewController {//}, UIContextMenuInteractionDelegate {
|
||||
|
||||
var xmppService:XmppService!;
|
||||
|
||||
|
@ -82,7 +82,7 @@ class MucChatOccupantsTableViewController: CustomTableViewController {
|
|||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "MucChatOccupantsTableViewCell", for: indexPath as IndexPath) as! MucChatOccupantsTableViewCell;
|
||||
|
||||
|
||||
let occupant = participants[indexPath.row];
|
||||
cell.nicknameLabel.text = occupant.nickname;
|
||||
if let jid = occupant.jid {
|
||||
|
@ -112,6 +112,51 @@ class MucChatOccupantsTableViewController: CustomTableViewController {
|
|||
}
|
||||
self.navigationController?.popViewController(animated: true);
|
||||
}
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
|
||||
guard room.state == .joined else {
|
||||
return nil;
|
||||
}
|
||||
|
||||
let participant = self.participants[indexPath.row];
|
||||
return UIContextMenuConfiguration(identifier: nil, previewProvider: nil, actionProvider: { suggestedActions in
|
||||
var actions: [UIAction] = [];
|
||||
actions.append(UIAction(title: "Private message", handler: { action in
|
||||
let alert = UIAlertController(title: "Send message", message: "Enter message to send to: \(participant.nickname)", preferredStyle: .alert);
|
||||
alert.addTextField(configurationHandler: nil);
|
||||
alert.addAction(UIAlertAction(title: "Send", style: .default, handler: { action in
|
||||
guard let text = alert.textFields?.first?.text else {
|
||||
return;
|
||||
}
|
||||
MucEventHandler.instance.sendPrivateMessage(room: self.room, recipientNickname: participant.nickname, body: text);
|
||||
}));
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil));
|
||||
self.present(alert, animated: true, completion: nil);
|
||||
}));
|
||||
if let jid = participant.jid, self.room.presences[self.room.nickname]?.affiliation == MucAffiliation.admin {
|
||||
actions.append(UIAction(title: "Ban user", handler: { action in
|
||||
guard let mucModule: MucModule = XmppService.instance.getClient(for: self.room.account)?.modulesManager.getModule(MucModule.ID) else {
|
||||
return;
|
||||
}
|
||||
let alert = UIAlertController(title: "Banning user", message: "Do you want to ban user \(participant.nickname)?", preferredStyle: .alert);
|
||||
alert.addAction(UIAlertAction(title: "Yes", style: .destructive, handler: { action in
|
||||
mucModule.setRoomAffiliations(to: self.room, changedAffiliations: [MucModule.RoomAffiliation(jid: jid, affiliation: .outcast)], completionHandler: { error in
|
||||
guard let err = error else {
|
||||
return;
|
||||
}
|
||||
let alert = UIAlertController(title: "Banning user \(participant.nickname) failed", message: "Server returned an error: \(err.rawValue)", preferredStyle: .alert);
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil));
|
||||
self.present(alert, animated: true, completion: nil);
|
||||
})
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil));
|
||||
self.present(alert, animated: true, completion: nil);
|
||||
}));
|
||||
}
|
||||
return UIMenu(title: "", children: actions);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
// Override to support editing the table view.
|
||||
|
|
|
@ -130,7 +130,17 @@ class MucChatViewController: BaseChatViewControllerWithDataSourceAndContextMenuA
|
|||
cell.avatarView?.set(name: item.authorNickname, avatar: nil, orDefault: AvatarManager.instance.defaultAvatar);
|
||||
}
|
||||
}
|
||||
cell.nicknameView?.text = item.authorNickname;
|
||||
let sender = item.authorNickname ?? "From \(item.jid.stringValue)";
|
||||
if let author = item.authorNickname, let recipient = item.recipientNickname {
|
||||
let val = NSMutableAttributedString(string: item.state.direction == .incoming ? "From \(author) " : "To \(recipient) ");
|
||||
let font = UIFont.italicSystemFont(ofSize: cell.nicknameView!.font!.pointSize - 2);
|
||||
val.append(NSAttributedString(string: " (private message)", attributes: [.font: font, .foregroundColor: Appearance.current.secondaryLabelColor]));
|
||||
|
||||
cell.nicknameView?.attributedText = val;
|
||||
} else {
|
||||
cell.nicknameView?.text = sender;
|
||||
}
|
||||
|
||||
cell.set(message: item);
|
||||
cell.backgroundColor = Appearance.current.systemBackground;
|
||||
return cell;
|
||||
|
@ -147,7 +157,17 @@ class MucChatViewController: BaseChatViewControllerWithDataSourceAndContextMenuA
|
|||
cell.avatarView?.set(name: item.authorNickname, avatar: nil, orDefault: AvatarManager.instance.defaultAvatar);
|
||||
}
|
||||
}
|
||||
cell.nicknameView?.text = item.authorNickname;
|
||||
let sender = item.authorNickname ?? "From \(item.jid.stringValue)";
|
||||
if let author = item.authorNickname, let recipient = item.recipientNickname {
|
||||
let val = NSMutableAttributedString(string: item.state.direction == .incoming ? "From \(author) " : "To \(recipient) ");
|
||||
let font = UIFont.italicSystemFont(ofSize: cell.nicknameView!.font!.pointSize - 2);
|
||||
val.append(NSAttributedString(string: " (private message)", attributes: [.font: font, .foregroundColor: Appearance.current.secondaryLabelColor]));
|
||||
|
||||
cell.nicknameView?.attributedText = val;
|
||||
} else {
|
||||
cell.nicknameView?.text = sender;
|
||||
}
|
||||
|
||||
cell.set(attachment: item);
|
||||
cell.setNeedsUpdateConstraints();
|
||||
cell.updateConstraintsIfNeeded();
|
||||
|
|
|
@ -32,7 +32,7 @@ open class DBChatHistoryStore: Logger {
|
|||
public static let MESSAGE_REMOVED = Notification.Name("messageRemoved");
|
||||
|
||||
fileprivate static let CHAT_GET_ID_WITH_ACCOUNT_PARTICIPANT_AND_STANZA_ID = "SELECT id FROM chat_history WHERE account = :account AND jid = :jid AND stanza_id = :stanzaId";
|
||||
fileprivate static let CHAT_MSG_APPEND = "INSERT INTO chat_history (account, jid, author_jid, author_nickname, timestamp, item_type, data, stanza_id, state, encryption, fingerprint, appendix) VALUES (:account, :jid, :author_jid, :author_nickname, :timestamp, :item_type, :data, :stanza_id, :state, :encryption, :fingerprint, :appendix)";
|
||||
fileprivate static let CHAT_MSG_APPEND = "INSERT INTO chat_history (account, jid, author_jid, author_nickname, recipient_nickname, timestamp, item_type, data, stanza_id, state, encryption, fingerprint, appendix) VALUES (:account, :jid, :author_jid, :author_nickname, :recipient_nickname, :timestamp, :item_type, :data, :stanza_id, :state, :encryption, :fingerprint, :appendix)";
|
||||
fileprivate static let CHAT_MSGS_COUNT = "SELECT count(id) FROM chat_history WHERE account = :account AND jid = :jid";
|
||||
fileprivate static let CHAT_MSGS_DELETE = "DELETE FROM chat_history WHERE account = :account AND jid = :jid";
|
||||
fileprivate static let CHAT_MSGS_MARK_AS_READ = "UPDATE chat_history SET state = case state when \(MessageState.incoming_error_unread.rawValue) then \(MessageState.incoming_error.rawValue) when \(MessageState.outgoing_error_unread.rawValue) then \(MessageState.outgoing_error.rawValue) else \(MessageState.incoming.rawValue) end WHERE account = :account AND jid = :jid AND state in (\(MessageState.incoming_unread.rawValue), \(MessageState.incoming_error_unread.rawValue), \(MessageState.outgoing_error_unread.rawValue))";
|
||||
|
@ -54,7 +54,7 @@ open class DBChatHistoryStore: Logger {
|
|||
fileprivate lazy var getMessagePositionStmtInverted: DBStatement! = try? self.dbConnection.prepareStatement("SELECT count(id) FROM chat_history WHERE account = :account AND jid = :jid AND id <> :msgId AND (:showLinkPreviews OR item_type IN (\(ItemType.message.rawValue), \(ItemType.attachment.rawValue))) AND timestamp > (SELECT timestamp FROM chat_history WHERE id = :msgId)");
|
||||
fileprivate lazy var markMessageAsErrorStmt: DBStatement! = try? self.dbConnection.prepareStatement("UPDATE chat_history SET state = :state, error = :error WHERE id = :id");
|
||||
fileprivate lazy var getMessageErrorDetails: DBStatement! = try? self.dbConnection.prepareStatement("SELECT error FROM chat_history WHERE id = ?");
|
||||
fileprivate lazy var getChatMessageWithIdStmt: DBStatement! = try! self.dbConnection.prepareStatement("SELECT id, account, jid, author_nickname, author_jid, timestamp, item_type, data, state, preview, encryption, fingerprint, error, appendix FROM chat_history WHERE id = :id");
|
||||
fileprivate lazy var getChatMessageWithIdStmt: DBStatement! = try! self.dbConnection.prepareStatement("SELECT id, account, jid, author_nickname, author_jid, recipient_nickname, timestamp, item_type, data, state, preview, encryption, fingerprint, error, appendix FROM chat_history WHERE id = :id");
|
||||
fileprivate let getUnsentMessagesForAccountStmt: DBStatement;
|
||||
fileprivate let getChatMessagesStmt: DBStatement;
|
||||
fileprivate let getChatAttachmentsStmt: DBStatement;
|
||||
|
@ -71,14 +71,14 @@ open class DBChatHistoryStore: Logger {
|
|||
self.dispatcher = QueueDispatcher(label: "chat_history_store");
|
||||
self.dbConnection = dbConnection;
|
||||
self.getUnsentMessagesForAccountStmt = try! self.dbConnection.prepareStatement("SELECT ch.account as account, ch.jid as jid, ch.data as data, ch.stanza_id as stanza_id, ch.encryption as encryption FROM chat_history ch WHERE ch.account = :account AND ch.state = \(MessageState.outgoing_unsent.rawValue) ORDER BY timestamp ASC");
|
||||
self.getChatMessagesStmt = try! dbConnection.prepareStatement("SELECT id, author_nickname, author_jid, timestamp, item_type, data, state, preview, encryption, fingerprint, error, appendix FROM chat_history WHERE account = :account AND jid = :jid AND (:showLinkPreviews OR item_type IN (\(ItemType.message.rawValue), \(ItemType.attachment.rawValue))) ORDER BY timestamp DESC LIMIT :limit OFFSET :offset");
|
||||
self.getChatAttachmentsStmt = try! dbConnection.prepareStatement("SELECT id, author_nickname, author_jid, timestamp, item_type, data, state, preview, encryption, fingerprint, error, appendix FROM chat_history WHERE account = :account AND jid = :jid AND item_type = \(ItemType.attachment.rawValue) ORDER BY timestamp DESC");
|
||||
self.getChatMessagesStmt = try! dbConnection.prepareStatement("SELECT id, author_nickname, author_jid, recipient_nickname, timestamp, item_type, data, state, preview, encryption, fingerprint, error, appendix FROM chat_history WHERE account = :account AND jid = :jid AND (:showLinkPreviews OR item_type IN (\(ItemType.message.rawValue), \(ItemType.attachment.rawValue))) ORDER BY timestamp DESC LIMIT :limit OFFSET :offset");
|
||||
self.getChatAttachmentsStmt = try! dbConnection.prepareStatement("SELECT id, author_nickname, author_jid, recipient_nickname, timestamp, item_type, data, state, preview, encryption, fingerprint, error, appendix FROM chat_history WHERE account = :account AND jid = :jid AND item_type = \(ItemType.attachment.rawValue) ORDER BY timestamp DESC");
|
||||
self.updateItemStmt = try! dbConnection.prepareStatement("UPDATE chat_history SET appendix = :appendix WHERE id = :id")
|
||||
super.init();
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(DBChatHistoryStore.accountRemoved), name: NSNotification.Name(rawValue: "accountRemoved"), object: nil);
|
||||
}
|
||||
|
||||
public func appendItem(for account: BareJID, with jid: BareJID, state inState: MessageState, authorNickname: String? = nil, authorJid: BareJID? = nil, type: ItemType = .message, timestamp inTimestamp: Date, stanzaId id: String?, data: String, chatState: ChatState? = nil, errorCondition: ErrorCondition? = nil, errorMessage: String? = nil, encryption: MessageEncryption, encryptionFingerprint: String?, chatAttachmentAppendix: ChatAttachmentAppendix? = nil, skipItemAlreadyExists: Bool = false, completionHandler: ((Int)->Void)?) {
|
||||
public func appendItem(for account: BareJID, with jid: BareJID, state inState: MessageState, authorNickname: String? = nil, authorJid: BareJID? = nil, recipientNickname: String? = nil, type: ItemType = .message, timestamp inTimestamp: Date, stanzaId id: String?, data: String, chatState: ChatState? = nil, errorCondition: ErrorCondition? = nil, errorMessage: String? = nil, encryption: MessageEncryption, encryptionFingerprint: String?, chatAttachmentAppendix: ChatAttachmentAppendix? = nil, skipItemAlreadyExists: Bool = false, completionHandler: ((Int)->Void)?) {
|
||||
dispatcher.async {
|
||||
let timestamp = Date(timeIntervalSince1970: Double(Int64(inTimestamp.timeIntervalSince1970 * 1000)) / 1000);
|
||||
|
||||
|
@ -99,7 +99,8 @@ open class DBChatHistoryStore: Logger {
|
|||
}
|
||||
}
|
||||
|
||||
let params:[String:Any?] = ["account" : account, "jid" : jid, "timestamp": timestamp, "data": data, "item_type": type.rawValue, "state": state.rawValue, "stanza_id": id, "author_jid" : authorJid, "author_nickname": authorNickname, "encryption": encryption.rawValue, "fingerprint": encryptionFingerprint, "appendix": appendix]
|
||||
let params:[String:Any?] = ["account" : account, "jid" : jid, "timestamp": timestamp, "data": data, "item_type": type.rawValue, "state": state.rawValue, "stanza_id": id, "author_jid" : authorJid, "author_nickname": authorNickname,
|
||||
"recipient_nickname": recipientNickname, "encryption": encryption.rawValue, "fingerprint": encryptionFingerprint, "appendix": appendix]
|
||||
guard let msgId = try! self.appendMessageStmt.insert(params) else {
|
||||
return;
|
||||
}
|
||||
|
@ -108,12 +109,12 @@ open class DBChatHistoryStore: Logger {
|
|||
var item: ChatViewItemProtocol?;
|
||||
switch type {
|
||||
case .message:
|
||||
item = ChatMessage(id: msgId, timestamp: timestamp, account: account, jid: jid, state: state, message: data, authorNickname: authorNickname, authorJid: authorJid, encryption: encryption, encryptionFingerprint: encryptionFingerprint, error: errorMessage);
|
||||
item = ChatMessage(id: msgId, timestamp: timestamp, account: account, jid: jid, state: state, message: data, authorNickname: authorNickname, authorJid: authorJid, recipientNickname: recipientNickname, encryption: encryption, encryptionFingerprint: encryptionFingerprint, error: errorMessage);
|
||||
case .attachment:
|
||||
item = ChatAttachment(id: msgId, timestamp: timestamp, account: account, jid: jid, state: state, url: data, authorNickname: authorNickname, authorJid: authorJid, encryption: encryption, encryptionFingerprint: encryptionFingerprint, appendix: chatAttachmentAppendix ?? ChatAttachmentAppendix(), error: errorMessage);
|
||||
item = ChatAttachment(id: msgId, timestamp: timestamp, account: account, jid: jid, state: state, url: data, authorNickname: authorNickname, authorJid: authorJid, recipientNickname: recipientNickname, encryption: encryption, encryptionFingerprint: encryptionFingerprint, appendix: chatAttachmentAppendix ?? ChatAttachmentAppendix(), error: errorMessage);
|
||||
case .linkPreview:
|
||||
if #available(iOS 13.0, *), Settings.linkPreviews.bool() {
|
||||
item = ChatLinkPreview(id: msgId, timestamp: timestamp, account: account, jid: jid, state: state, url: data, authorNickname: authorNickname, authorJid: authorJid, encryption: encryption, encryptionFingerprint: encryptionFingerprint, error: errorMessage);
|
||||
item = ChatLinkPreview(id: msgId, timestamp: timestamp, account: account, jid: jid, state: state, url: data, authorNickname: authorNickname, authorJid: authorJid, recipientNickname: recipientNickname, encryption: encryption, encryptionFingerprint: encryptionFingerprint, error: errorMessage);
|
||||
}
|
||||
}
|
||||
if item != nil {
|
||||
|
@ -412,6 +413,7 @@ open class DBChatHistoryStore: Logger {
|
|||
|
||||
let authorNickname: String? = cursor["author_nickname"];
|
||||
let authorJid: BareJID? = cursor["author_jid"];
|
||||
let recipientNickname: String? = cursor["recipient_nickname"];
|
||||
let encryption: MessageEncryption = MessageEncryption(rawValue: cursor["encryption"] ?? 0) ?? .none;
|
||||
let encryptionFingerprint: String? = cursor["fingerprint"];
|
||||
let error: String? = cursor["error"];
|
||||
|
@ -420,16 +422,16 @@ open class DBChatHistoryStore: Logger {
|
|||
case .message:
|
||||
let message: String = cursor["data"]!;
|
||||
|
||||
return ChatMessage(id: id, timestamp: timestamp, account: account, jid: jid, state: MessageState(rawValue: stateInt)!, message: message, authorNickname: authorNickname, authorJid: authorJid, encryption: encryption, encryptionFingerprint: encryptionFingerprint, error: error);
|
||||
return ChatMessage(id: id, timestamp: timestamp, account: account, jid: jid, state: MessageState(rawValue: stateInt)!, message: message, authorNickname: authorNickname, authorJid: authorJid, recipientNickname: recipientNickname, encryption: encryption, encryptionFingerprint: encryptionFingerprint, error: error);
|
||||
case .attachment:
|
||||
let url: String = cursor["data"]!;
|
||||
|
||||
let appendix = parseAttachmentAppendix(string: cursor["appendix"]);
|
||||
|
||||
return ChatAttachment(id: id, timestamp: timestamp, account: account, jid: jid, state: MessageState(rawValue: stateInt)!, url: url, authorNickname: authorNickname, authorJid: authorJid, encryption: encryption, encryptionFingerprint: encryptionFingerprint, appendix: appendix, error: error);
|
||||
return ChatAttachment(id: id, timestamp: timestamp, account: account, jid: jid, state: MessageState(rawValue: stateInt)!, url: url, authorNickname: authorNickname, authorJid: authorJid, recipientNickname: recipientNickname, encryption: encryption, encryptionFingerprint: encryptionFingerprint, appendix: appendix, error: error);
|
||||
case .linkPreview:
|
||||
let url: String = cursor["data"]!;
|
||||
return ChatLinkPreview(id: id, timestamp: timestamp, account: account, jid: jid, state: MessageState(rawValue: stateInt)!, url: url, authorNickname: authorNickname, authorJid: authorJid, encryption: encryption, encryptionFingerprint: encryptionFingerprint, error: error)
|
||||
return ChatLinkPreview(id: id, timestamp: timestamp, account: account, jid: jid, state: MessageState(rawValue: stateInt)!, url: url, authorNickname: authorNickname, authorJid: authorJid, recipientNickname: recipientNickname, encryption: encryption, encryptionFingerprint: encryptionFingerprint, error: error)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -751,6 +751,12 @@ class DBRoom: Room, DBChatProtocol {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
override func createPrivateMessage(_ body: String?, recipientNickname: String) -> Message {
|
||||
let stanza = super.createPrivateMessage(body, recipientNickname: recipientNickname);
|
||||
stanza.id = UUID().uuidString;
|
||||
return stanza;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ChatEncryption: String {
|
||||
|
|
|
@ -100,19 +100,31 @@ class MessageEventHandler: XmppServiceEventHandler {
|
|||
}
|
||||
}
|
||||
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: from.bareJid, state: state, type: type, timestamp: timestamp, stanzaId: e.message.id, data: body!, chatState: e.message.chatState, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: nil);
|
||||
var authorNickname: String? = nil;
|
||||
var recipientNickname: String? = nil;
|
||||
if let room = DBChatStore.instance.getChat(for: account, with: from.bareJid) as? DBRoom {
|
||||
if state.direction == .incoming {
|
||||
authorNickname = from.resource;
|
||||
recipientNickname = room.nickname;
|
||||
} else {
|
||||
authorNickname = room.nickname;
|
||||
recipientNickname = e.message.to?.resource;
|
||||
}
|
||||
}
|
||||
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: from.bareJid, state: state, authorNickname: authorNickname, recipientNickname: recipientNickname, type: type, timestamp: timestamp, stanzaId: e.message.id, data: body!, chatState: e.message.chatState, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: nil);
|
||||
|
||||
if type == .message && !state.isError, #available(iOS 13.0, *) {
|
||||
let detector = try! NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue | NSTextCheckingResult.CheckingType.address.rawValue);
|
||||
let matches = detector.matches(in: body!, range: NSMakeRange(0, body!.utf16.count));
|
||||
matches.forEach { match in
|
||||
if let url = match.url, let scheme = url.scheme, ["https", "http"].contains(scheme) {
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: from.bareJid, state: state, type: .linkPreview, timestamp: timestamp, stanzaId: nil, data: url.absoluteString, chatState: e.message.chatState, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: nil);
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: from.bareJid, state: state, authorNickname: authorNickname, recipientNickname: recipientNickname, type: .linkPreview, timestamp: timestamp, stanzaId: nil, data: url.absoluteString, chatState: e.message.chatState, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: nil);
|
||||
}
|
||||
if let address = match.components {
|
||||
let query = address.values.joined(separator: ",").addingPercentEncoding(withAllowedCharacters: .urlHostAllowed);
|
||||
let mapUrl = URL(string: "http://maps.apple.com/?q=\(query!)")!;
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: from.bareJid, state: state, type: .linkPreview, timestamp: timestamp, stanzaId: nil, data: mapUrl.absoluteString, chatState: e.message.chatState, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: nil);
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: from.bareJid, state: state, authorNickname: authorNickname, recipientNickname: recipientNickname, type: .linkPreview, timestamp: timestamp, stanzaId: nil, data: mapUrl.absoluteString, chatState: e.message.chatState, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: nil);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,7 +182,14 @@ class MessageEventHandler: XmppServiceEventHandler {
|
|||
}
|
||||
}
|
||||
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: jid, state: state, type: type, timestamp: timestamp, stanzaId: e.message.id, data: body!, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: { (msgId) in
|
||||
var authorNickname: String? = nil;
|
||||
var recipientNickname: String? = nil;
|
||||
if let room = DBChatStore.instance.getChat(for: account, with: jid) as? DBRoom {
|
||||
// carbons should not copy PM messages
|
||||
return;
|
||||
}
|
||||
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: jid, state: state, authorNickname: authorNickname, recipientNickname: recipientNickname, type: type, timestamp: timestamp, stanzaId: e.message.id, data: body!, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: { (msgId) in
|
||||
if state.direction == .outgoing {
|
||||
DBChatHistoryStore.instance.markAsRead(for: account, with: jid, before: timestamp);
|
||||
}
|
||||
|
@ -181,12 +200,12 @@ class MessageEventHandler: XmppServiceEventHandler {
|
|||
let matches = detector.matches(in: body!, range: NSMakeRange(0, body!.utf16.count));
|
||||
matches.forEach { match in
|
||||
if let url = match.url, let scheme = url.scheme, ["https", "http"].contains(scheme) {
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: jid, state: state, type: .linkPreview, timestamp: timestamp, stanzaId: nil, data: url.absoluteString, chatState: e.message.chatState, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: nil);
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: jid, state: state, authorNickname: authorNickname, recipientNickname: recipientNickname, type: .linkPreview, timestamp: timestamp, stanzaId: nil, data: url.absoluteString, chatState: e.message.chatState, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: nil);
|
||||
}
|
||||
if let address = match.components {
|
||||
let query = address.values.joined(separator: ",").addingPercentEncoding(withAllowedCharacters: .urlHostAllowed);
|
||||
let mapUrl = URL(string: "http://maps.apple.com/?q=\(query!)")!;
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: jid, state: state, type: .linkPreview, timestamp: timestamp, stanzaId: nil, data: mapUrl.absoluteString, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: nil);
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: jid, state: state, authorNickname: authorNickname, recipientNickname: recipientNickname, type: .linkPreview, timestamp: timestamp, stanzaId: nil, data: mapUrl.absoluteString, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: nil);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -208,20 +227,46 @@ class MessageEventHandler: XmppServiceEventHandler {
|
|||
|
||||
let jid = account == from.bareJid ? to.bareJid : from.bareJid;
|
||||
let timestamp = e.timestamp!;
|
||||
let state: MessageState = calculateState(direction: account == from.bareJid ? .outgoing : .incoming, error: ((e.message.type ?? .chat) == .error), unread: false);
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: jid, state: state, type: type, timestamp: timestamp, stanzaId: e.message.id, data: body!, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: nil);
|
||||
var state: MessageState = calculateState(direction: account == from.bareJid ? .outgoing : .incoming, error: ((e.message.type ?? .chat) == .error), unread: false);
|
||||
|
||||
var authorNickname: String? = nil;
|
||||
var recipientNickname: String? = nil;
|
||||
if let room = DBChatStore.instance.getChat(for: account, with: from.bareJid) as? DBRoom {
|
||||
if room.nickname == from.resource {
|
||||
if state.isError {
|
||||
state = .incoming_error;
|
||||
} else {
|
||||
state = .incoming;
|
||||
}
|
||||
} else {
|
||||
if state.isError {
|
||||
state = .outgoing_error;
|
||||
} else {
|
||||
state = .outgoing;
|
||||
}
|
||||
}
|
||||
if state.direction == .incoming {
|
||||
authorNickname = from.resource;
|
||||
recipientNickname = room.nickname;
|
||||
} else {
|
||||
authorNickname = room.nickname;
|
||||
recipientNickname = e.message.to?.resource;
|
||||
}
|
||||
}
|
||||
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: jid, state: state, authorNickname: authorNickname, recipientNickname: recipientNickname, type: type, timestamp: timestamp, stanzaId: e.message.id, data: body!, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: nil);
|
||||
|
||||
if type == .message && !state.isError, #available(iOS 13.0, *) {
|
||||
let detector = try! NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue | NSTextCheckingResult.CheckingType.address.rawValue);
|
||||
let matches = detector.matches(in: body!, range: NSMakeRange(0, body!.utf16.count));
|
||||
matches.forEach { match in
|
||||
if let url = match.url, let scheme = url.scheme, ["https", "http"].contains(scheme) {
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: from.bareJid, state: state, type: .linkPreview, timestamp: timestamp, stanzaId: nil, data: url.absoluteString, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: nil);
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: from.bareJid, state: state, authorNickname: authorNickname, recipientNickname: recipientNickname, type: .linkPreview, timestamp: timestamp, stanzaId: nil, data: url.absoluteString, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: nil);
|
||||
}
|
||||
if let address = match.components {
|
||||
let query = address.values.joined(separator: ",").addingPercentEncoding(withAllowedCharacters: .urlHostAllowed);
|
||||
let mapUrl = URL(string: "http://maps.apple.com/?q=\(query!)")!;
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: from.bareJid, state: state, type: .linkPreview, timestamp: timestamp, stanzaId: nil, data: mapUrl.absoluteString, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: nil);
|
||||
DBChatHistoryStore.instance.appendItem(for: account, with: from.bareJid, state: state, authorNickname: authorNickname, recipientNickname: recipientNickname, type: .linkPreview, timestamp: timestamp, stanzaId: nil, data: mapUrl.absoluteString, errorCondition: e.message.errorCondition, errorMessage: e.message.errorText, encryption: encryption, encryptionFingerprint: fingerprint, completionHandler: nil);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ class MucEventHandler: XmppServiceEventHandler {
|
|||
static let ROOM_STATUS_CHANGED = Notification.Name("roomStatusChanged");
|
||||
static let ROOM_NAME_CHANGED = Notification.Name("roomNameChanged");
|
||||
static let ROOM_OCCUPANTS_CHANGED = Notification.Name("roomOccupantsChanged");
|
||||
|
||||
static let instance = MucEventHandler();
|
||||
|
||||
let events: [Event] = [ SessionEstablishmentModule.SessionEstablishmentSuccessEvent.TYPE, MucModule.YouJoinedEvent.TYPE, MucModule.RoomClosedEvent.TYPE, MucModule.MessageReceivedEvent.TYPE, MucModule.OccupantChangedNickEvent.TYPE, MucModule.OccupantChangedPresenceEvent.TYPE, MucModule.OccupantLeavedEvent.TYPE, MucModule.OccupantComesEvent.TYPE, MucModule.PresenceErrorEvent.TYPE, MucModule.InvitationReceivedEvent.TYPE, MucModule.InvitationDeclinedEvent.TYPE, PEPBookmarksModule.BookmarksChangedEvent.TYPE ];
|
||||
|
||||
|
@ -217,6 +219,12 @@ class MucEventHandler: XmppServiceEventHandler {
|
|||
}
|
||||
}
|
||||
|
||||
open func sendPrivateMessage(room: DBRoom, recipientNickname: String, body: String) {
|
||||
let message = room.createPrivateMessage(body, recipientNickname: recipientNickname);
|
||||
DBChatHistoryStore.instance.appendItem(for: room.account, with: room.roomJid, state: .outgoing, authorNickname: room.nickname, recipientNickname: recipientNickname, type: .message, timestamp: Date(), stanzaId: message.id, data: body, encryption: .none, encryptionFingerprint: nil, chatAttachmentAppendix: nil, completionHandler: nil);
|
||||
room.context.writer?.write(message);
|
||||
}
|
||||
|
||||
fileprivate func updateRoomName(room: DBRoom) {
|
||||
guard let client = XmppService.instance.getClient(for: room.account), let discoModule: DiscoveryModule = client.modulesManager.getModule(DiscoveryModule.ID) else {
|
||||
return;
|
||||
|
|
|
@ -48,9 +48,9 @@ open class XmppService: Logger, EventHandler {
|
|||
fileprivate var fetchStart = NSDate();
|
||||
|
||||
#if targetEnvironment(simulator)
|
||||
fileprivate let eventHandlers: [XmppServiceEventHandler] = [NewFeaturesDetector(), MessageEventHandler(), MucEventHandler(), PresenceRosterEventHandler(), AvatarEventHandler(), DiscoEventHandler(), PushEventHandler.instance, BlockedEventHandler.instance];
|
||||
fileprivate let eventHandlers: [XmppServiceEventHandler] = [NewFeaturesDetector(), MessageEventHandler(), MucEventHandler.instance, PresenceRosterEventHandler(), AvatarEventHandler(), DiscoEventHandler(), PushEventHandler.instance, BlockedEventHandler.instance];
|
||||
#else
|
||||
fileprivate let eventHandlers: [XmppServiceEventHandler] = [NewFeaturesDetector(), MessageEventHandler(), MucEventHandler(), PresenceRosterEventHandler(), AvatarEventHandler(), DiscoEventHandler(), PushEventHandler.instance, JingleManager.instance, BlockedEventHandler.instance];
|
||||
fileprivate let eventHandlers: [XmppServiceEventHandler] = [NewFeaturesDetector(), MessageEventHandler(), MucEventHandler.instance, PresenceRosterEventHandler(), AvatarEventHandler(), DiscoEventHandler(), PushEventHandler.instance, JingleManager.instance, BlockedEventHandler.instance];
|
||||
#endif
|
||||
|
||||
public let dbCapsCache: DBCapabilitiesCache;
|
||||
|
|
Loading…
Reference in a new issue