FEATURE: Add search operator to see all direct messages from a user (#7913)

FEATURE: Add search operator to see all direct messages from a user (#7913)

  • FEATURE: Add search operator to see all direct messages from a user

  • Only show message if related messages >= 5

  • Make “all messages” the hyperlink

  • Review

diff --git a/app/assets/javascripts/discourse/components/related-messages.js.es6 b/app/assets/javascripts/discourse/components/related-messages.js.es6
index 807359d..e7d65a4 100644
--- a/app/assets/javascripts/discourse/components/related-messages.js.es6
+++ b/app/assets/javascripts/discourse/components/related-messages.js.es6
@@ -6,6 +6,30 @@ export default Ember.Component.extend({
   classNames: ["suggested-topics"],
 
   @computed("topic")
+  targetUser(topic) {
+    if (!topic || !topic.isPrivateMessage) {
+      return;
+    }
+    const allowedUsers = topic.details.allowed_users;
+    if (
+      topic.relatedMessages &&
+      topic.relatedMessages.length >= 5 &&
+      allowedUsers.length === 2 &&
+      topic.details.allowed_groups.length === 0 &&
+      allowedUsers.find(u => u.username === this.currentUser.username)
+    ) {
+      return allowedUsers.find(u => u.username !== this.currentUser.username);
+    }
+  },
+
+  @computed
+  searchLink() {
+    return Discourse.getURL(
+      `/search?expanded=true&q=%40${this.targetUser.username}%20in%3Apersonal-direct`
+    );
+  },
+
+  @computed("topic")
   relatedTitle(topic) {
     const href = this.currentUser && this.currentUser.pmPath(topic);
     return href
diff --git a/app/assets/javascripts/discourse/components/search-advanced-options.js.es6 b/app/assets/javascripts/discourse/components/search-advanced-options.js.es6
index 75b2e54..50da93e 100644
--- a/app/assets/javascripts/discourse/components/search-advanced-options.js.es6
+++ b/app/assets/javascripts/discourse/components/search-advanced-options.js.es6
@@ -19,7 +19,7 @@ const REGEXP_TAGS_REPLACE = /(^(tags?:|#(?=[a-z0-9\-]+::tag))|::tag\s?$)/gi;
 const REGEXP_IN_MATCH = /^(in|with):(posted|watching|tracking|bookmarks|first|pinned|unpinned|wiki|unseen|image)/gi;
 const REGEXP_SPECIAL_IN_LIKES_MATCH = /^in:likes/gi;
 const REGEXP_SPECIAL_IN_TITLE_MATCH = /^in:title/gi;
-const REGEXP_SPECIAL_IN_PRIVATE_MATCH = /^in:private/gi;
+const REGEXP_SPECIAL_IN_PERSONAL_MATCH = /^in:personal/gi;
 const REGEXP_SPECIAL_IN_SEEN_MATCH = /^in:seen/gi;
 
 const REGEXP_CATEGORY_SLUG = /^(\#[a-zA-Z0-9\-:]+)/gi;
@@ -93,7 +93,7 @@ export default Ember.Component.extend({
           in: {
             title: false,
             likes: false,
-            private: false,
+            personal: false,
             seen: false
           },
           all_tags: false
@@ -140,8 +140,8 @@ export default Ember.Component.extend({
     );
 
     this.setSearchedTermSpecialInValue(
-      "searchedTerms.special.in.private",
-      REGEXP_SPECIAL_IN_PRIVATE_MATCH
+      "searchedTerms.special.in.personal",
+      REGEXP_SPECIAL_IN_PERSONAL_MATCH
     );
 
     this.setSearchedTermSpecialInValue(
@@ -512,9 +512,9 @@ export default Ember.Component.extend({
     this.updateInRegex(REGEXP_SPECIAL_IN_LIKES_MATCH, "likes");
   },
 
-  @observes("searchedTerms.special.in.private")
-  updateSearchTermForSpecialInPrivate() {
-    this.updateInRegex(REGEXP_SPECIAL_IN_PRIVATE_MATCH, "private");
+  @observes("searchedTerms.special.in.personal")
+  updateSearchTermForSpecialInPersonal() {
+    this.updateInRegex(REGEXP_SPECIAL_IN_PERSONAL_MATCH, "personal");
   },
 
   @observes("searchedTerms.special.in.seen")
diff --git a/app/assets/javascripts/discourse/controllers/full-page-search.js.es6 b/app/assets/javascripts/discourse/controllers/full-page-search.js.es6
index af832d4..c7175a4 100644
--- a/app/assets/javascripts/discourse/controllers/full-page-search.js.es6
+++ b/app/assets/javascripts/discourse/controllers/full-page-search.js.es6
@@ -161,9 +161,9 @@ export default Ember.Controller.extend({
     return (
       q &&
       this.currentUser &&
-      (q.indexOf("in:private") > -1 ||
+      (q.indexOf("in:personal") > -1 ||
         q.indexOf(
-          `private_messages:${this.currentUser.get("username_lower")}`
+          `personal_messages:${this.currentUser.get("username_lower")}`
         ) > -1)
     );
   },
diff --git a/app/assets/javascripts/discourse/templates/components/related-messages.hbs b/app/assets/javascripts/discourse/templates/components/related-messages.hbs
index 41c98c7..dde8dea 100644
--- a/app/assets/javascripts/discourse/templates/components/related-messages.hbs
+++ b/app/assets/javascripts/discourse/templates/components/related-messages.hbs
@@ -5,3 +5,7 @@
   showPosters="true"
   topics=topic.relatedMessages}}
 </div>
+
+{{#if targetUser}}
+  <h3 class="see-all-pms-message">{{{i18n "related_messages.see_all" path=searchLink username=targetUser.username}}}</h3>
+{{/if}}
diff --git a/app/assets/javascripts/discourse/templates/components/search-advanced-options.hbs b/app/assets/javascripts/discourse/templates/components/search-advanced-options.hbs
index 371a1c3..b1d8019 100644
--- a/app/assets/javascripts/discourse/templates/components/search-advanced-options.hbs
+++ b/app/assets/javascripts/discourse/templates/components/search-advanced-options.hbs
@@ -62,7 +62,7 @@
         <section class='field'>
           <label>{{input type="checkbox" class="in-title" checked=searchedTerms.special.in.title}} {{i18n "search.advanced.filters.title"}}</label>
           <label>{{input type="checkbox" class="in-likes" checked=searchedTerms.special.in.likes}} {{i18n "search.advanced.filters.likes"}}</label>
-          <label>{{input type="checkbox" class="in-private" checked=searchedTerms.special.in.private}} {{i18n "search.advanced.filters.private"}}</label>
+          <label>{{input type="checkbox" class="in-private" checked=searchedTerms.special.in.personal}} {{i18n "search.advanced.filters.private"}}</label>
           <label>{{input type="checkbox" class="in-seen" checked=searchedTerms.special.in.seen}} {{i18n "search.advanced.filters.seen"}}</label>
         </section>
       {{/if}}
diff --git a/app/assets/javascripts/discourse/widgets/search-menu.js.es6 b/app/assets/javascripts/discourse/widgets/search-menu.js.es6
index 4792f02..808f328 100644
--- a/app/assets/javascripts/discourse/widgets/search-menu.js.es6
+++ b/app/assets/javascripts/discourse/widgets/search-menu.js.es6
@@ -114,7 +114,7 @@ export default createWidget("search-menu", {
             this.currentUser.get("username_lower") &&
           type === "private_messages"
         ) {
-          query += " in:private";
+          query += " in:personal";
         } else {
           query += encodeURIComponent(" " + type + ":" + ctx.id);
         }
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 3b3370d..23c624d 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -262,6 +262,7 @@ en:
 
     related_messages:
       title: "Related Messages"
+      see_all: "See <a href=\"%{path}\">all messages</a> from @%{username}..."
 
     suggested_topics:
       title: "Suggested Topics"
diff --git a/lib/search.rb b/lib/search.rb
index 08512e3..a109f48 100644
--- a/lib/search.rb
+++ b/lib/search.rb
@@ -265,6 +265,27 @@ class Search
     @advanced_filters
   end
 
+  advanced_filter(/^in:personal-direct$/) do |posts|
+    if @guardian.user
+      posts
+        .joins("LEFT JOIN topic_allowed_groups tg ON posts.topic_id = tg.topic_id")
+        .where(<<~SQL, user_id: @guardian.user.id)
+          tg.id IS NULL
+          AND posts.topic_id IN (
+            SELECT tau.topic_id
+            FROM topic_allowed_users tau
+            JOIN topic_allowed_users tau2
+            ON tau2.topic_id = tau.topic_id
+            AND tau2.id != tau.id
+            WHERE tau.user_id = :user_id
+            AND tau.topic_id = posts.topic_id
+            GROUP BY tau.topic_id
+            HAVING COUNT(*) = 1
+          )
+        SQL
+    end
+  end
+
   advanced_filter(/^in:tagged$/) do |posts|
     posts
       .where('EXISTS (SELECT 1 FROM topic_tags WHERE topic_tags.topic_id = posts.topic_id)')
@@ -631,10 +652,14 @@ class Search
       elsif word == 'order:likes'
         @order = :likes
         nil
-      elsif word == 'in:private'
+      elsif %w{in:private in:personal}.include?(word) # remove private after 2.4 release

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

GitHub sha: 5fc5a7f5

1 Like

PERF: Improve query speed when looking up direct PMs