turbo_chat 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -1
- data/README.md +178 -190
- data/app/assets/config/turbo_chat_manifest.js +3 -0
- data/app/assets/javascripts/turbo_chat/application.js +3 -0
- data/app/assets/javascripts/turbo_chat/invite_picker.js +19 -392
- data/app/assets/javascripts/turbo_chat/member_sync.js +426 -0
- data/app/assets/javascripts/turbo_chat/mentions.js +366 -0
- data/app/assets/javascripts/turbo_chat/messages.js +18 -370
- data/app/assets/javascripts/turbo_chat/realtime.js +3 -10
- data/app/assets/javascripts/turbo_chat/scroll_proxy.js +379 -0
- data/app/assets/javascripts/turbo_chat/shared.js +7 -383
- data/app/assets/stylesheets/turbo_chat/application.css +9 -1646
- data/app/assets/stylesheets/turbo_chat/base.css +84 -0
- data/app/assets/stylesheets/turbo_chat/components.css +193 -0
- data/app/assets/stylesheets/turbo_chat/composer.css +241 -0
- data/app/assets/stylesheets/turbo_chat/layout.css +307 -0
- data/app/assets/stylesheets/turbo_chat/members.css +264 -0
- data/app/assets/stylesheets/turbo_chat/menus.css +172 -0
- data/app/assets/stylesheets/turbo_chat/messages.css +430 -0
- data/app/controllers/turbo_chat/application_controller.rb +3 -7
- data/app/controllers/turbo_chat/chat_memberships_controller.rb +35 -1
- data/app/controllers/turbo_chat/chat_messages_controller.rb +4 -8
- data/app/controllers/turbo_chat/chats_controller.rb +10 -12
- data/app/helpers/turbo_chat/application_helper/config_support.rb +42 -32
- data/app/helpers/turbo_chat/application_helper/mention_support.rb +3 -3
- data/app/helpers/turbo_chat/application_helper/message_rendering.rb +24 -13
- data/app/models/turbo_chat/chat.rb +43 -20
- data/app/models/turbo_chat/chat_membership.rb +1 -1
- data/app/models/turbo_chat/chat_message/blocked_words_moderation.rb +9 -25
- data/app/models/turbo_chat/chat_message/body_length_validation.rb +1 -1
- data/app/models/turbo_chat/chat_message/broadcasting.rb +2 -6
- data/app/models/turbo_chat/chat_message/formatting.rb +3 -7
- data/app/models/turbo_chat/chat_message/mention_validation.rb +1 -1
- data/app/models/turbo_chat/chat_message/signals.rb +1 -1
- data/app/models/turbo_chat/chat_message.rb +3 -8
- data/app/views/turbo_chat/chat_messages/_form.html.erb +9 -9
- data/app/views/turbo_chat/chat_messages/_message.html.erb +2 -2
- data/app/views/turbo_chat/chat_messages/_signals.html.erb +11 -13
- data/app/views/turbo_chat/chat_messages/_system.html.erb +1 -1
- data/app/views/turbo_chat/chats/_invite_form.html.erb +1 -1
- data/app/views/turbo_chat/chats/_member_entries.html.erb +15 -1
- data/app/views/turbo_chat/chats/index.html.erb +1 -1
- data/app/views/turbo_chat/chats/new.html.erb +4 -7
- data/app/views/turbo_chat/chats/show.html.erb +29 -27
- data/config/routes.rb +6 -1
- data/db/migrate/20260325000016_add_chat_mode_to_turbo_chat_chats.rb +6 -0
- data/lib/generators/turbo_chat/install/templates/turbo_chat.rb +8 -0
- data/lib/turbo_chat/configuration/defaults.rb +21 -0
- data/lib/turbo_chat/configuration.rb +105 -0
- data/lib/turbo_chat/moderation/chat_actions.rb +2 -2
- data/lib/turbo_chat/moderation/member_actions.rb +2 -1
- data/lib/turbo_chat/moderation/support.rb +5 -9
- data/lib/turbo_chat/permission/support.rb +6 -2
- data/lib/turbo_chat/permission.rb +1 -5
- data/lib/turbo_chat/signals.rb +1 -1
- data/lib/turbo_chat/version.rb +1 -1
- metadata +13 -2
|
@@ -37,7 +37,8 @@ end.sort_by { |(role_name, role_key, role_rank)| [role_rank, role_name.to_s.down
|
|
|
37
37
|
data-chat-member-search="<%= invite_search_value %>"
|
|
38
38
|
data-chat-member-role-key="<%= membership.effective_role_key %>"
|
|
39
39
|
data-chat-member-role-name="<%= membership.effective_role_name %>"
|
|
40
|
-
data-chat-member-role-rank="<%= membership.effective_role_rank %>"
|
|
40
|
+
data-chat-member-role-rank="<%= membership.effective_role_rank %>"
|
|
41
|
+
data-chat-member-muted="<%= membership.muted? %>">
|
|
41
42
|
<div class="chat-members-item-main">
|
|
42
43
|
<span class="chat-members-name"><%= participant_name %></span>
|
|
43
44
|
<span class="chat-members-role"><%= membership.effective_role_name %></span>
|
|
@@ -69,6 +70,19 @@ end.sort_by { |(role_name, role_key, role_rank)| [role_rank, role_name.to_s.down
|
|
|
69
70
|
class: "chat-btn chat-btn--small chat-member-role-submit",
|
|
70
71
|
data: { chat_member_role_submit: true } %>
|
|
71
72
|
<% end %>
|
|
73
|
+
|
|
74
|
+
<div class="chat-member-moderation" data-chat-member-moderation-panel hidden>
|
|
75
|
+
<%= button_to(membership.muted? ? "Unmute" : "Mute",
|
|
76
|
+
TurboChat::Engine.routes.url_helpers.mute_chat_chat_membership_path(chat_id: chat.id, id: membership.id),
|
|
77
|
+
method: :patch,
|
|
78
|
+
class: "chat-btn chat-btn--small chat-btn--ghost",
|
|
79
|
+
data: { chat_member_mute_action: true }) %>
|
|
80
|
+
<%= button_to("Remove",
|
|
81
|
+
TurboChat::Engine.routes.url_helpers.ban_chat_chat_membership_path(chat_id: chat.id, id: membership.id),
|
|
82
|
+
method: :patch,
|
|
83
|
+
class: "chat-btn chat-btn--small chat-btn--danger",
|
|
84
|
+
data: { turbo_confirm: "Remove this member from this chat?", chat_member_ban_action: true }) %>
|
|
85
|
+
</div>
|
|
72
86
|
</div>
|
|
73
87
|
</li>
|
|
74
88
|
<% end %>
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
<li class="chat-list-item chat-list-item--chat">
|
|
49
49
|
<%= link_to chat_path(chat), class: "chat-list-link" do %>
|
|
50
50
|
<span class="chat-list-title"><%= chat.title %></span>
|
|
51
|
-
<% if
|
|
51
|
+
<% if chat_show_members?(chat: chat) %>
|
|
52
52
|
<span class="chat-list-meta"><%= pluralize(member_count, "member") %></span>
|
|
53
53
|
<% end %>
|
|
54
54
|
<% end %>
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
<section class="chat-shell">
|
|
1
|
+
<section class="chat-shell chat-shell--narrow">
|
|
2
2
|
<header class="chat-header">
|
|
3
3
|
<h1>New Chat</h1>
|
|
4
4
|
</header>
|
|
5
5
|
|
|
6
|
-
<%= form_with model: @chat, url: chats_path do |f| %>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
<%= f.text_field :title, required: true, autofocus: true %>
|
|
10
|
-
</div>
|
|
11
|
-
<%= f.submit "Create", class: "chat-btn" %>
|
|
6
|
+
<%= form_with model: @chat, url: chats_path, class: "chat-form chat-form--create" do |f| %>
|
|
7
|
+
<%= f.text_field :title, required: true, autofocus: true, placeholder: "Chat title" %>
|
|
8
|
+
<%= f.submit "Create", class: "chat-btn chat-btn--small" %>
|
|
12
9
|
<% end %>
|
|
13
10
|
</section>
|
|
@@ -3,36 +3,40 @@
|
|
|
3
3
|
<% current_participant_id = current_participant.id %>
|
|
4
4
|
<% self_mention_tokens = chat_self_mention_tokens(chat: @chat, participant: current_participant) %>
|
|
5
5
|
<% self_role_mention_token = chat_self_role_mention_token(chat: @chat, participant: current_participant) %>
|
|
6
|
-
<% mention_container_style = chat_mentions_container_inline_style %>
|
|
6
|
+
<% mention_container_style = chat_mentions_container_inline_style(chat: @chat) %>
|
|
7
7
|
<% chat_shell_classes = [
|
|
8
8
|
"chat-shell",
|
|
9
|
-
chat_shell_style_class,
|
|
9
|
+
chat_shell_style_class(chat: @chat),
|
|
10
10
|
("chat-shell--closed" if @chat.closed?),
|
|
11
11
|
("chat-shell--can-manage-member-permissions" if @can_manage_member_permissions)
|
|
12
12
|
].compact.join(" ") %>
|
|
13
|
-
<% message_insert_position = chat_message_insert_position %>
|
|
13
|
+
<% message_insert_position = chat_message_insert_position(chat: @chat) %>
|
|
14
14
|
<% chat_messages = @chat_messages.to_a %>
|
|
15
15
|
<% chat_messages.reverse! if message_insert_position == "append_start" %>
|
|
16
|
-
<% can_render_composer = @can_post_message && !chat_disable_input? %>
|
|
17
|
-
<% show_header_title = chat_show_header_title? %>
|
|
18
|
-
<% show_header_status = chat_show_header_status? %>
|
|
19
|
-
<% show_header_close_action = chat_show_header_close_action? %>
|
|
20
|
-
<% show_header_leave_action = chat_show_header_leave_action? %>
|
|
21
|
-
<% show_header_back_action = chat_show_header_back_action? %>
|
|
16
|
+
<% can_render_composer = @can_post_message && !chat_disable_input?(chat: @chat) %>
|
|
17
|
+
<% show_header_title = chat_show_header_title?(chat: @chat) %>
|
|
18
|
+
<% show_header_status = chat_show_header_status?(chat: @chat) %>
|
|
19
|
+
<% show_header_close_action = chat_show_header_close_action?(chat: @chat) %>
|
|
20
|
+
<% show_header_leave_action = chat_show_header_leave_action?(chat: @chat) %>
|
|
21
|
+
<% show_header_back_action = chat_show_header_back_action?(chat: @chat) %>
|
|
22
22
|
<% show_header_actions = show_header_close_action || show_header_leave_action || show_header_back_action %>
|
|
23
|
-
<% show_members_list = chat_show_members_list? %>
|
|
24
|
-
<% show_members_invite_controls = chat_show_members_invite_controls? %>
|
|
25
|
-
<% show_invite_fallback_when_members_hidden = chat_show_invite_fallback_when_members_hidden? %>
|
|
23
|
+
<% show_members_list = chat_show_members_list?(chat: @chat) %>
|
|
24
|
+
<% show_members_invite_controls = chat_show_members_invite_controls?(chat: @chat) %>
|
|
25
|
+
<% show_invite_fallback_when_members_hidden = chat_show_invite_fallback_when_members_hidden?(chat: @chat) %>
|
|
26
26
|
<% show_members_invite_block = show_members_invite_controls && @can_invite_member %>
|
|
27
27
|
<% show_members_panel = @show_members && (show_members_list || show_members_invite_block) %>
|
|
28
28
|
<% show_invite_fallback = !@show_members && show_members_invite_block && show_invite_fallback_when_members_hidden %>
|
|
29
29
|
<section class="<%= chat_shell_classes %>"
|
|
30
30
|
data-chat-id="<%= @chat.id %>"
|
|
31
|
-
data-chat-style="<%= chat_style_key %>"
|
|
31
|
+
data-chat-style="<%= chat_style_key(chat: @chat) %>"
|
|
32
|
+
data-chat-mode="<%= chat_mode_key(@chat) %>"
|
|
32
33
|
data-chat-self-participant-type="<%= current_participant_type %>"
|
|
33
34
|
data-chat-self-participant-id="<%= current_participant_id %>"
|
|
34
35
|
data-chat-can-manage-member-permissions="<%= @can_manage_member_permissions %>"
|
|
35
|
-
data-chat-
|
|
36
|
+
data-chat-can-grant-member-permissions="<%= @can_grant_member_permissions %>"
|
|
37
|
+
data-chat-can-mute-member="<%= @can_mute_member %>"
|
|
38
|
+
data-chat-can-ban-member="<%= @can_ban_member %>"
|
|
39
|
+
data-chat-emit-chat-lifecycle-events="<%= chat_emit_chat_lifecycle_events?(chat: @chat) %>"
|
|
36
40
|
data-chat-lifecycle-event="<%= json_escape(@chat_lifecycle_event.to_json) if @chat_lifecycle_event.present? %>">
|
|
37
41
|
<% invite_option_rows = if @can_invite_member && @invitable_participants.present?
|
|
38
42
|
@invitable_participants.map do |participant|
|
|
@@ -47,7 +51,6 @@
|
|
|
47
51
|
end %>
|
|
48
52
|
<header class="chat-header">
|
|
49
53
|
<div class="chat-header-copy">
|
|
50
|
-
<p class="chat-header-kicker"><%= @chat.closed? ? "Conversation closed" : "Conversation active" %></p>
|
|
51
54
|
<% if show_header_title || show_header_status %>
|
|
52
55
|
<div class="chat-header-title-row">
|
|
53
56
|
<% if show_header_title %>
|
|
@@ -150,21 +153,20 @@
|
|
|
150
153
|
data-chat-self-participant-id="<%= current_participant_id %>"
|
|
151
154
|
data-chat-self-mention-tokens="<%= json_escape(self_mention_tokens.to_json) %>"
|
|
152
155
|
data-chat-self-role-mention-token="<%= self_role_mention_token %>"
|
|
153
|
-
data-chat-mention-filter-exclude-self="<%= chat_mention_filter_exclude_self? %>"
|
|
154
|
-
data-chat-mention-filter-hide-roles="<%= chat_mention_filter_hide_roles? %>"
|
|
155
|
-
data-chat-emit-mention-events="<%= chat_emit_mention_events? %>"
|
|
156
|
+
data-chat-mention-filter-exclude-self="<%= chat_mention_filter_exclude_self?(chat: @chat) %>"
|
|
157
|
+
data-chat-mention-filter-hide-roles="<%= chat_mention_filter_hide_roles?(chat: @chat) %>"
|
|
158
|
+
data-chat-emit-mention-events="<%= chat_emit_mention_events?(chat: @chat) %>"
|
|
156
159
|
data-chat-can-edit-own-messages="<%= @can_edit_own_messages %>"
|
|
157
160
|
<%= %(style="#{mention_container_style}") if mention_container_style.present? %>>
|
|
158
161
|
<%= render chat_messages %>
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
<%= render "turbo_chat/chat_messages/signals", chat: @chat %>
|
|
162
|
+
<div id="<%= dom_id(@chat, :signals) %>"
|
|
163
|
+
class="chat-signals"
|
|
164
|
+
data-chat-show-self-signals="<%= chat_show_self_signals?(chat: @chat) %>"
|
|
165
|
+
data-chat-signal-ttl-seconds="<%= chat_signal_ttl_seconds(chat: @chat) %>"
|
|
166
|
+
data-chat-self-participant-type="<%= current_participant_type %>"
|
|
167
|
+
data-chat-self-participant-id="<%= current_participant_id %>">
|
|
168
|
+
<%= render "turbo_chat/chat_messages/signals", chat: @chat %>
|
|
169
|
+
</div>
|
|
168
170
|
</div>
|
|
169
171
|
</section>
|
|
170
172
|
|
data/config/routes.rb
CHANGED
|
@@ -10,7 +10,12 @@ TurboChat::Engine.routes.draw do
|
|
|
10
10
|
patch :reopen
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
resources :chat_memberships, only: %i[create update]
|
|
13
|
+
resources :chat_memberships, only: %i[create update] do
|
|
14
|
+
member do
|
|
15
|
+
patch :mute
|
|
16
|
+
patch :ban
|
|
17
|
+
end
|
|
18
|
+
end
|
|
14
19
|
resources :chat_messages, only: %i[index create update]
|
|
15
20
|
end
|
|
16
21
|
end
|
|
@@ -133,4 +133,12 @@ TurboChat.configure do |config|
|
|
|
133
133
|
# Shows your own active typing/signal indicators.
|
|
134
134
|
# config.signals.replace_signals_on_message_submit = true
|
|
135
135
|
# Replaces existing signals when sending a message.
|
|
136
|
+
|
|
137
|
+
# Chat mode overrides
|
|
138
|
+
# Assistant chats default to a simplified 1:1 profile.
|
|
139
|
+
# Uncomment any of these to customize assistant-mode behavior.
|
|
140
|
+
# config.mode(:assistant).show_members = true
|
|
141
|
+
# config.mode(:assistant).enable_mentions = true
|
|
142
|
+
# config.mode(:assistant).show_header_close_action = true
|
|
143
|
+
# config.mode(:assistant).max_chat_participants = 2
|
|
136
144
|
end
|
|
@@ -155,6 +155,27 @@ class TurboChat::Configuration
|
|
|
155
155
|
defaults[attribute] = SCOPED_DEFAULTS.fetch(scope_name).fetch(attribute)
|
|
156
156
|
end.freeze
|
|
157
157
|
|
|
158
|
+
ASSISTANT_MODE_DEFAULTS = {
|
|
159
|
+
max_chat_participants: 2,
|
|
160
|
+
show_members: false,
|
|
161
|
+
show_members_list: false,
|
|
162
|
+
show_members_invite_controls: false,
|
|
163
|
+
show_invite_fallback_when_members_hidden: false,
|
|
164
|
+
system_messages: false,
|
|
165
|
+
show_header_close_action: false,
|
|
166
|
+
enable_mentions: false,
|
|
167
|
+
emit_mention_events: false,
|
|
168
|
+
emit_invitation_events: false,
|
|
169
|
+
emit_chat_lifecycle_events: false,
|
|
170
|
+
emit_moderation_events: false
|
|
171
|
+
}.freeze
|
|
172
|
+
|
|
173
|
+
MODE_DEFAULTS = {
|
|
174
|
+
standard: {}.freeze,
|
|
175
|
+
assistant: ASSISTANT_MODE_DEFAULTS
|
|
176
|
+
}.freeze
|
|
177
|
+
|
|
178
|
+
MODE_NAMES = MODE_DEFAULTS.keys.freeze
|
|
158
179
|
SCOPE_NAMES = SCOPED_DEFAULTS.keys.freeze
|
|
159
180
|
ATTRIBUTES = ATTRIBUTE_SCOPES.keys.freeze
|
|
160
181
|
end
|
|
@@ -25,6 +25,31 @@ class TurboChat::Configuration
|
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
class ModeSettings
|
|
29
|
+
class << self
|
|
30
|
+
attr_reader :mode_attributes
|
|
31
|
+
|
|
32
|
+
def configure_attributes(attributes)
|
|
33
|
+
@mode_attributes = attributes
|
|
34
|
+
attr_accessor(*attributes)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
configure_attributes(ATTRIBUTES)
|
|
39
|
+
|
|
40
|
+
def initialize(defaults = {})
|
|
41
|
+
self.class.mode_attributes.each do |attribute|
|
|
42
|
+
instance_variable_set("@#{attribute}", nil)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
defaults.each do |attribute, value|
|
|
46
|
+
next unless respond_to?("#{attribute}=")
|
|
47
|
+
|
|
48
|
+
public_send("#{attribute}=", TurboChat::Configuration.resolve_default_value(value))
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
28
53
|
class Chat < Scope
|
|
29
54
|
configure_defaults(SCOPED_DEFAULTS.fetch(:chat))
|
|
30
55
|
end
|
|
@@ -75,11 +100,91 @@ class TurboChat::Configuration
|
|
|
75
100
|
instance_variable_set("@#{scope_name}", klass.new)
|
|
76
101
|
end
|
|
77
102
|
@additional_roles = {}
|
|
103
|
+
reset_modes!
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def mode(name)
|
|
107
|
+
normalized = self.class.normalize_mode_name(name)
|
|
108
|
+
raise ArgumentError, "unknown chat mode: #{name.inspect}" if normalized.nil?
|
|
109
|
+
|
|
110
|
+
@modes[normalized]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def reset_mode!(name)
|
|
114
|
+
normalized = self.class.normalize_mode_name(name)
|
|
115
|
+
raise ArgumentError, "unknown chat mode: #{name.inspect}" if normalized.nil?
|
|
116
|
+
|
|
117
|
+
@modes[normalized] = ModeSettings.new(MODE_DEFAULTS.fetch(normalized))
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def reset_modes!
|
|
121
|
+
@modes = MODE_DEFAULTS.each_with_object({}) do |(mode_name, defaults), modes|
|
|
122
|
+
modes[mode_name] = ModeSettings.new(defaults)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def resolved_value(attribute, chat: nil, default: nil)
|
|
127
|
+
base_value = if respond_to?(attribute)
|
|
128
|
+
public_send(attribute)
|
|
129
|
+
else
|
|
130
|
+
default
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
mode_name = self.class.extract_chat_mode(chat)
|
|
134
|
+
return base_value if mode_name.nil? || mode_name == :standard
|
|
135
|
+
|
|
136
|
+
mode_settings = @modes[mode_name]
|
|
137
|
+
return base_value unless mode_settings&.respond_to?(attribute)
|
|
138
|
+
|
|
139
|
+
override_value = mode_settings.public_send(attribute)
|
|
140
|
+
override_value.nil? ? base_value : override_value
|
|
141
|
+
rescue NoMethodError, TypeError, ArgumentError
|
|
142
|
+
default
|
|
78
143
|
end
|
|
79
144
|
|
|
80
145
|
class << self
|
|
81
146
|
def resolve_default_value(default_value)
|
|
82
147
|
default_value.respond_to?(:call) ? default_value.call : (default_value.dup rescue default_value)
|
|
83
148
|
end
|
|
149
|
+
|
|
150
|
+
def config_value(method_name, default: nil, chat: nil)
|
|
151
|
+
config = TurboChat.configuration
|
|
152
|
+
return config.resolved_value(method_name, chat: chat, default: default) if config.respond_to?(:resolved_value)
|
|
153
|
+
return config.public_send(method_name) if config.respond_to?(method_name)
|
|
154
|
+
|
|
155
|
+
default
|
|
156
|
+
rescue NoMethodError, TypeError, ArgumentError
|
|
157
|
+
default
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def config_boolean(method_name, default:, chat: nil)
|
|
161
|
+
value = config_value(method_name, default: default, chat: chat)
|
|
162
|
+
ActiveModel::Type::Boolean.new.cast(value)
|
|
163
|
+
rescue NoMethodError, TypeError
|
|
164
|
+
default
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def extract_chat_mode(chat_or_mode)
|
|
168
|
+
case chat_or_mode
|
|
169
|
+
when nil
|
|
170
|
+
nil
|
|
171
|
+
when String, Symbol
|
|
172
|
+
normalize_mode_name(chat_or_mode)
|
|
173
|
+
else
|
|
174
|
+
return nil unless chat_or_mode.respond_to?(:chat_mode)
|
|
175
|
+
|
|
176
|
+
normalize_mode_name(chat_or_mode.chat_mode)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def normalize_mode_name(value)
|
|
181
|
+
normalized = value.to_s.strip
|
|
182
|
+
return nil if normalized.blank?
|
|
183
|
+
|
|
184
|
+
mode_name = normalized.to_sym
|
|
185
|
+
return nil unless MODE_NAMES.include?(mode_name)
|
|
186
|
+
|
|
187
|
+
mode_name
|
|
188
|
+
end
|
|
84
189
|
end
|
|
85
190
|
end
|
|
@@ -6,7 +6,7 @@ module TurboChat::Moderation
|
|
|
6
6
|
|
|
7
7
|
payload = moderation_message_payload(message)
|
|
8
8
|
message.destroy!
|
|
9
|
-
emit_moderation_event("turbo_chat.moderation.message_deleted", actor: actor, payload: payload)
|
|
9
|
+
emit_moderation_event("turbo_chat.moderation.message_deleted", actor: actor, payload: payload, chat: message.chat)
|
|
10
10
|
true
|
|
11
11
|
end
|
|
12
12
|
|
|
@@ -37,7 +37,7 @@ module TurboChat::Moderation
|
|
|
37
37
|
def update_chat!(actor:, chat:, gate:, error_message:, event_name:, chat_event:)
|
|
38
38
|
authorize_chat_action!(actor: actor, chat: chat, gate: gate, error_message: error_message)
|
|
39
39
|
chat.public_send(chat_event)
|
|
40
|
-
emit_moderation_event(event_name, actor: actor, payload: moderation_chat_payload(chat))
|
|
40
|
+
emit_moderation_event(event_name, actor: actor, payload: moderation_chat_payload(chat), chat: chat)
|
|
41
41
|
chat
|
|
42
42
|
end
|
|
43
43
|
end
|
|
@@ -31,6 +31,7 @@ module TurboChat::Moderation
|
|
|
31
31
|
"turbo_chat.moderation.member_timed_out",
|
|
32
32
|
actor: actor,
|
|
33
33
|
membership: membership,
|
|
34
|
+
chat: membership.chat,
|
|
34
35
|
extra: { timed_out_until: membership.timed_out_until }
|
|
35
36
|
)
|
|
36
37
|
create_membership_system_message(actor: actor, membership: membership, event: :timed_out)
|
|
@@ -68,7 +69,7 @@ module TurboChat::Moderation
|
|
|
68
69
|
action: action,
|
|
69
70
|
attributes: attributes
|
|
70
71
|
)
|
|
71
|
-
emit_moderation_event(event_name, actor: actor, membership: updated_membership)
|
|
72
|
+
emit_moderation_event(event_name, actor: actor, membership: updated_membership, chat: updated_membership.chat)
|
|
72
73
|
create_membership_system_message(actor: actor, membership: updated_membership, event: system_event)
|
|
73
74
|
updated_membership
|
|
74
75
|
end
|
|
@@ -37,21 +37,17 @@ module TurboChat::Moderation
|
|
|
37
37
|
)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
def emit_moderation_event(name, actor:, membership: nil, payload: nil, extra: {})
|
|
41
|
-
|
|
40
|
+
def emit_moderation_event(name, actor:, membership: nil, payload: nil, extra: {}, chat: nil)
|
|
41
|
+
event_chat = chat || membership&.chat
|
|
42
|
+
return unless moderation_events_enabled?(event_chat)
|
|
42
43
|
return unless defined?(ActiveSupport::Notifications)
|
|
43
44
|
|
|
44
45
|
event_payload = payload.presence || moderation_membership_payload(membership)
|
|
45
46
|
ActiveSupport::Notifications.instrument(name, event_payload.merge(actor_payload(actor)).merge(extra))
|
|
46
47
|
end
|
|
47
48
|
|
|
48
|
-
def moderation_events_enabled?
|
|
49
|
-
|
|
50
|
-
return false unless config.respond_to?(:emit_moderation_events)
|
|
51
|
-
|
|
52
|
-
ActiveModel::Type::Boolean.new.cast(config.emit_moderation_events)
|
|
53
|
-
rescue NoMethodError, TypeError
|
|
54
|
-
false
|
|
49
|
+
def moderation_events_enabled?(chat = nil)
|
|
50
|
+
TurboChat::Configuration.config_boolean(:emit_moderation_events, default: false, chat: chat)
|
|
55
51
|
end
|
|
56
52
|
|
|
57
53
|
def moderation_membership_payload(membership)
|
|
@@ -24,7 +24,7 @@ class TurboChat::Permission
|
|
|
24
24
|
return nil unless chat_present?
|
|
25
25
|
return nil if target_participant.nil?
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
lookup_membership(target_participant)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def role_permission?(permission) = role_permissions.include?(permission.to_sym)
|
|
@@ -39,7 +39,7 @@ class TurboChat::Permission
|
|
|
39
39
|
return @actor_membership if defined?(@actor_membership)
|
|
40
40
|
|
|
41
41
|
@actor_membership = if chat_present? && participant_present?
|
|
42
|
-
|
|
42
|
+
lookup_membership(participant)
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
|
|
@@ -64,5 +64,9 @@ class TurboChat::Permission
|
|
|
64
64
|
def message_in_chat?(message) = message.chat_id == chat&.id
|
|
65
65
|
|
|
66
66
|
def membership_in_chat?(target_membership) = target_membership.chat_id == chat&.id
|
|
67
|
+
|
|
68
|
+
def lookup_membership(target)
|
|
69
|
+
chat.find_active_membership(target)
|
|
70
|
+
end
|
|
67
71
|
end
|
|
68
72
|
end
|
|
@@ -70,10 +70,6 @@ class TurboChat::Permission
|
|
|
70
70
|
private
|
|
71
71
|
|
|
72
72
|
def chat_input_disabled?
|
|
73
|
-
|
|
74
|
-
value = configuration.respond_to?(:disable_input) ? configuration.disable_input : false
|
|
75
|
-
ActiveModel::Type::Boolean.new.cast(value)
|
|
76
|
-
rescue NoMethodError, TypeError
|
|
77
|
-
false
|
|
73
|
+
TurboChat::Configuration.config_boolean(:disable_input, default: false, chat: chat)
|
|
78
74
|
end
|
|
79
75
|
end
|
data/lib/turbo_chat/signals.rb
CHANGED
data/lib/turbo_chat/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: turbo_chat
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alexander Haumer
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-03-
|
|
11
|
+
date: 2026-03-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -64,10 +64,20 @@ files:
|
|
|
64
64
|
- app/assets/javascripts/turbo_chat/application.js
|
|
65
65
|
- app/assets/javascripts/turbo_chat/invite_picker.js
|
|
66
66
|
- app/assets/javascripts/turbo_chat/lifecycle_events.js
|
|
67
|
+
- app/assets/javascripts/turbo_chat/member_sync.js
|
|
68
|
+
- app/assets/javascripts/turbo_chat/mentions.js
|
|
67
69
|
- app/assets/javascripts/turbo_chat/messages.js
|
|
68
70
|
- app/assets/javascripts/turbo_chat/realtime.js
|
|
71
|
+
- app/assets/javascripts/turbo_chat/scroll_proxy.js
|
|
69
72
|
- app/assets/javascripts/turbo_chat/shared.js
|
|
70
73
|
- app/assets/stylesheets/turbo_chat/application.css
|
|
74
|
+
- app/assets/stylesheets/turbo_chat/base.css
|
|
75
|
+
- app/assets/stylesheets/turbo_chat/components.css
|
|
76
|
+
- app/assets/stylesheets/turbo_chat/composer.css
|
|
77
|
+
- app/assets/stylesheets/turbo_chat/layout.css
|
|
78
|
+
- app/assets/stylesheets/turbo_chat/members.css
|
|
79
|
+
- app/assets/stylesheets/turbo_chat/menus.css
|
|
80
|
+
- app/assets/stylesheets/turbo_chat/messages.css
|
|
71
81
|
- app/controllers/turbo_chat/application_controller.rb
|
|
72
82
|
- app/controllers/turbo_chat/chat_memberships_controller.rb
|
|
73
83
|
- app/controllers/turbo_chat/chat_messages_controller.rb
|
|
@@ -114,6 +124,7 @@ files:
|
|
|
114
124
|
- db/migrate/20260218000013_add_invitation_accepted_to_turbo_chat_chat_memberships.rb
|
|
115
125
|
- db/migrate/20260223000014_add_source_fields_to_turbo_chat_chat_messages.rb
|
|
116
126
|
- db/migrate/20260302000015_add_kind_index_to_turbo_chat_chat_messages.rb
|
|
127
|
+
- db/migrate/20260325000016_add_chat_mode_to_turbo_chat_chats.rb
|
|
117
128
|
- lib/generators/turbo_chat/install/install_generator.rb
|
|
118
129
|
- lib/generators/turbo_chat/install/templates/turbo_chat.rb
|
|
119
130
|
- lib/tasks/turbo_chat_tasks.rake
|