FEATURE: Decrypt bookmark titles (#119)

FEATURE: Decrypt bookmark titles (#119)

This commit also implements search in encrypted bookmarks and improves existent encrypted topic search.

diff --git a/assets/javascripts/discourse/initializers/add-search-results.js b/assets/javascripts/discourse/initializers/add-search-results.js
index 828a447..3550901 100644
--- a/assets/javascripts/discourse/initializers/add-search-results.js
+++ b/assets/javascripts/discourse/initializers/add-search-results.js
@@ -6,12 +6,10 @@ import Topic from "discourse/models/topic";
 import {
   ENCRYPT_ACTIVE,
   getEncryptionStatus,
-  getIdentity,
+  getTopicTitle,
+  putTopicKey,
+  putTopicTitle,
 } from "discourse/plugins/discourse-encrypt/lib/discourse";
-import {
-  decrypt,
-  importKey,
-} from "discourse/plugins/discourse-encrypt/lib/protocol";
 import I18n from "I18n";
 import { Promise } from "rsvp";
 
@@ -46,13 +44,15 @@ function getOrFetchCache(session) {
       });
 
       result.topics.forEach((topic) => {
+        putTopicKey(topic.id, topic.topic_key);
+        putTopicTitle(topic.id, topic.encrypted_title);
+
         promises.push(
-          getIdentity()
-            .then((id) => importKey(topic.topic_key, id.encryptPrivate))
-            .then((key) => decrypt(key, topic.encrypted_title))
-            .then((decrypted) => {
-              topic.title = decrypted.raw;
-              topic.fancy_title = `${iconHTML("user-secret")} ${decrypted.raw}`;
+          getTopicTitle(topic.id)
+            .then((title) => {
+              topic.title = title;
+              topic.fancy_title = `${iconHTML("user-secret")} ${title}`;
+              topic.excerpt = null;
               addCacheItem(session, "topics", topic);
             })
             .catch(() => {})
diff --git a/assets/javascripts/discourse/initializers/decrypt-topics.js b/assets/javascripts/discourse/initializers/decrypt-topics.js
index f621840..e3b595c 100644
--- a/assets/javascripts/discourse/initializers/decrypt-topics.js
+++ b/assets/javascripts/discourse/initializers/decrypt-topics.js
@@ -168,22 +168,20 @@ export default {
   },
 
   decryptTitles() {
-    decryptElements("a.topic-link[data-topic-id]", "span");
-    decryptElements("a.topic-link[data-topic-id]", null, { addIcon: true });
-    decryptElements("a.raw-topic-link[data-topic-id]", null, { addIcon: true });
+    decryptElements("a.raw-topic-link", null, { addIcon: true });
+    decryptElements("a.topic-link", "span");
+    decryptElements("a.topic-link", null, { addIcon: true });
 
     // Title in site header
     decryptElements("h1.header-title", ".topic-link", { replaceIcon: true });
 
     // Title in topic header
-    decryptElements("h1[data-topic-id]", ".fancy-title", { replaceIcon: true });
+    decryptElements("h1", ".fancy-title", { replaceIcon: true });
 
     // Title in topic lists
-    decryptElements(
-      ".topic-list-item[data-topic-id], .latest-topic-list-item[data-topic-id]",
-      ".title",
-      { addIcon: true }
-    );
+    decryptElements(".topic-list-item, .latest-topic-list-item", ".title", {
+      addIcon: true,
+    });
   },
 
   decryptDocTitle(data) {
diff --git a/assets/javascripts/discourse/initializers/fetch-encrypt-keys.js b/assets/javascripts/discourse/initializers/fetch-encrypt-keys.js
index 67f4047..0e3dab7 100644
--- a/assets/javascripts/discourse/initializers/fetch-encrypt-keys.js
+++ b/assets/javascripts/discourse/initializers/fetch-encrypt-keys.js
@@ -1,12 +1,60 @@
+import { iconHTML } from "discourse-common/lib/icon-library";
 import NotificationAdapter from "discourse/adapters/notification";
+import { ajax } from "discourse/lib/ajax";
+import PreloadStore from "discourse/lib/preload-store";
+import Bookmark from "discourse/models/bookmark";
 import Topic from "discourse/models/topic";
 import {
   ENCRYPT_DISABLED,
   getEncryptionStatus,
+  getTopicTitle,
   putTopicKey,
   putTopicTitle,
 } from "discourse/plugins/discourse-encrypt/lib/discourse";
-import PreloadStore from "discourse/lib/preload-store";
+import { Promise } from "rsvp";
+
+const CACHE_KEY = "discourse-encrypt-bookmark-cache";
+
+function saveBookmarksResponse(session, response) {
+  if (!response?.user_bookmark_list?.bookmarks) {
+    return Promise.resolve();
+  }
+
+  const cacheObj = {};
+
+  // Keep current cache values
+  let cache = session.get(CACHE_KEY);
+  if (cache) {
+    cache.forEach((bookmark) => {
+      cacheObj[bookmark.id] = bookmark;
+    });
+  }
+
+  const promises = [];
+  response?.user_bookmark_list?.bookmarks?.forEach((bookmark) => {
+    if (!bookmark.topic_key) {
+      return;
+    }
+
+    putTopicKey(bookmark.topic_id, bookmark.topic_key);
+    putTopicTitle(bookmark.topic_id, bookmark.encrypted_title);
+
+    promises.push(
+      getTopicTitle(bookmark.topic_id)
+        .then((title) => {
+          bookmark.title = title;
+          bookmark.fancy_title = `${iconHTML("user-secret")} ${title}`;
+          bookmark.excerpt = null;
+          cacheObj[bookmark.id] = bookmark;
+        })
+        .catch(() => {})
+    );
+  });
+
+  return Promise.all(promises).then(() => {
+    session.set(CACHE_KEY, Object.values(cacheObj));
+  });
+}
 
 export default {
   name: "fetch-encrypt-keys",
@@ -18,7 +66,9 @@ export default {
       return;
     }
 
-    // Go through the `PreloadStore` and look for any preloaded topic keys.
+    const session = container.lookup("session:main");
+
+    // Go through the `PreloadStore` and look for preloaded topic keys
     for (let storeKey in PreloadStore.data) {
       if (storeKey.includes("topic_")) {
         const topic = PreloadStore.data[storeKey];
@@ -36,7 +86,6 @@ export default {
       }
     }
 
-    // Hook `Notification` adapter to gather encrypted topic keys.
     NotificationAdapter.reopen({
       find() {
         return this._super(...arguments).then((result) => {
@@ -54,7 +103,6 @@ export default {
       },
     });
 
-    // Hook `Topic` model to gather encrypted topic keys.
     Topic.reopenClass({
       create(args) {
         if (args && args.topic_key) {
@@ -74,5 +122,40 @@ export default {
         return this._super(...arguments);
       },
     });
+
+    Bookmark.reopen({
+      loadItems(params) {
+        return this._super(...arguments).then((response) => {
+          if (!params.q) {
+            saveBookmarksResponse(session, response);
+            return response;
+          }
+
+          let cachePromise = Promise.resolve();
+          if (!session.get(CACHE_KEY)) {
+            const url = `/u/${currentUser.username}/bookmarks.json`;
+            cachePromise = ajax(url).then((resp) =>
+              saveBookmarksResponse(session, resp)
+            );
+          }
+
+          return cachePromise.then(() => {
+            const cache = session.get(CACHE_KEY);
+            cache.forEach((bookmark) => {
+              if (
+                bookmark.title.toLowerCase().includes(params.q.toLowerCase())
+              ) {
+                if (!response?.user_bookmark_list?.bookmarks) {
+                  response = { user_bookmark_list: { bookmarks: [] } };
+                }
+
+                response.user_bookmark_list.bookmarks.push(bookmark);
+              }
+            });
+            return response;
+          });
+        });
+      },
+    });
   },
 };
diff --git a/plugin.rb b/plugin.rb
index ab95e3f..0112278 100644
--- a/plugin.rb
+++ b/plugin.rb
@@ -91,10 +91,22 @@ after_initialize do
     end
   end
 
+  BookmarkQuery.on_preload do |bookmarks, query|
+    if SiteSetting.encrypt_enabled? && bookmarks.size > 0
+      user_id = bookmarks.first.user_id
+      topic_ids = bookmarks.map(&:topic_id)
+
+      encrypted_topics_data = EncryptedTopicsData.where(topic_id: topic_ids).index_by(&:topic_id)
+      encrypted_topics_users = EncryptedTopicsUser.where(user_id: user_id, topic_id: topic_ids).index_by(&:topic_id)
+
+      bookmarks.each do |bookmark|
+        bookmark.topic.association(:encrypted_topics_data).target = encrypted_topics_data[bookmark.topic_id]
+        bookmark.topic.association(:encrypted_topics_users).target = [encrypted_topics_users[bookmark.topic_id]].compact

[... diff too long, it was truncated ...]

GitHub sha: 241ce5024ea85fb814898cd8b8f2417cb2cb87dd

This commit appears in #119 which was approved by ZogStriP. It was merged by udan11.