- Implemented Last Message Sync
This commit is contained in:
parent
be17e6bb46
commit
9b68d847e1
|
@ -24,7 +24,7 @@ import TigaseSwift
|
||||||
|
|
||||||
public class DBSchemaManager {
|
public class DBSchemaManager {
|
||||||
|
|
||||||
static let CURRENT_VERSION = 14;
|
static let CURRENT_VERSION = 15;
|
||||||
|
|
||||||
fileprivate let dbConnection: DBConnection;
|
fileprivate let dbConnection: DBConnection;
|
||||||
|
|
||||||
|
@ -69,32 +69,6 @@ public class DBSchemaManager {
|
||||||
try dbConnection.execute("ALTER TABLE chat_history ADD COLUMN error TEXT;");
|
try dbConnection.execute("ALTER TABLE chat_history ADD COLUMN error TEXT;");
|
||||||
}
|
}
|
||||||
|
|
||||||
// let queryStmt = try dbConnection.prepareStatement("SELECT account, jid, encryption FROM chats WHERE encryption IS NOT NULL AND options IS NULL");
|
|
||||||
// let toConvert = try queryStmt.query { (cursor) -> (BareJID, BareJID, ChatEncryption)? in
|
|
||||||
// let account: BareJID = cursor["account"]!;
|
|
||||||
// let jid: BareJID = cursor["jid"]!;
|
|
||||||
// guard let encryptionStr: String = cursor["encryption"] else {
|
|
||||||
// return nil;
|
|
||||||
// }
|
|
||||||
// guard let encryption = ChatEncryption(rawValue: encryptionStr) else {
|
|
||||||
// return nil;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return (account, jid, encryption);
|
|
||||||
// }
|
|
||||||
// if !toConvert.isEmpty {
|
|
||||||
// let updateStmt = try dbConnection.prepareStatement("UPDATE chats SET options = ?, encryption = null WHERE account = ? AND jid = ?");
|
|
||||||
// try toConvert.forEach { (arg0) in
|
|
||||||
// let (account, jid, encryption) = arg0
|
|
||||||
// var options = ChatOptions();
|
|
||||||
// options.encryption = encryption;
|
|
||||||
// let data = try? JSONEncoder().encode(options);
|
|
||||||
// let dataStr = data != nil ? String(data: data!, encoding: .utf8)! : nil;
|
|
||||||
// _ = try updateStmt.update(dataStr, account, jid);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
let toRemove: [(String,String,Int32)] = try dbConnection.prepareStatement("SELECT sess.account as account, sess.name as name, sess.device_id as deviceId FROM omemo_sessions sess WHERE NOT EXISTS (select 1 FROM omemo_identities i WHERE i.account = sess.account and i.name = sess.name and i.device_id = sess.device_id)").query([:] as [String: Any?], map: { (cursor:DBCursor) -> (String, String, Int32)? in
|
let toRemove: [(String,String,Int32)] = try dbConnection.prepareStatement("SELECT sess.account as account, sess.name as name, sess.device_id as deviceId FROM omemo_sessions sess WHERE NOT EXISTS (select 1 FROM omemo_identities i WHERE i.account = sess.account and i.name = sess.name and i.device_id = sess.device_id)").query([:] as [String: Any?], map: { (cursor:DBCursor) -> (String, String, Int32)? in
|
||||||
return (cursor["account"]!, cursor["name"]!, cursor["deviceId"]!);
|
return (cursor["account"]!, cursor["name"]!, cursor["deviceId"]!);
|
||||||
});
|
});
|
||||||
|
|
13
Shared/db-schema-15.sql
Normal file
13
Shared/db-schema-15.sql
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS last_message_sync (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
account TEXT NOT NULL COLLATE NOCASE,
|
||||||
|
jid TEXT NOT NULL COLLATE NOCASE,
|
||||||
|
received_id TEXT,
|
||||||
|
read_id TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
PRAGMA user_version = 15;
|
|
@ -7,6 +7,8 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
379D914A26E8A0E300B877CA /* db-schema-15.sql in Resources */ = {isa = PBXBuildFile; fileRef = 379D914926E8A0E300B877CA /* db-schema-15.sql */; };
|
||||||
|
379D914C26E8A29800B877CA /* DBLastMessageSyncStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379D914B26E8A29800B877CA /* DBLastMessageSyncStore.swift */; };
|
||||||
E928AD4326D6A08A00F29F93 /* db-schema-14.sql in Resources */ = {isa = PBXBuildFile; fileRef = E928AD4226D6A08A00F29F93 /* db-schema-14.sql */; };
|
E928AD4326D6A08A00F29F93 /* db-schema-14.sql in Resources */ = {isa = PBXBuildFile; fileRef = E928AD4226D6A08A00F29F93 /* db-schema-14.sql */; };
|
||||||
E95AA70226D38B6F00A38D44 /* DisplayNameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95AA70126D38B6E00A38D44 /* DisplayNameViewController.swift */; };
|
E95AA70226D38B6F00A38D44 /* DisplayNameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95AA70126D38B6E00A38D44 /* DisplayNameViewController.swift */; };
|
||||||
E963721026D786D000332482 /* BlockingCommandModuleExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E963720F26D786D000332482 /* BlockingCommandModuleExtension.swift */; };
|
E963721026D786D000332482 /* BlockingCommandModuleExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E963720F26D786D000332482 /* BlockingCommandModuleExtension.swift */; };
|
||||||
|
@ -295,6 +297,8 @@
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
379D914926E8A0E300B877CA /* db-schema-15.sql */ = {isa = PBXFileReference; lastKnownFileType = text; path = "db-schema-15.sql"; sourceTree = "<group>"; };
|
||||||
|
379D914B26E8A29800B877CA /* DBLastMessageSyncStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBLastMessageSyncStore.swift; sourceTree = "<group>"; };
|
||||||
E928AD4226D6A08A00F29F93 /* db-schema-14.sql */ = {isa = PBXFileReference; lastKnownFileType = text; path = "db-schema-14.sql"; sourceTree = "<group>"; };
|
E928AD4226D6A08A00F29F93 /* db-schema-14.sql */ = {isa = PBXFileReference; lastKnownFileType = text; path = "db-schema-14.sql"; sourceTree = "<group>"; };
|
||||||
E95AA70126D38B6E00A38D44 /* DisplayNameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayNameViewController.swift; sourceTree = "<group>"; };
|
E95AA70126D38B6E00A38D44 /* DisplayNameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayNameViewController.swift; sourceTree = "<group>"; };
|
||||||
E963720F26D786D000332482 /* BlockingCommandModuleExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockingCommandModuleExtension.swift; sourceTree = "<group>"; };
|
E963720F26D786D000332482 /* BlockingCommandModuleExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockingCommandModuleExtension.swift; sourceTree = "<group>"; };
|
||||||
|
@ -659,6 +663,7 @@
|
||||||
FEE49DCF2426485500900BBB /* DBConnection_main.swift */,
|
FEE49DCF2426485500900BBB /* DBConnection_main.swift */,
|
||||||
FE3E387B242766A900D3A8E8 /* DBChannelStore.swift */,
|
FE3E387B242766A900D3A8E8 /* DBChannelStore.swift */,
|
||||||
FEBC12F324C70E2900689475 /* DBChatHistorySyncStore.swift */,
|
FEBC12F324C70E2900689475 /* DBChatHistorySyncStore.swift */,
|
||||||
|
379D914B26E8A29800B877CA /* DBLastMessageSyncStore.swift */,
|
||||||
);
|
);
|
||||||
path = database;
|
path = database;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -805,6 +810,7 @@
|
||||||
FE3BA0BF24B61583000C80D4 /* db-schema-12.sql */,
|
FE3BA0BF24B61583000C80D4 /* db-schema-12.sql */,
|
||||||
FEBC12F124C70DE000689475 /* db-schema-13.sql */,
|
FEBC12F124C70DE000689475 /* db-schema-13.sql */,
|
||||||
E928AD4226D6A08A00F29F93 /* db-schema-14.sql */,
|
E928AD4226D6A08A00F29F93 /* db-schema-14.sql */,
|
||||||
|
379D914926E8A0E300B877CA /* db-schema-15.sql */,
|
||||||
);
|
);
|
||||||
path = Shared;
|
path = Shared;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1183,6 +1189,7 @@
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
379D914A26E8A0E300B877CA /* db-schema-15.sql in Resources */,
|
||||||
FE10BCF323FD4EF000E214F3 /* db-schema-10.sql in Resources */,
|
FE10BCF323FD4EF000E214F3 /* db-schema-10.sql in Resources */,
|
||||||
FE759FF22371F21C001E78D9 /* db-schema-6.sql in Resources */,
|
FE759FF22371F21C001E78D9 /* db-schema-6.sql in Resources */,
|
||||||
FE759FEF2371F21C001E78D9 /* db-schema-3.sql in Resources */,
|
FE759FEF2371F21C001E78D9 /* db-schema-3.sql in Resources */,
|
||||||
|
@ -1415,6 +1422,7 @@
|
||||||
FE0E30F12535BB530030F8C5 /* BaseChatViewController+ShareFile.swift in Sources */,
|
FE0E30F12535BB530030F8C5 /* BaseChatViewController+ShareFile.swift in Sources */,
|
||||||
FE507A251CDB7B3B001A015C /* AccountManager.swift in Sources */,
|
FE507A251CDB7B3B001A015C /* AccountManager.swift in Sources */,
|
||||||
FEC79195241ABEF4007BE572 /* MessageState.swift in Sources */,
|
FEC79195241ABEF4007BE572 /* MessageState.swift in Sources */,
|
||||||
|
379D914C26E8A29800B877CA /* DBLastMessageSyncStore.swift in Sources */,
|
||||||
FE719E7C22730DC3007CEEC9 /* OMEMOIdentityTableViewCell.swift in Sources */,
|
FE719E7C22730DC3007CEEC9 /* OMEMOIdentityTableViewCell.swift in Sources */,
|
||||||
FEDE93901D09BB8300CA60A9 /* VCardEditPhoneTableViewCell.swift in Sources */,
|
FEDE93901D09BB8300CA60A9 /* VCardEditPhoneTableViewCell.swift in Sources */,
|
||||||
FE507A221CDB7B3B001A015C /* AvatarStatusView.swift in Sources */,
|
FE507A221CDB7B3B001A015C /* AvatarStatusView.swift in Sources */,
|
||||||
|
|
|
@ -450,7 +450,6 @@ public class DBChatHistoryStore {
|
||||||
let uuid = UUID();
|
let uuid = UUID();
|
||||||
previewsInProgress[masterId] = uuid;
|
previewsInProgress[masterId] = uuid;
|
||||||
previewGenerationDispatcher.async {
|
previewGenerationDispatcher.async {
|
||||||
print("generating previews for master id:", masterId, "uuid:", uuid);
|
|
||||||
// if we may have previews, we should add them here..
|
// if we may have previews, we should add them here..
|
||||||
if let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) {
|
if let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) {
|
||||||
let matches = detector.matches(in: data, range: NSMakeRange(0, data.utf16.count));
|
let matches = detector.matches(in: data, range: NSMakeRange(0, data.utf16.count));
|
||||||
|
@ -464,7 +463,6 @@ public class DBChatHistoryStore {
|
||||||
}) else {
|
}) else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
print("adding previews for master id:", masterId, "uuid:", uuid);
|
|
||||||
matches.forEach { match in
|
matches.forEach { match in
|
||||||
if let url = match.url, let scheme = url.scheme, ["https", "http"].contains(scheme) {
|
if let url = match.url, let scheme = url.scheme, ["https", "http"].contains(scheme) {
|
||||||
if (data as NSString).range(of: "http", options: .caseInsensitive, range: match.range).location == match.range.location {
|
if (data as NSString).range(of: "http", options: .caseInsensitive, range: match.range).location == match.range.location {
|
||||||
|
|
79
Snikket/database/DBLastMessageSyncStore.swift
Normal file
79
Snikket/database/DBLastMessageSyncStore.swift
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
//
|
||||||
|
// DBLastMessageSyncStore.swift
|
||||||
|
// Snikket
|
||||||
|
//
|
||||||
|
// Created by Hammad Ashraf on 08/09/2021.
|
||||||
|
// Copyright © 2021 Snikket. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import TigaseSwift
|
||||||
|
import Shared
|
||||||
|
|
||||||
|
// Table last_message_sync :-
|
||||||
|
// account TEXT
|
||||||
|
// jid TEXT
|
||||||
|
// received_id TEXT
|
||||||
|
// read_id TEXT
|
||||||
|
|
||||||
|
class DBLastMessageSyncStore {
|
||||||
|
|
||||||
|
static let instance = DBLastMessageSyncStore()
|
||||||
|
|
||||||
|
fileprivate let dispatcher: QueueDispatcher;
|
||||||
|
|
||||||
|
private let insertLastMessage: DBStatement
|
||||||
|
private let getLastMessage: DBStatement
|
||||||
|
private let updateLastMessage: DBStatement
|
||||||
|
|
||||||
|
private init() {
|
||||||
|
insertLastMessage = try! DBConnection.main.prepareStatement("INSERT INTO last_message_sync (account, jid, received_id, read_id) VALUES (:account, :jid, :received_id, :read_id)")
|
||||||
|
|
||||||
|
updateLastMessage = try! DBConnection.main.prepareStatement("UPDATE last_message_sync SET received_id = :received_id, read_id = :read_id WHERE account = :account AND jid = :jid")
|
||||||
|
|
||||||
|
getLastMessage = try! DBConnection.main.prepareStatement("SELECT account, jid, received_id, read_id FROM last_message_sync WHERE account = :account AND jid = :jid")
|
||||||
|
|
||||||
|
dispatcher = QueueDispatcher(label: "last_message_sync")
|
||||||
|
}
|
||||||
|
|
||||||
|
func insertmessage(account: BareJID, jid: BareJID, receivedId: String?, readId: String?) {
|
||||||
|
let params: [String:Any?] = ["account":account, "jid":jid, "received_id": receivedId, "read_id":readId]
|
||||||
|
dispatcher.async {
|
||||||
|
_ = try! self.insertLastMessage.insert(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateMessage(account: BareJID, jid: BareJID, receivedId: String?, readId: String?) {
|
||||||
|
let params: [String:Any?] = ["account":account, "jid":jid, "received_id": receivedId, "read_id":readId]
|
||||||
|
dispatcher.async {
|
||||||
|
_ = try! self.updateLastMessage.update(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLastMessage(account: BareJID, jid: BareJID) -> LastMessageSync? {
|
||||||
|
let params: [String:Any?] = ["account":account, "jid":jid]
|
||||||
|
let message = try! self.getLastMessage.queryFirstMatching(params) { (cursor) -> LastMessageSync? in
|
||||||
|
return LastMessageSync(account: account, jid: jid, receivedId: cursor["received_id"], readId: cursor["read_id"])
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateOrInsertLastMessage(account: BareJID, jid: BareJID, receivedId: String?, readId: String?) {
|
||||||
|
dispatcher.async {
|
||||||
|
let lastMessage = self.getLastMessage(account: account, jid: jid)
|
||||||
|
if lastMessage != nil, receivedId != nil {
|
||||||
|
self.updateMessage(account: account, jid: jid, receivedId: receivedId, readId: readId)
|
||||||
|
}
|
||||||
|
else if lastMessage == nil {
|
||||||
|
self.insertmessage(account: account, jid: jid, receivedId: receivedId, readId: readId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LastMessageSync {
|
||||||
|
var account : BareJID
|
||||||
|
var jid : BareJID
|
||||||
|
var receivedId : String?
|
||||||
|
var readId : String?
|
||||||
|
}
|
|
@ -362,6 +362,30 @@ class MessageEventHandler: XmppServiceEventHandler {
|
||||||
syncMessages(for: account, since: syncMessagesSince);
|
syncMessages(for: account, since: syncMessagesSince);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func syncMessagesAfterID(for account: BareJID, version: MessageArchiveManagementModule.Version?, componentJID: JID, afterId: String) {
|
||||||
|
guard let client = XmppService.instance.getClient(for: account), let mamModule: MessageArchiveManagementModule = client.modulesManager.getModule(MessageArchiveManagementModule.ID) else { return }
|
||||||
|
|
||||||
|
let queryId = UUID().uuidString
|
||||||
|
|
||||||
|
let query = JabberDataElement(type: .submit)
|
||||||
|
let formTypeField = HiddenField(name: "FORM_TYPE")
|
||||||
|
formTypeField.value = version?.rawValue
|
||||||
|
query.addField(formTypeField)
|
||||||
|
|
||||||
|
let withField = JidSingleField(name: "with")
|
||||||
|
withField.value = componentJID
|
||||||
|
query.addField(withField)
|
||||||
|
|
||||||
|
let startField = TextSingleField(name: "after-id")
|
||||||
|
startField.value = afterId
|
||||||
|
query.addField(startField)
|
||||||
|
|
||||||
|
mamModule.queryItems(version: version, componentJid: componentJID, node: nil, query: query, queryId: queryId, rsm: RSM.Query(after:afterId ,max: 150)) { responseStanza in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static func syncMessages(for account: BareJID, version: MessageArchiveManagementModule.Version? = nil, componentJID: JID? = nil, since: Date, rsmQuery: RSM.Query? = nil) {
|
static func syncMessages(for account: BareJID, version: MessageArchiveManagementModule.Version? = nil, componentJID: JID? = nil, since: Date, rsmQuery: RSM.Query? = nil) {
|
||||||
let period = DBChatHistorySyncStore.Period(account: account, component: componentJID?.bareJid, from: since, after: nil);
|
let period = DBChatHistorySyncStore.Period(account: account, component: componentJID?.bareJid, from: since, after: nil);
|
||||||
|
|
|
@ -54,13 +54,21 @@ class MucEventHandler: XmppServiceEventHandler {
|
||||||
room.supportedFeatures = [];
|
room.supportedFeatures = [];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
let account = e.sessionObject.userBareJid!;
|
||||||
if let timestamp = room.lastMessageDate, !mamVersions.isEmpty {
|
if let timestamp = room.lastMessageDate, !mamVersions.isEmpty {
|
||||||
let account = e.sessionObject.userBareJid!;
|
|
||||||
room.onRoomJoined = { room in
|
room.onRoomJoined = { room in
|
||||||
MessageEventHandler.syncMessages(for: account, version: mamVersions.contains(.MAM2) ? .MAM2 : .MAM1, componentJID: room.jid, since: timestamp);
|
MessageEventHandler.syncMessages(for: account, version: mamVersions.contains(.MAM2) ? .MAM2 : .MAM1, componentJID: room.jid, since: timestamp);
|
||||||
}
|
}
|
||||||
_ = room.rejoin(skipHistoryFetch: true);
|
_ = room.rejoin(skipHistoryFetch: true);
|
||||||
NotificationCenter.default.post(name: MucEventHandler.ROOM_STATUS_CHANGED, object: room);
|
NotificationCenter.default.post(name: MucEventHandler.ROOM_STATUS_CHANGED, object: room);
|
||||||
|
} else if let lastMessageRecieved = DBLastMessageSyncStore.instance.getLastMessage(account: account, jid: room.roomJid), let afterId = lastMessageRecieved.receivedId {
|
||||||
|
|
||||||
|
room.onRoomJoined = { room in
|
||||||
|
MessageEventHandler.syncMessagesAfterID(for: account, version: mamVersions.contains(.MAM2) ? .MAM2 : .MAM1, componentJID: room.jid, afterId: afterId)
|
||||||
|
}
|
||||||
|
room.lastMessageDate = Date()
|
||||||
|
_ = room.rejoin(skipHistoryFetch: true);
|
||||||
|
NotificationCenter.default.post(name: MucEventHandler.ROOM_STATUS_CHANGED, object: room);
|
||||||
} else {
|
} else {
|
||||||
_ = room.rejoin();
|
_ = room.rejoin();
|
||||||
NotificationCenter.default.post(name: MucEventHandler.ROOM_STATUS_CHANGED, object: room);
|
NotificationCenter.default.post(name: MucEventHandler.ROOM_STATUS_CHANGED, object: room);
|
||||||
|
@ -101,7 +109,9 @@ class MucEventHandler: XmppServiceEventHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DBChatHistoryStore.instance.append(for: room.account, message: e.message, source: .stream);
|
DBChatHistoryStore.instance.append(for: room.account, message: e.message, source: .stream)
|
||||||
|
|
||||||
|
DBLastMessageSyncStore.instance.updateOrInsertLastMessage(account: room.account, jid: room.roomJid, receivedId: e.message.id, readId: nil)
|
||||||
case let e as MucModule.AbstractOccupantEvent:
|
case let e as MucModule.AbstractOccupantEvent:
|
||||||
if let room = e.room as? DBRoom, room.isOMEMOCapable, let jid = e.occupant.jid {
|
if let room = e.room as? DBRoom, room.isOMEMOCapable, let jid = e.occupant.jid {
|
||||||
if (e.occupant.affiliation == .none || e.occupant.affiliation == .outcast) {
|
if (e.occupant.affiliation == .none || e.occupant.affiliation == .outcast) {
|
||||||
|
|
Loading…
Reference in a new issue