[FIX] web_editor: do not base selected alternative on index

When selecting an alternative in the AI Copywriter dialog, what we store
in the state is the index of the message. But that targets a different
message every time we add each new message at the start of the list.

This pushes new messages at the end of the list and only displays them
in reverse order, but also adds an ID to each message so they can be
targeted more robustly.

Part-of: odoo/odoo#139857
This commit is contained in:
Antoine Guenet
2023-11-02 16:57:48 +00:00
parent 849763b8f7
commit ca824e547e
5 changed files with 23 additions and 15 deletions
@@ -25,7 +25,7 @@ export class ChatGPTAlternativesDialog extends ChatGPTDialog {
setup() {
super.setup();
this.state = useState({
selectedMessage: null,
...this.state,
conversationHistory: [{
role: 'system',
content: 'The user wrote the following text:\n' +
@@ -86,12 +86,13 @@ export class ChatGPTAlternativesDialog extends ChatGPTDialog {
content,
});
}
this.state.messages.unshift({
this.state.messages.push({
author: 'assistant',
text: alternative,
isError,
batchId,
mode: this.state.alternativesMode,
id: new Date().getTime(),
});
}
}).catch(() => {
@@ -23,7 +23,7 @@
<em t-else="">Generating <t t-out="state.messagesInProgress"/> alternatives...</em>
</p>
</div>
<t t-foreach="state.messages" t-as="message" t-key="message_index">
<t t-foreach="[...state.messages].reverse()" t-as="message" t-key="message.id">
<t t-if="message.isError">
<div class="list-group-item o-chatgpt-alternative border-danger bg-danger o-message-error"
t-att-class="{ 'text-muted': state.currentBatchId != message.batchId }">
@@ -33,10 +33,10 @@
<t t-else="">
<button type="button" class="list-group-item list-group-item-action o-chatgpt-alternative"
t-on-click="selectMessage"
t-att-data-message-index="message_index"
t-att-data-message-id="message.id"
t-att-class="{
active: state.selectedMessage == message_index,
'text-muted': state.currentBatchId != message.batchId,
active: state.selectedMessageId == message.id,
'text-muted': state.selectedMessageId != message.id and state.currentBatchId != message.batchId,
}">
<span t-if="message.mode" class="badge bg-secondary float-end"><t t-out="props.alternativesModes[message.mode]"/></span>
<t t-out="formatContent(message.text)"/>
@@ -49,7 +49,7 @@
<!-- FOOTER -->
<t t-set-slot="footer">
<button class="btn btn-primary" t-on-click="_confirm"
t-att-disabled="typeof state.selectedMessage !== 'number'">Insert</button>
t-att-disabled="typeof state.selectedMessageId !== 'number'">Insert</button>
<button class="btn btn-secondary" t-on-click="_cancel">Cancel</button>
</t>
</Dialog>
@@ -1,6 +1,6 @@
/** @odoo-module **/
import { Component, markup } from "@odoo/owl";
import { Component, useState, markup } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";
import { Dialog } from "@web/core/dialog/dialog";
import { escape } from "@web/core/utils/strings";
@@ -18,6 +18,7 @@ export class ChatGPTDialog extends Component {
setup() {
this.rpc = useService('rpc');
this.state = useState({ selectedMessageId: null });
}
//--------------------------------------------------------------------------
@@ -25,7 +26,7 @@ export class ChatGPTDialog extends Component {
//--------------------------------------------------------------------------
selectMessage(ev) {
this.state.selectedMessage = +ev.currentTarget.getAttribute('data-message-index');
this.state.selectedMessageId = +ev.currentTarget.getAttribute('data-message-id');
}
insertMessage(ev) {
this.selectMessage(ev);
@@ -91,7 +92,7 @@ export class ChatGPTDialog extends Component {
_confirm() {
try {
this.props.close();
const text = this.state.messages[this.state.selectedMessage]?.text;
const text = this.state.messages.find(message => message.id === this.state.selectedMessageId)?.text;
this.props.insert(this._postprocessGeneratedContent(text || ''));
} catch (e) {
this.props.close();
@@ -21,6 +21,7 @@ export class ChatGPTPromptDialog extends ChatGPTDialog {
this.assistantAvatarUrl = `${browser.location.origin}/web_editor/static/src/img/odoobot_transparent.png`;
this.userAvatarUrl = `${browser.location.origin}/web/image?model=res.users&field=avatar_128&id=${encodeURIComponent(session.uid)}`;
this.state = useState({
...this.state,
prompt: this.props.initialPrompt,
conversationHistory: [{
role: 'system',
@@ -55,10 +56,10 @@ export class ChatGPTPromptDialog extends ChatGPTDialog {
ev.preventDefault();
const prompt = this.state.prompt;
this.state.messages.push({ author: 'user', text: prompt });
const datetime = new Date().getTime();
const messageId = new Date().getTime();
const conversation = { role: 'user', content: prompt };
this.state.conversationHistory.push(conversation);
this.state.messages.push({ author: 'assistant', datetime });
this.state.messages.push({ author: 'assistant', id: messageId });
this.state.prompt = '';
this._generate(prompt, (content, isError) => {
if (isError) {
@@ -68,8 +69,13 @@ export class ChatGPTPromptDialog extends ChatGPTDialog {
// There was no error, add the response to the history.
this.state.conversationHistory.push({ role: 'assistant', content });
}
const messageIndex = this.state.messages.findIndex(m => m.datetime === datetime);
this.state.messages[messageIndex] = { author: 'assistant', text: content, isError };
const messageIndex = this.state.messages.findIndex(m => m.id === messageId);
this.state.messages[messageIndex] = {
author: 'assistant',
text: content,
isError,
id: messageId,
};
this._unfreezeInput();
});
}
@@ -30,7 +30,7 @@
<div class="position-relative text-break mb-0 py-2 px-3 align-self-start rounded-end-3 rounded-bottom-3 o-chatgpt-message">
<!-- INSERT BUTTON -->
<button t-if="message.author === 'assistant' and 'text' in message and message.isError !== true"
t-att-data-message-index="message_index"
t-att-data-message-id="message.id"
class="o-message-insert d-block float-end p-1 mb-1 btn btn-info opacity-75 fst-italic ms-3"
t-on-click="insertMessage">Insert</button>
<div class="d-inline">