DEV: Add review menu state (#159)
This commit is contained in:
parent
65c6b5e16c
commit
7790313b1b
|
@ -0,0 +1,48 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import DModal from "discourse/components/d-modal";
|
||||
import DModalCancel from "discourse/components/d-modal-cancel";
|
||||
import I18n from "I18n";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
|
||||
const t = I18n.t.bind(I18n);
|
||||
|
||||
export default class ModalDiffModal extends Component {
|
||||
<template>
|
||||
<DModal
|
||||
class="composer-ai-helper-modal"
|
||||
@title={{t "discourse_ai.ai_helper.context_menu.changes"}}
|
||||
@closeModal={{@closeModal}}
|
||||
>
|
||||
<:body>
|
||||
{{#if @diff}}
|
||||
{{htmlSafe @diff}}
|
||||
{{else}}
|
||||
<div class="composer-ai-helper-modal__old-value">
|
||||
{{@oldValue}}
|
||||
</div>
|
||||
|
||||
<div class="composer-ai-helper-modal__new-value">
|
||||
{{@newValue}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</:body>
|
||||
|
||||
<:footer>
|
||||
<DButton
|
||||
class="btn-primary confirm"
|
||||
@action={{this.triggerConfirmChanges}}
|
||||
@label="discourse_ai.ai_helper.context_menu.confirm"
|
||||
/>
|
||||
<DModalCancel @close={{@closeModal}} />
|
||||
</:footer>
|
||||
</DModal>
|
||||
</template>
|
||||
|
||||
@action
|
||||
triggerConfirmChanges() {
|
||||
this.args.closeModal();
|
||||
this.args.confirm();
|
||||
}
|
||||
}
|
|
@ -51,6 +51,34 @@
|
|||
</li>
|
||||
</ul>
|
||||
|
||||
{{else if (eq this.menuState this.CONTEXT_MENU_STATES.review)}}
|
||||
<ul class="ai-helper-context-menu__review">
|
||||
<li>
|
||||
<DButton
|
||||
@icon="exchange-alt"
|
||||
@label="discourse_ai.ai_helper.context_menu.view_changes"
|
||||
@action={{this.viewChanges}}
|
||||
class="btn-flat view-changes"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<DButton
|
||||
@icon="undo"
|
||||
@label="discourse_ai.ai_helper.context_menu.revert"
|
||||
@action={{this.undoAIAction}}
|
||||
class="btn-flat revert"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<DButton
|
||||
@icon="check"
|
||||
@label="discourse_ai.ai_helper.context_menu.confirm"
|
||||
@action={{this.confirmChanges}}
|
||||
class="btn-flat confirm"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{else if (eq this.menuState this.CONTEXT_MENU_STATES.resets)}}
|
||||
<ul class="ai-helper-context-menu__resets">
|
||||
<li>
|
||||
|
@ -76,3 +104,13 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if this.showDiffModal}}
|
||||
<Modal::DiffModal
|
||||
@confirm={{this.confirmChanges}}
|
||||
@diff={{this.diff}}
|
||||
@oldValue={{this.selectedText}}
|
||||
@newValue={{this.newSelectedText}}
|
||||
@closeModal={{fn (mut this.showDiffModal) false}}
|
||||
/>
|
||||
{{/if}}
|
|
@ -38,10 +38,14 @@ export default class AiHelperContextMenu extends Component {
|
|||
@tracked caretCoords;
|
||||
@tracked virtualElement;
|
||||
@tracked selectedText = "";
|
||||
@tracked newSelectedText;
|
||||
@tracked loading = false;
|
||||
@tracked oldEditorValue;
|
||||
@tracked newEditorValue;
|
||||
@tracked generatedTitleSuggestions = [];
|
||||
@tracked lastUsedOption = null;
|
||||
@tracked showDiffModal = false;
|
||||
@tracked diff;
|
||||
|
||||
CONTEXT_MENU_STATES = {
|
||||
triggers: "TRIGGERS",
|
||||
|
@ -49,6 +53,7 @@ export default class AiHelperContextMenu extends Component {
|
|||
resets: "RESETS",
|
||||
loading: "LOADING",
|
||||
suggesions: "SUGGESTIONS",
|
||||
review: "REVIEW",
|
||||
};
|
||||
prompts = [];
|
||||
promptTypes = {};
|
||||
|
@ -104,8 +109,9 @@ export default class AiHelperContextMenu extends Component {
|
|||
: "";
|
||||
|
||||
if (this.selectedText.length === 0) {
|
||||
if (this.loading) {
|
||||
// prevent accidentally closing context menu while results loading
|
||||
if (this.loading || this.menuState === this.CONTEXT_MENU_STATES.review) {
|
||||
// prevent accidentally closing context menu
|
||||
// while results loading or in review state
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -163,12 +169,18 @@ export default class AiHelperContextMenu extends Component {
|
|||
_updateSuggestedByAI(data) {
|
||||
const composer = this.args.outletArgs.composer;
|
||||
this.oldEditorValue = this._dEditorInput.value;
|
||||
const newValue = this.oldEditorValue.replace(
|
||||
this.newSelectedText = data.suggestions[0];
|
||||
|
||||
this.newEditorValue = this.oldEditorValue.replace(
|
||||
this.selectedText,
|
||||
data.suggestions[0]
|
||||
this.newSelectedText
|
||||
);
|
||||
composer.set("reply", newValue);
|
||||
this.menuState = this.CONTEXT_MENU_STATES.resets;
|
||||
|
||||
if (data.diff) {
|
||||
this.diff = data.diff;
|
||||
}
|
||||
composer.set("reply", this.newEditorValue);
|
||||
this.menuState = this.CONTEXT_MENU_STATES.review;
|
||||
}
|
||||
|
||||
@afterRender
|
||||
|
@ -240,6 +252,10 @@ export default class AiHelperContextMenu extends Component {
|
|||
data: { mode: option, text: this.selectedText },
|
||||
})
|
||||
.then((data) => {
|
||||
// resets the values if new suggestion is started:
|
||||
this.diff = null;
|
||||
this.newSelectedText = null;
|
||||
|
||||
if (this.prompts[option].name === "generate_titles") {
|
||||
this.menuState = this.CONTEXT_MENU_STATES.suggestions;
|
||||
this.generatedTitleSuggestions = data.suggestions;
|
||||
|
@ -263,4 +279,14 @@ export default class AiHelperContextMenu extends Component {
|
|||
this.closeContextMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
viewChanges() {
|
||||
this.showDiffModal = true;
|
||||
}
|
||||
|
||||
@action
|
||||
confirmChanges() {
|
||||
this.menuState = this.CONTEXT_MENU_STATES.resets;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
margin: 10px 0 10px 0;
|
||||
}
|
||||
|
||||
.text-preview {
|
||||
.text-preview,
|
||||
.inline-diff {
|
||||
ins {
|
||||
background-color: var(--success-low);
|
||||
text-decoration: underline;
|
||||
|
@ -20,6 +21,17 @@
|
|||
}
|
||||
}
|
||||
|
||||
&__old-value {
|
||||
background-color: var(--danger-low);
|
||||
color: var(--danger);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
&__new-value {
|
||||
background-color: var(--success-low);
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.selection-hint {
|
||||
font-size: var(--font-down-2);
|
||||
margin-bottom: 20px;
|
||||
|
@ -34,7 +46,7 @@
|
|||
background: var(--secondary);
|
||||
box-shadow: var(--shadow-dropdown);
|
||||
padding: 0.25rem;
|
||||
max-width: 15rem;
|
||||
max-width: 25rem;
|
||||
border: 1px solid var(--primary-low);
|
||||
list-style: none;
|
||||
z-index: 999;
|
||||
|
@ -79,6 +91,12 @@
|
|||
align-items: center;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
|
||||
&__review {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.d-editor-input.loading {
|
||||
|
|
|
@ -22,6 +22,10 @@ en:
|
|||
loading: "AI is generating"
|
||||
cancel: "Cancel"
|
||||
regen: "Try Again"
|
||||
view_changes: "View Changes"
|
||||
confirm: "Confirm"
|
||||
revert: "Revert"
|
||||
changes: "Changes"
|
||||
reviewables:
|
||||
model_used: "Model used:"
|
||||
accuracy: "Accuracy:"
|
||||
|
|
|
@ -14,6 +14,7 @@ RSpec.describe "AI Composer helper", type: :system, js: true do
|
|||
let(:composer) { PageObjects::Components::Composer.new }
|
||||
let(:ai_helper_context_menu) { PageObjects::Components::AIHelperContextMenu.new }
|
||||
let(:ai_helper_modal) { PageObjects::Modals::AiHelper.new }
|
||||
let(:diff_modal) { PageObjects::Modals::DiffModal.new }
|
||||
|
||||
context "when using the translation mode" do
|
||||
let(:mode) { OpenAiCompletionsInferenceStubs::TRANSLATE }
|
||||
|
@ -140,6 +141,7 @@ RSpec.describe "AI Composer helper", type: :system, js: true do
|
|||
composer.composer_input.value == OpenAiCompletionsInferenceStubs.translated_response.strip
|
||||
end
|
||||
|
||||
ai_helper_context_menu.click_confirm_button
|
||||
expect(ai_helper_context_menu).to be_showing_resets
|
||||
end
|
||||
|
||||
|
@ -154,10 +156,26 @@ RSpec.describe "AI Composer helper", type: :system, js: true do
|
|||
composer.composer_input.value == OpenAiCompletionsInferenceStubs.translated_response.strip
|
||||
end
|
||||
|
||||
ai_helper_context_menu.click_confirm_button
|
||||
ai_helper_context_menu.click_undo_button
|
||||
expect(composer.composer_input.value).to eq(OpenAiCompletionsInferenceStubs.spanish_text)
|
||||
end
|
||||
|
||||
it "reverts results when revert button is clicked" do
|
||||
trigger_context_menu(OpenAiCompletionsInferenceStubs.spanish_text)
|
||||
ai_helper_context_menu.click_ai_button
|
||||
ai_helper_context_menu.select_helper_model(
|
||||
OpenAiCompletionsInferenceStubs.text_mode_to_id(mode),
|
||||
)
|
||||
|
||||
wait_for do
|
||||
composer.composer_input.value == OpenAiCompletionsInferenceStubs.translated_response.strip
|
||||
end
|
||||
|
||||
ai_helper_context_menu.click_revert_button
|
||||
expect(composer.composer_input.value).to eq(OpenAiCompletionsInferenceStubs.spanish_text)
|
||||
end
|
||||
|
||||
it "reverts results when Ctrl/Cmd + Z is pressed on the keyboard" do
|
||||
trigger_context_menu(OpenAiCompletionsInferenceStubs.spanish_text)
|
||||
ai_helper_context_menu.click_ai_button
|
||||
|
@ -173,12 +191,57 @@ RSpec.describe "AI Composer helper", type: :system, js: true do
|
|||
expect(composer.composer_input.value).to eq(OpenAiCompletionsInferenceStubs.spanish_text)
|
||||
end
|
||||
|
||||
it "confirms the results when confirm button is pressed" do
|
||||
trigger_context_menu(OpenAiCompletionsInferenceStubs.spanish_text)
|
||||
ai_helper_context_menu.click_ai_button
|
||||
ai_helper_context_menu.select_helper_model(
|
||||
OpenAiCompletionsInferenceStubs.text_mode_to_id(mode),
|
||||
)
|
||||
|
||||
wait_for do
|
||||
composer.composer_input.value == OpenAiCompletionsInferenceStubs.translated_response.strip
|
||||
end
|
||||
|
||||
ai_helper_context_menu.click_confirm_button
|
||||
expect(composer.composer_input.value).to eq(
|
||||
OpenAiCompletionsInferenceStubs.translated_response.strip,
|
||||
)
|
||||
end
|
||||
|
||||
it "hides the context menu when pressing Escape on the keyboard" do
|
||||
trigger_context_menu(OpenAiCompletionsInferenceStubs.spanish_text)
|
||||
ai_helper_context_menu.click_ai_button
|
||||
ai_helper_context_menu.press_escape_key
|
||||
expect(ai_helper_context_menu).to have_no_context_menu
|
||||
end
|
||||
|
||||
it "shows the changes in a modal when view changes button is pressed" do
|
||||
trigger_context_menu(OpenAiCompletionsInferenceStubs.spanish_text)
|
||||
ai_helper_context_menu.click_ai_button
|
||||
ai_helper_context_menu.select_helper_model(
|
||||
OpenAiCompletionsInferenceStubs.text_mode_to_id(mode),
|
||||
)
|
||||
|
||||
wait_for do
|
||||
composer.composer_input.value == OpenAiCompletionsInferenceStubs.translated_response.strip
|
||||
end
|
||||
|
||||
ai_helper_context_menu.click_view_changes_button
|
||||
expect(diff_modal).to be_visible
|
||||
expect(diff_modal.old_value).to eq(
|
||||
OpenAiCompletionsInferenceStubs.spanish_text.gsub(/[[:space:]]+/, " ").strip,
|
||||
)
|
||||
expect(diff_modal.new_value).to eq(
|
||||
OpenAiCompletionsInferenceStubs
|
||||
.translated_response
|
||||
.gsub(/[[:space:]]+/, " ")
|
||||
.gsub(/[‘’]/, "'")
|
||||
.gsub(/[“”]/, '"')
|
||||
.strip,
|
||||
)
|
||||
diff_modal.confirm_changes
|
||||
expect(ai_helper_context_menu).to be_showing_resets
|
||||
end
|
||||
end
|
||||
|
||||
context "when using the proofreading mode" do
|
||||
|
|
|
@ -10,6 +10,7 @@ module PageObjects
|
|||
SUGGESTIONS_STATE_SELECTOR = "#{CONTEXT_MENU_SELECTOR}__suggestions"
|
||||
LOADING_STATE_SELECTOR = "#{CONTEXT_MENU_SELECTOR}__loading"
|
||||
RESETS_STATE_SELECTOR = "#{CONTEXT_MENU_SELECTOR}__resets"
|
||||
REVIEW_STATE_SELECTOR = "#{CONTEXT_MENU_SELECTOR}__review"
|
||||
|
||||
def click_ai_button
|
||||
find("#{TRIGGER_STATE_SELECTOR} .btn").click
|
||||
|
@ -27,6 +28,18 @@ module PageObjects
|
|||
find("#{RESETS_STATE_SELECTOR} .undo").click
|
||||
end
|
||||
|
||||
def click_revert_button
|
||||
find("#{REVIEW_STATE_SELECTOR} .revert").click
|
||||
end
|
||||
|
||||
def click_view_changes_button
|
||||
find("#{REVIEW_STATE_SELECTOR} .view-changes").click
|
||||
end
|
||||
|
||||
def click_confirm_button
|
||||
find("#{REVIEW_STATE_SELECTOR} .confirm").click
|
||||
end
|
||||
|
||||
def press_undo_keys
|
||||
find(COMPOSER_EDITOR_SELECTOR).send_keys([:control, "z"])
|
||||
end
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module PageObjects
|
||||
module Modals
|
||||
class DiffModal < PageObjects::Modals::Base
|
||||
def visible?
|
||||
page.has_css?(".composer-ai-helper-modal", wait: 5)
|
||||
end
|
||||
|
||||
def confirm_changes
|
||||
find(".modal-footer button.confirm", wait: 5).click
|
||||
end
|
||||
|
||||
def old_value
|
||||
find(".composer-ai-helper-modal__old-value").text
|
||||
end
|
||||
|
||||
def new_value
|
||||
find(".composer-ai-helper-modal__new-value").text
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue