FEATURE: Tag-level chat channels (#220)

FEATURE: Tag-level chat channels (#220)

diff --git a/app/controllers/chat_controller.rb b/app/controllers/chat_controller.rb
index 6064edb..8dd832c 100644
--- a/app/controllers/chat_controller.rb
+++ b/app/controllers/chat_controller.rb
@@ -24,7 +24,7 @@ class DiscourseChat::ChatController < DiscourseChat::ChatBaseController
     end
 
     success = chat_channel.save
-    if success
+    if success && chat_channel.chatable_has_custom_fields?
       @chatable.custom_fields[DiscourseChat::HAS_CHAT_ENABLED] = true
       @chatable.save!
 
@@ -36,12 +36,12 @@ class DiscourseChat::ChatController < DiscourseChat::ChatBaseController
   def disable_chat
     chat_channel = ChatChannel.with_deleted.find_by(chatable: @chatable)
     if chat_channel.trashed?
-      return render_json_error I18n.t("chat.already_disabled")
+      return render json: success_json
     end
     chat_channel.trash!(current_user)
 
     success = chat_channel.save
-    if success &&
+    if success && chat_channel.chatable_has_custom_fields?
       @chatable.custom_fields.delete(DiscourseChat::HAS_CHAT_ENABLED)
       @chatable.save!
 
@@ -252,9 +252,12 @@ class DiscourseChat::ChatController < DiscourseChat::ChatBaseController
   end
 
   def find_chatable
-    @chatable = params[:chatable_type].downcase == "topic" ?
-      Topic.find(params[:chatable_id]) :
-      Category.find(params[:chatable_id])
+    chatable_class = case params[:chatable_type].downcase
+                     when "topic" then Topic
+                     when "category" then Category
+                     when "tag" then Tag
+    end
+    @chatable = chatable_class.find_by(id: params[:chatable_id])
 
     guardian.ensure_can_see!(@chatable)
     guardian.ensure_can_moderate_chat!(@chatable)
diff --git a/app/models/chat_channel.rb b/app/models/chat_channel.rb
index ea4ee66..e59c1ac 100644
--- a/app/models/chat_channel.rb
+++ b/app/models/chat_channel.rb
@@ -23,6 +23,10 @@ class ChatChannel < ActiveRecord::Base
       chatable.url
   end
 
+  def tag_channel?
+    chatable_type == "Tag"
+  end
+
   def topic_channel?
     chatable_type == "Topic"
   end
@@ -39,6 +43,10 @@ class ChatChannel < ActiveRecord::Base
     chatable_type == DiscourseChat::SITE_CHAT_TYPE
   end
 
+  def chatable_has_custom_fields?
+    topic_channel? || category_channel?
+  end
+
   def allowed_user_ids
     direct_message_channel? ?
       chatable.user_ids :
@@ -63,6 +71,8 @@ class ChatChannel < ActiveRecord::Base
       chatable.title.parameterize
     when "Category"
       chatable.name
+    when "Tag"
+      chatable.name
     when "Site"
       I18n.t("chat.site_chat_name")
     when "DirectMessageChannel"
diff --git a/app/serializers/chat_channel_serializer.rb b/app/serializers/chat_channel_serializer.rb
index 675fe3f..8e169b6 100644
--- a/app/serializers/chat_channel_serializer.rb
+++ b/app/serializers/chat_channel_serializer.rb
@@ -33,6 +33,8 @@ class ChatChannelSerializer < ApplicationSerializer
       BasicTopicSerializer.new(object.chatable, root: false).as_json
     when "Category"
       BasicCategorySerializer.new(object.chatable, root: false).as_json
+    when "Tag"
+      TagSerializer.new(object.chatable, root: false).as_json
     when "DirectMessageChannel"
       DirectMessageChannelSerializer.new(object.chatable, scope: scope, root: false).as_json
     when "Site"
diff --git a/assets/javascripts/discourse/components/tag-chat-settings.js b/assets/javascripts/discourse/components/tag-chat-settings.js
new file mode 100644
index 0000000..ae6f12f
--- /dev/null
+++ b/assets/javascripts/discourse/components/tag-chat-settings.js
@@ -0,0 +1,26 @@
+import Component from "@ember/component";
+import { action } from "@ember/object";
+import { ajax } from "discourse/lib/ajax";
+import { popupAjaxError } from "discourse/lib/ajax-error";
+
+export default Component.extend({
+  tag: null,
+  loading: false,
+
+  @action
+  toggleChatEnabled() {
+    this.set("loading", true);
+    return ajax(`/chat/${this.tag.chat_enabled ? "disable" : "enable"}`, {
+      type: "POST",
+      data: {
+        chatable_type: "tag",
+        chatable_id: this.tag.id,
+      },
+    })
+      .then(() => {
+        this.tag.set("chat_enabled", !this.tag.chat_enabled);
+      })
+      .catch(popupAjaxError)
+      .finally(() => this.set("loading", false));
+  },
+});
diff --git a/assets/javascripts/discourse/connectors/tag-custom-settings/chat-enabled-toggle.hbs b/assets/javascripts/discourse/connectors/tag-custom-settings/chat-enabled-toggle.hbs
new file mode 100644
index 0000000..d72ae45
--- /dev/null
+++ b/assets/javascripts/discourse/connectors/tag-custom-settings/chat-enabled-toggle.hbs
@@ -0,0 +1 @@
+{{tag-chat-settings tag=tag}}
diff --git a/assets/javascripts/discourse/templates/components/chat-channel-title.hbs b/assets/javascripts/discourse/templates/components/chat-channel-title.hbs
index ff534f3..108a497 100644
--- a/assets/javascripts/discourse/templates/components/chat-channel-title.hbs
+++ b/assets/javascripts/discourse/templates/components/chat-channel-title.hbs
@@ -15,6 +15,11 @@
     </div>
 
   {{/if}}
+{{else if (eq channel.chatable_type "Tag")}}
+  <span class="tag-chat-badge">
+    {{d-icon "tag"}}
+    <span class="tag-chat-name">{{channel.title}}</span>
+  </span>
 {{else if (eq channel.chatable_type "Category")}}
   <span class="category-chat-badge" style={{html-safe (concat "color: #" channel.chatable.color)}}>
     {{d-icon "hashtag"}}
diff --git a/assets/javascripts/discourse/templates/components/tag-chat-settings.hbs b/assets/javascripts/discourse/templates/components/tag-chat-settings.hbs
new file mode 100644
index 0000000..f732154
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/tag-chat-settings.hbs
@@ -0,0 +1,9 @@
+{{#if siteSettings.topic_chat_enabled}}
+  {{d-button
+    class="tag-chat-toggle"
+    icon="comment"
+    action=(action "toggleChatEnabled")
+    label=(if tag.chat_enabled "chat.disable" "chat.enable")
+    disabled=loading
+  }}
+{{/if}}
diff --git a/assets/stylesheets/common/common.scss b/assets/stylesheets/common/common.scss
index 9d474e9..7fdd135 100644
--- a/assets/stylesheets/common/common.scss
+++ b/assets/stylesheets/common/common.scss
@@ -900,10 +900,11 @@ $float-height: 530px;
   max-width: calc(100% - 10px - 0.5em);
   .category-chat-name,
   .topic-chat-name,
+  .tag-chat-name,
   .chat-name {
     color: var(--primary-high);
   }
-  .topic-chat-name {
+  .topic-chat-name, .tag-chat-name {
     margin: 0 0 0 0.25em;
   }
   .d-icon-lock {
@@ -1357,3 +1358,7 @@ $float-height: 530px;
 .user-preferences .chat-setting .controls {
   margin-bottom: 0;
 }
+
+.tag-info .tag-chat-toggle {
+  margin-bottom: 0.5em;
+}
diff --git a/lib/chat_channel_fetcher.rb b/lib/chat_channel_fetcher.rb
index 96a6e48..e817e2b 100644
--- a/lib/chat_channel_fetcher.rb
+++ b/lib/chat_channel_fetcher.rb
@@ -18,9 +18,10 @@ module DiscourseChat::ChatChannelFetcher
     category_channels = channels.select(&:category_channel?)
     topic_channels = channels.select(&:topic_channel?)
     site_channel = channels.detect(&:site_channel?)
+    tag_channels = channels.select(&:tag_channel?)
     added_channel_ids = category_channels.map(&:id)
 
-    structured = category_channels.map do |category_channel|
+    structured = tag_channels + category_channels.map do |category_channel|
       category_channel.chat_channels = channels.select do |channel|
         add = channel.topic_channel? && channel.chatable.category_id == category_channel.chatable.id
         added_channel_ids << channel.id if add
@@ -42,7 +43,7 @@ module DiscourseChat::ChatChannelFetcher
       channels = channels.includes(:chat_messages)
     end
 
-    channels = channels.where(chatable_type: [DiscourseChat::SITE_CHAT_TYPE, "Topic", "Category"])
+    channels = channels.where(chatable_type: [DiscourseChat::SITE_CHAT_TYPE, "Topic", "Category", "Tag"])
     if scope_with_membership
       channels = channels
         .joins(:user_chat_channel_memberships)

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

GitHub sha: e618a5af89ded1576fc2b5bea2be4bc369110842

This commit appears in #220 which was approved by eviltrout. It was merged by markvanlan.