discourse-ai/assets/javascripts/discourse/components/suggestion-menus/ai-category-suggester.gjs

155 lines
4.3 KiB
Plaintext

import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { fn } from "@ember/helper";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import DropdownMenu from "discourse/components/dropdown-menu";
import categoryBadge from "discourse/helpers/category-badge";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import i18n from "discourse-common/helpers/i18n";
import DMenu from "float-kit/components/d-menu";
import { MIN_CHARACTER_COUNT } from "../../lib/ai-helper-suggestions";
export default class AiCategorySuggester extends Component {
@service siteSettings;
@tracked loading = false;
@tracked suggestions = null;
@tracked untriggers = [];
@tracked triggerIcon = "discourse-sparkles";
@tracked content = null;
@tracked topicContent = null;
constructor() {
super(...arguments);
if (!this.topicContent && this.args.composer?.reply === undefined) {
this.fetchTopicContent();
}
}
async fetchTopicContent() {
await ajax(`/t/${this.args.buffered.content.id}.json`).then(
({ post_stream }) => {
this.topicContent = post_stream.posts[0].cooked;
}
);
}
get showSuggestionButton() {
const composerFields = document.querySelector(".composer-fields");
this.content = this.args.composer?.reply || this.topicContent;
const showTrigger = this.content?.length > MIN_CHARACTER_COUNT;
if (composerFields) {
if (showTrigger) {
composerFields.classList.add("showing-ai-suggestions");
} else {
composerFields.classList.remove("showing-ai-suggestions");
}
}
return this.siteSettings.ai_embeddings_enabled && showTrigger;
}
@action
async loadSuggestions() {
if (this.suggestions && !this.dMenu.expanded) {
return this.suggestions;
}
this.loading = true;
this.triggerIcon = "spinner";
try {
const { assistant } = await ajax(
"/discourse-ai/ai-helper/suggest_category",
{
method: "POST",
data: { text: this.content },
}
);
this.suggestions = assistant;
} catch (error) {
popupAjaxError(error);
} finally {
this.loading = false;
this.triggerIcon = "sync-alt";
}
return this.suggestions;
}
@action
applySuggestion(suggestion) {
const composer = this.args.composer;
const buffered = this.args.buffered;
if (composer) {
composer.set("categoryId", suggestion.id);
composer.get("categoryId");
}
if (buffered) {
this.args.buffered.set("category_id", suggestion.id);
}
return this.dMenu.close();
}
@action
onRegisterApi(api) {
this.dMenu = api;
}
@action
onClose() {
this.triggerIcon = "discourse-sparkles";
}
<template>
{{#if this.showSuggestionButton}}
<DMenu
@title={{i18n "discourse_ai.ai_helper.suggest"}}
@icon={{this.triggerIcon}}
@identifier="ai-category-suggester"
@onClose={{this.onClose}}
@triggerClass="suggestion-button suggest-category-button {{if
this.loading
'is-loading'
}}"
@contentClass="ai-suggestions-menu"
@onRegisterApi={{this.onRegisterApi}}
@modalForMobile={{true}}
@untriggers={{this.untriggers}}
{{on "click" this.loadSuggestions}}
>
<:content>
{{#unless this.loading}}
<DropdownMenu as |dropdown|>
{{#each this.suggestions as |suggestion index|}}
<dropdown.item>
<DButton
class="category-row"
data-name={{suggestion.name}}
data-value={{index}}
title={{suggestion.name}}
@action={{fn this.applySuggestion suggestion}}
>
<div class="category-status">
{{categoryBadge suggestion}}
<span class="topic-count">x
{{suggestion.topicCount}}</span>
</div>
</DButton>
</dropdown.item>
{{/each}}
</DropdownMenu>
{{/unless}}
</:content>
</DMenu>
{{/if}}
</template>
}