Skip to content
This repository was archived by the owner on Jul 22, 2025. It is now read-only.

Commit 2214aa6

Browse files
committed
remove composer reliance.. simply some shiz
1 parent 331e68f commit 2214aa6

File tree

5 files changed

+111
-105
lines changed

5 files changed

+111
-105
lines changed

assets/javascripts/discourse/controllers/discourse-ai-bot-conversations.js

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,28 @@
11
import Controller from "@ember/controller";
22
import { action } from "@ember/object";
3+
import { alias } from "@ember/object/computed";
34
import { service } from "@ember/service";
4-
import { tracked } from "@ember-compat/tracked-built-ins";
55

66
export default class DiscourseAiBotConversations extends Controller {
77
@service aiBotConversationsHiddenSubmit;
88
@service currentUser;
99

10-
@tracked selectedPersona = this.personaOptions[0].username;
10+
@alias("aiBotConversationsHiddenSubmit.loading") loading;
1111

1212
textarea = null;
1313

1414
init() {
1515
super.init(...arguments);
16-
this.selectedPersonaChanged(this.selectedPersona);
1716
}
1817

19-
get personaOptions() {
20-
if (this.currentUser.ai_enabled_personas) {
21-
return this.currentUser.ai_enabled_personas
22-
.filter((persona) => persona.username)
23-
.map((persona) => {
24-
return {
25-
id: persona.id,
26-
username: persona.username,
27-
name: persona.name,
28-
description: persona.description,
29-
};
30-
});
31-
}
32-
}
33-
34-
get displayPersonaSelector() {
35-
return this.personaOptions.length > 1;
36-
}
37-
38-
get filterable() {
39-
return this.personaOptions.length > 4;
18+
@action
19+
setPersonaId(id) {
20+
this.aiBotConversationsHiddenSubmit.personaId = id;
4021
}
4122

4223
@action
43-
selectedPersonaChanged(username) {
44-
this.selectedPersona = username;
45-
this.aiBotConversationsHiddenSubmit.personaUsername = username;
24+
setTargetRecipient(username) {
25+
this.aiBotConversationsHiddenSubmit.targetUsername = username;
4626
}
4727

4828
@action
Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
import { action } from "@ember/object";
22
import { next } from "@ember/runloop";
33
import Service, { service } from "@ember/service";
4+
import { tracked } from "@ember-compat/tracked-built-ins";
5+
import { ajax } from "discourse/lib/ajax";
46
import { popupAjaxError } from "discourse/lib/ajax-error";
57
import { i18n } from "discourse-i18n";
6-
import { composeAiBotMessage } from "../lib/ai-bot-helper";
78

89
export default class AiBotConversationsHiddenSubmit extends Service {
9-
@service composer;
1010
@service aiConversationsSidebarManager;
11+
@service appEvents;
12+
@service composer;
1113
@service dialog;
14+
@service router;
15+
16+
@tracked loading = false;
1217

13-
personaUsername;
18+
personaId;
19+
targetUsername;
1420

1521
inputValue = "";
1622

@@ -25,9 +31,6 @@ export default class AiBotConversationsHiddenSubmit extends Service {
2531

2632
@action
2733
async submitToBot() {
28-
this.composer.destroyDraft();
29-
this.composer.close();
30-
3134
if (this.inputValue.length < 10) {
3235
return this.dialog.alert({
3336
message: i18n(
@@ -38,25 +41,32 @@ export default class AiBotConversationsHiddenSubmit extends Service {
3841
});
3942
}
4043

41-
// we are intentionally passing null as the targetBot to allow for the
42-
// function to select the first available bot. This will be refactored in the
43-
// future to allow for selecting a specific bot.
44-
await composeAiBotMessage(null, this.composer, {
45-
skipFocus: true,
46-
topicBody: this.inputValue,
47-
personaUsername: this.personaUsername,
48-
});
44+
this.loading = true;
45+
const title = i18n("discourse_ai.ai_bot.default_pm_prefix");
4946

5047
try {
51-
await this.composer.save();
52-
this.aiConversationsSidebarManager.newTopicForceSidebar = true;
53-
if (this.inputValue.length > 10) {
54-
// prevents submitting same message again when returning home
55-
// but avoids deleting too-short message on submit
56-
this.inputValue = "";
57-
}
48+
const response = await ajax("/posts.json", {
49+
method: "POST",
50+
data: {
51+
raw: this.inputValue,
52+
title,
53+
archetype: "private_message",
54+
target_recipients: this.targetUsername,
55+
meta_data: { ai_persona_id: this.personaId },
56+
topic_custom_fields: { is_ai_bot_pm: true },
57+
},
58+
});
59+
60+
this.appEvents.trigger("discourse-ai:bot-pm-created", {
61+
id: response.topic_id,
62+
slug: response.topic_slug,
63+
title,
64+
});
65+
this.router.transitionTo(response.post_url);
5866
} catch (e) {
5967
popupAjaxError(e);
68+
} finally {
69+
this.loading = false;
6070
}
6171
}
6272
}

assets/javascripts/discourse/templates/discourse-ai-bot-conversations.gjs

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,45 @@
1-
import { hash } from "@ember/helper";
21
import { on } from "@ember/modifier";
32
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
43
import RouteTemplate from "ember-route-template";
4+
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
55
import DButton from "discourse/components/d-button";
66
import { i18n } from "discourse-i18n";
7-
import DropdownSelectBox from "select-kit/components/dropdown-select-box";
7+
import AiPersonaLlmSelector from "discourse/plugins/discourse-ai/discourse/components/ai-persona-llm-selector";
88

99
export default RouteTemplate(
1010
<template>
1111
<div class="ai-bot-conversations">
12-
{{#if @controller.displayPersonaSelector}}
13-
<div class="ai-bot-conversations__persona-selector">
14-
<DropdownSelectBox
15-
class="persona-llm-selector__persona-dropdown"
16-
@value={{@controller.selectedPersona}}
17-
@valueProperty="username"
18-
@content={{@controller.personaOptions}}
19-
@options={{hash icon="robot" filterable=@controller.filterable}}
20-
@onChange={{@controller.selectedPersonaChanged}}
21-
/>
22-
</div>
23-
{{/if}}
12+
<AiPersonaLlmSelector
13+
@setPersonaId={{@controller.setPersonaId}}
14+
@setTargetRecipient={{@controller.setTargetRecipient}}
15+
/>
2416

2517
<div class="ai-bot-conversations__content-wrapper">
26-
27-
<h1>{{i18n "discourse_ai.ai_bot.conversations.header"}}</h1>
28-
<div class="ai-bot-conversations__input-wrapper">
29-
<textarea
30-
{{didInsert @controller.setTextArea}}
31-
{{on "input" @controller.updateInputValue}}
32-
{{on "keydown" @controller.handleKeyDown}}
33-
id="ai-bot-conversations-input"
34-
placeholder={{i18n "discourse_ai.ai_bot.conversations.placeholder"}}
35-
minlength="10"
36-
rows="1"
37-
/>
38-
<DButton
39-
@action={{@controller.aiBotConversationsHiddenSubmit.submitToBot}}
40-
@icon="paper-plane"
41-
@title="discourse_ai.ai_bot.conversations.header"
42-
class="ai-bot-button btn-primary ai-conversation-submit"
43-
/>
44-
</div>
45-
<p class="ai-disclaimer">
46-
{{i18n "discourse_ai.ai_bot.conversations.disclaimer"}}
47-
</p>
18+
<ConditionalLoadingSpinner @condition={{@controller.loading}}>
19+
<h1>{{i18n "discourse_ai.ai_bot.conversations.header"}}</h1>
20+
<div class="ai-bot-conversations__input-wrapper">
21+
<textarea
22+
{{didInsert @controller.setTextArea}}
23+
{{on "input" @controller.updateInputValue}}
24+
{{on "keydown" @controller.handleKeyDown}}
25+
id="ai-bot-conversations-input"
26+
placeholder={{i18n
27+
"discourse_ai.ai_bot.conversations.placeholder"
28+
}}
29+
minlength="10"
30+
rows="1"
31+
/>
32+
<DButton
33+
@action={{@controller.aiBotConversationsHiddenSubmit.submitToBot}}
34+
@icon="paper-plane"
35+
@title="discourse_ai.ai_bot.conversations.header"
36+
class="ai-bot-button btn-primary ai-conversation-submit"
37+
/>
38+
</div>
39+
<p class="ai-disclaimer">
40+
{{i18n "discourse_ai.ai_bot.conversations.disclaimer"}}
41+
</p>
42+
</ConditionalLoadingSpinner>
4843
</div>
4944
</div>
5045
</template>

assets/javascripts/initializers/ai-conversations-sidebar.js

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ export default {
5353
this.topic = topic;
5454
}
5555

56+
get key() {
57+
return this.topic.id;
58+
}
59+
5660
get name() {
5761
return this.topic.title;
5862
}
@@ -90,13 +94,21 @@ export default {
9094
super(...arguments);
9195
this.fetchMessages();
9296

93-
appEvents.on("topic:created", this, "addNewMessageToSidebar");
97+
appEvents.on(
98+
"discourse-ai:bot-pm-created",
99+
this,
100+
"addNewPMToSidebar"
101+
);
94102
}
95103

96104
@bind
97105
willDestroy() {
98106
this.removeScrollListener();
99-
appEvents.on("topic:created", this, "addNewMessageToSidebar");
107+
appEvents.off(
108+
"discourse-ai:bot-pm-created",
109+
this,
110+
"addNewPMToSidebar"
111+
);
100112
}
101113

102114
get name() {
@@ -115,8 +127,8 @@ export default {
115127
);
116128
}
117129

118-
addNewMessageToSidebar(topic) {
119-
this.addNewMessage(topic);
130+
addNewPMToSidebar(topic) {
131+
this.links = [new AiConversationLink(topic), ...this.links];
120132
this.watchForTitleUpdate(topic);
121133
}
122134

@@ -201,28 +213,25 @@ export default {
201213
);
202214
}
203215

204-
addNewMessage(newTopic) {
205-
this.links = [new AiConversationLink(newTopic), ...this.links];
206-
}
207-
208216
watchForTitleUpdate(topic) {
209-
const channel = `/discourse-ai/ai-bot/topic/${topic.topic_id}`;
210-
const topicId = topic.topic_id;
217+
const channel = `/discourse-ai/ai-bot/topic/${topic.id}`;
211218
const callback = this.updateTopicTitle.bind(this);
212219
messageBus.subscribe(channel, ({ title }) => {
213-
callback(topicId, title);
220+
callback(topic, title);
214221
messageBus.unsubscribe(channel);
215222
});
216223
}
217224

218-
updateTopicTitle(topicId, title) {
219-
// update the topic title in the sidebar, instead of the default title
220-
const text = document.querySelector(
221-
`.sidebar-section-link-wrapper .ai-conversation-${topicId} .sidebar-section-link-content-text`
225+
updateTopicTitle(topic, title) {
226+
// update the data
227+
topic.title = title;
228+
229+
// force Glimmer to re-render that one link
230+
this.links = this.links.map((link) =>
231+
link.topic.id === topic.id
232+
? new AiConversationLink(topic)
233+
: link
222234
);
223-
if (text) {
224-
text.innerText = title;
225-
}
226235
}
227236
};
228237
},
@@ -240,9 +249,10 @@ export default {
240249
if (
241250
topic?.archetype === "private_message" &&
242251
topic.user_id === currentUser.id &&
243-
topic.postStream.posts.some((post) =>
244-
isPostFromAiBot(post, currentUser)
245-
)
252+
(topic.is_bot_pm ||
253+
topic.postStream.posts.some((post) =>
254+
isPostFromAiBot(post, currentUser)
255+
))
246256
) {
247257
return aiConversationsSidebarManager.forceCustomSidebar();
248258
}

lib/ai_bot/entry_point.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
module DiscourseAi
44
module AiBot
55
USER_AGENT = "Discourse AI Bot 1.0 (https://www.discourse.org)"
6+
TOPIC_AI_BOT_PM_FIELD = "is_ai_bot_pm"
67

78
class EntryPoint
89
Bot = Struct.new(:id, :name, :llm)
@@ -64,6 +65,8 @@ def self.ai_share_error(topic, guardian)
6465
end
6566

6667
def inject_into(plugin)
68+
plugin.register_editable_topic_custom_field(TOPIC_AI_BOT_PM_FIELD)
69+
6770
plugin.register_modifier(:chat_allowed_bot_user_ids) do |user_ids, guardian|
6871
if guardian.user
6972
allowed_chat =
@@ -102,6 +105,14 @@ def inject_into(plugin)
102105
Rails.root.join("plugins", "discourse-ai", "db", "fixtures", "ai_bot"),
103106
)
104107

108+
plugin.add_to_serializer(
109+
:topic_view,
110+
:is_bot_pm,
111+
include_condition: -> do
112+
object.personal_message && object.topic.custom_fields[TOPIC_AI_BOT_PM_FIELD]
113+
end,
114+
) { true }
115+
105116
plugin.add_to_serializer(
106117
:current_user,
107118
:ai_enabled_personas,

0 commit comments

Comments
 (0)