git » sdk » commit 0f601cd

Initial searchMessages function for browser persistence layer

author Stephen Paul Weber
2024-09-24 02:56:07 UTC
committer Stephen Paul Weber
2024-09-24 02:56:07 UTC
parent 75543e6a5bcded1cf5c9a53c4b41583125fc1b70

Initial searchMessages function for browser persistence layer

snikket/persistence/browser.js +53 -5

diff --git a/snikket/persistence/browser.js b/snikket/persistence/browser.js
index 51cdaea..acea1f3 100644
--- a/snikket/persistence/browser.js
+++ b/snikket/persistence/browser.js
@@ -4,7 +4,10 @@
 import { snikket as enums } from "./snikket-enums";
 import { snikket } from "./snikket";
 
-const browser = (dbname) => {
+const browser = (dbname, tokenize, stemmer) => {
+	if (!tokenize) tokenize = function(s) { return s.split(" "); }
+	if (!stemmer) stemmer = function(s) { return s; }
+
 	var db = null;
 	function openDb(version) {
 		var dbOpenReq = indexedDB.open(dbname, version);
@@ -57,12 +60,11 @@ const browser = (dbname) => {
 		});
 	}
 
-	async function hydrateMessage(value) {
+	function hydrateMessageSync(value) {
 		if (!value) return null;
 
 		const tx = db.transaction(["messages"], "readonly");
 		const store = tx.objectStore("messages");
-		let replyToMessage = value.replyToMessage && await hydrateMessage((await promisifyRequest(store.openCursor(IDBKeyRange.only(value.replyToMessage))))?.value);
 
 		const message = new snikket.ChatMessage();
 		message.localId = value.localId ? value.localId : null;
@@ -77,18 +79,29 @@ const browser = (dbname) => {
 		message.sender = value.sender && snikket.JID.parse(value.sender);
 		message.recipients = value.recipients.map((r) => snikket.JID.parse(r));
 		message.replyTo = value.replyTo.map((r) => snikket.JID.parse(r));
-		message.replyToMessage = replyToMessage;
 		message.threadId = value.threadId;
 		message.attachments = value.attachments;
 		message.reactions = value.reactions;
 		message.text = value.text;
 		message.lang = value.lang;
 		message.isGroupchat = value.isGroupchat || value.groupchat;
-		message.versions = await Promise.all((value.versions || []).map(hydrateMessage));
 		message.payloads = (value.payloads || []).map(snikket.Stanza.parse);
 		return message;
 	}
 
+	async function hydrateMessage(value) {
+		if (!value) return null;
+
+		const message = hydrateMessageSync(value);
+		const tx = db.transaction(["messages"], "readonly");
+		const store = tx.objectStore("messages");
+		const replyToMessage = value.replyToMessage && await hydrateMessage((await promisifyRequest(store.openCursor(IDBKeyRange.only(value.replyToMessage))))?.value);
+
+		message.replyToMessage = replyToMessage;
+		message.versions = await Promise.all((value.versions || []).map(hydrateMessage));
+		return message;
+	}
+
 	function serializeMessage(account, message) {
 		return {
 			...message,
@@ -391,6 +404,41 @@ const browser = (dbname) => {
 			}
 		},
 
+		searchMessages: function(account, chatId, q, callback) {
+			const tx = db.transaction(["messages"], "readonly");
+			const store = tx.objectStore("messages");
+			var cursor;
+			if (chatId) {
+				cursor = store.index("chats").openCursor(
+					IDBKeyRange.bound([account, chatId], [account, chatId, []]),
+					"prev"
+				);
+			} else if (account) {
+				cursor = store.index("accounts").openCursor(
+					IDBKeyRange.bound([account], [account, []]),
+					"prev"
+				);
+			} else {
+				cursor = store.openCursor(undefined, "prev");
+			}
+			const qTok = new Set(tokenize(q).map(stemmer));
+			cursor.onsuccess = (event) => {
+				if (event.target.result) {
+					const value = event.target.result.value;
+					if (new Set(tokenize(value.text).map(stemmer)).isSupersetOf(qTok)) {
+						if (!callback(q, hydrateMessageSync(value))) return;
+					}
+					event.target.result.continue();
+				} else {
+					callback(null);
+				}
+			}
+			cursor.onerror = (event) => {
+				console.error(event);
+				callback(null);
+			}
+		},
+
 		getMediaUri: function(hashAlgorithm, hash, callback) {
 			(async function() {
 				var niUrl;