DEV: Cancel popup should abort request (#497)

This commit is contained in:
Keegan George 2024-02-28 13:32:45 -08:00 committed by GitHub
parent 484fd1435b
commit 6a30b06a55
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 81 additions and 46 deletions

View File

@ -11,12 +11,9 @@ import DTextarea from "discourse/components/d-textarea";
import autoFocus from "discourse/modifiers/auto-focus";
import icon from "discourse-common/helpers/d-icon";
import i18n from "discourse-common/helpers/i18n";
import { IMAGE_MARKDOWN_REGEX } from "../../lib/utilities";
export default class AiImageCaptionContainer extends Component {
@service imageCaptionPopup;
@service appEvents;
@service composer;
@action
updateCaption(event) {
@ -26,22 +23,7 @@ export default class AiImageCaptionContainer extends Component {
@action
saveCaption() {
const index = this.imageCaptionPopup.imageIndex;
const matchingPlaceholder =
this.composer.model.reply.match(IMAGE_MARKDOWN_REGEX);
if (matchingPlaceholder) {
const match = matchingPlaceholder[index];
const replacement = match.replace(
IMAGE_MARKDOWN_REGEX,
`![${this.imageCaptionPopup.newCaption}|$2$3$4]($5)`
);
if (match) {
this.appEvents.trigger("composer:replace-text", match, replacement);
}
}
this.imageCaptionPopup.updateCaption();
this.hidePopup();
}
@ -63,6 +45,11 @@ export default class AiImageCaptionContainer extends Component {
@action
hidePopup() {
this.imageCaptionPopup.showPopup = false;
if (this.imageCaptionPopup._request) {
this.imageCaptionPopup._request.abort();
this.imageCaptionPopup._request = null;
this.imageCaptionPopup.toggleLoadingState(false);
}
}
<template>
@ -91,7 +78,7 @@ export default class AiImageCaptionContainer extends Component {
@action={{this.saveCaption}}
/>
<DButton
class="btn-flat"
class="btn-flat cancel-request"
@label="cancel"
@action={{this.hidePopup}}
/>

View File

@ -1,10 +1,43 @@
import { tracked } from "@glimmer/tracking";
import Service from "@ember/service";
import Service, { inject as service } from "@ember/service";
import { IMAGE_MARKDOWN_REGEX } from "../lib/utilities";
export default class ImageCaptionPopup extends Service {
@service composer;
@service appEvents;
@tracked showPopup = false;
@tracked imageIndex = null;
@tracked imageSrc = null;
@tracked newCaption = null;
@tracked loading = false;
@tracked popupTrigger = null;
@tracked _request = null;
updateCaption() {
const matchingPlaceholder =
this.composer.model.reply.match(IMAGE_MARKDOWN_REGEX);
if (matchingPlaceholder) {
const match = matchingPlaceholder[this.imageIndex];
const replacement = match.replace(
IMAGE_MARKDOWN_REGEX,
`![${this.newCaption}|$2$3$4]($5)`
);
if (match) {
this.appEvents.trigger("composer:replace-text", match, replacement);
}
}
}
toggleLoadingState(loading) {
if (loading) {
this.popupTrigger?.classList.add("disabled");
return (this.loading = true);
}
this.popupTrigger?.classList.remove("disabled");
return (this.loading = false);
}
}

View File

@ -2,7 +2,6 @@ import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { apiInitializer } from "discourse/lib/api";
import I18n from "discourse-i18n";
import { IMAGE_MARKDOWN_REGEX } from "../discourse/lib/utilities";
export default apiInitializer("1.25.0", (api) => {
const buttonAttrs = {
@ -10,10 +9,7 @@ export default apiInitializer("1.25.0", (api) => {
icon: "discourse-sparkles",
class: "generate-caption",
};
const imageCaptionPopup = api.container.lookup("service:imageCaptionPopup");
const settings = api.container.lookup("service:site-settings");
const appEvents = api.container.lookup("service:app-events");
const site = api.container.lookup("site:main");
if (!settings.ai_helper_enabled_features.includes("image_caption")) {
return;
@ -23,7 +19,15 @@ export default apiInitializer("1.25.0", (api) => {
buttonAttrs.class,
buttonAttrs.icon,
(event) => {
if (event.target.classList.contains("generate-caption")) {
const imageCaptionPopup = api.container.lookup(
"service:imageCaptionPopup"
);
imageCaptionPopup.popupTrigger = event.target;
if (
imageCaptionPopup.popupTrigger.classList.contains("generate-caption")
) {
const buttonWrapper = event.target.closest(".button-wrapper");
const imageIndex = parseInt(
buttonWrapper.getAttribute("data-image-index"),
@ -34,20 +38,24 @@ export default apiInitializer("1.25.0", (api) => {
.querySelector("img")
.getAttribute("src");
imageCaptionPopup.loading = true;
imageCaptionPopup.toggleLoadingState(true);
const site = api.container.lookup("site:main");
if (!site.mobileView) {
imageCaptionPopup.showPopup = !imageCaptionPopup.showPopup;
}
event.target.classList.add("disabled");
imageCaptionPopup._request = ajax(
`/discourse-ai/ai-helper/caption_image`,
{
method: "POST",
data: {
image_url: imageSrc,
},
}
);
ajax(`/discourse-ai/ai-helper/caption_image`, {
method: "POST",
data: {
image_url: imageSrc,
},
})
imageCaptionPopup._request
.then(({ caption }) => {
imageCaptionPopup.imageSrc = imageSrc;
imageCaptionPopup.imageIndex = imageIndex;
@ -55,22 +63,12 @@ export default apiInitializer("1.25.0", (api) => {
if (site.mobileView) {
// Auto-saves caption on mobile view
const composer = api.container.lookup("service:composer");
const matchingPlaceholder =
composer.model.reply.match(IMAGE_MARKDOWN_REGEX);
const match = matchingPlaceholder[imageIndex];
const replacement = match.replace(
IMAGE_MARKDOWN_REGEX,
`![${imageCaptionPopup.newCaption}|$2$3$4]($5)`
);
appEvents.trigger("composer:replace-text", match, replacement);
imageCaptionPopup.updateCaption();
}
})
.catch(popupAjaxError)
.finally(() => {
imageCaptionPopup.loading = false;
event.target.classList.remove("disabled");
imageCaptionPopup.toggleLoadingState(false);
});
}
}

View File

@ -36,6 +36,15 @@ RSpec.describe "AI image caption", type: :system, js: true do
wait_for { page.find(".image-wrapper img")["alt"] == caption_with_attrs }
expect(page.find(".image-wrapper img")["alt"]).to eq(caption_with_attrs)
end
it "should allow you to cancel a caption request" do
visit("/latest")
page.find("#create-topic").click
attach_file([file_path]) { composer.click_toolbar_button("upload") }
popup.click_generate_caption
popup.cancel_caption
expect(popup).to have_no_disabled_generate_button
end
end
context "when triggering caption with AI on mobile", mobile: true do

View File

@ -18,6 +18,14 @@ module PageObjects
def save_caption
find("#{CAPTION_POPUP_SELECTOR} .btn-primary").click
end
def cancel_caption
find("#{CAPTION_POPUP_SELECTOR} .cancel-request").click
end
def has_no_disabled_generate_button?
page.has_no_css?("#{GENERATE_CAPTION_SELECTOR}.disabled", visible: false)
end
end
end
end