diff --git a/assets/javascripts/discourse/components/custom-wizard-composer-editor.js.es6 b/assets/javascripts/discourse/components/custom-wizard-composer-editor.js.es6 index 95ae56c0..4e035236 100644 --- a/assets/javascripts/discourse/components/custom-wizard-composer-editor.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-composer-editor.js.es6 @@ -18,6 +18,8 @@ import { inject as service } from "@ember/service"; const IMAGE_MARKDOWN_REGEX = /!\[(.*?)\|(\d{1,4}x\d{1,4})(,\s*\d{1,3}%)?(.*?)\]\((upload:\/\/.*?)\)(?!(.*`))/g; +export const wizardComposerEdtiorEventPrefix = "wizard-editor"; + export default ComposerEditor.extend({ modal: service(), @@ -33,29 +35,15 @@ export default ComposerEditor.extend({ draftStatus: "null", replyPlaceholder: alias("field.translatedPlaceholder"), wizardEventFieldId: null, - composerEventPrefix: "wizard-editor", + composerEventPrefix: wizardComposerEdtiorEventPrefix, @on("didInsertElement") _composerEditorInit() { - const $input = $(this.element.querySelector(".d-editor-input")); + this._super(...arguments); - if (this.siteSettings.enable_mentions) { - $input.autocomplete({ - template: findRawTemplate("user-selector-autocomplete"), - dataSource: (term) => this._userSearchTerm.call(this, term), - key: "@", - transformComplete: (v) => v.username || v.name, - afterComplete: (value) => { - this.composer.set("reply", value); - scheduleOnce("afterRender", () => $input.blur().focus()); - }, - triggerRule: (textarea) => - !inCodeBlock(textarea.value, caretPosition(textarea)), - }); - } + if (this.siteSettings.mentionables_enabled) { + const $input = $(this.element.querySelector(".d-editor-input")); - const siteSettings = this.siteSettings; - if (siteSettings.mentionables_enabled) { Site.currentProp("mentionable_items", this.wizard.mentionable_items); const { SEPARATOR } = requirejs( "discourse/plugins/discourse-mentionables/discourse/lib/discourse-markdown/mentionable-items" @@ -73,15 +61,14 @@ export default ComposerEditor.extend({ }, transformComplete: (item) => item.model.slug, dataSource: (term) => - term.match(/\s/) ? null : searchMentionableItem(term, siteSettings), + term.match(/\s/) + ? null + : searchMentionableItem(term, this.siteSettings), triggerRule: (textarea) => !inCodeBlock(textarea.value, caretPosition(textarea)), }); } - $input.on("scroll", this._throttledSyncEditorAndPreviewScroll); - this._bindUploadTarget(); - const field = this.field; this.editorInputClass = `.${dasherize(field.type)}-${dasherize( field.id diff --git a/assets/javascripts/discourse/components/custom-wizard-step.js.es6 b/assets/javascripts/discourse/components/custom-wizard-step.js.es6 index 44bef872..86ae1afa 100644 --- a/assets/javascripts/discourse/components/custom-wizard-step.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-step.js.es6 @@ -7,8 +7,19 @@ import { cook } from "discourse/lib/text"; import CustomWizard, { updateCachedWizard, } from "discourse/plugins/discourse-custom-wizard/discourse/models/custom-wizard"; -import { alias, not } from "@ember/object/computed"; +import { alias, not, or } from "@ember/object/computed"; import discourseLater from "discourse-common/lib/later"; +import { wizardComposerEdtiorEventPrefix } from "./custom-wizard-composer-editor"; + +const uploadStartedEventKeys = ["upload-started"]; +const uploadEndedEventKeys = [ + "upload-success", + "upload-error", + "upload-cancelled", + "uploads-cancelled", + "uploads-aborted", + "all-uploads-complete", +]; export default Component.extend({ classNameBindings: [":wizard-step", "step.id"], @@ -28,6 +39,17 @@ export default Component.extend({ cook(this.step.translatedDescription).then((cookedDescription) => { this.set("cookedDescription", cookedDescription); }); + + uploadStartedEventKeys.forEach((key) => { + this.appEvents.on(`${wizardComposerEdtiorEventPrefix}:${key}`, () => { + this.set("uploading", true); + }); + }); + uploadEndedEventKeys.forEach((key) => { + this.appEvents.on(`${wizardComposerEdtiorEventPrefix}:${key}`, () => { + this.set("uploading", false); + }); + }); }, didInsertElement() { @@ -40,6 +62,7 @@ export default Component.extend({ showNextButton: not("step.final"), showDoneButton: alias("step.final"), + btnsDisabled: or("saving", "uploading"), @discourseComputed( "step.index", diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-step.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-step.hbs index b9f0c06e..fd4e038e 100644 --- a/assets/javascripts/discourse/templates/components/custom-wizard-step.hbs +++ b/assets/javascripts/discourse/templates/components/custom-wizard-step.hbs @@ -60,7 +60,7 @@ type="button" class="wizard-btn next primary" {{action "nextStep"}} - disabled={{saving}} + disabled={{btnsDisabled}} tabindex={{primaryButtonIndex}} > {{i18n "wizard.next"}} @@ -73,7 +73,7 @@ type="button" class="wizard-btn done" {{action "done"}} - disabled={{saving}} + disabled={{btnsDisabled}} tabindex={{primaryButtonIndex}} > {{i18n "wizard.done_custom"}} diff --git a/assets/stylesheets/common/wizard/wizard.scss b/assets/stylesheets/common/wizard/wizard.scss index 1bb0d086..4c8f2357 100644 --- a/assets/stylesheets/common/wizard/wizard.scss +++ b/assets/stylesheets/common/wizard/wizard.scss @@ -160,6 +160,10 @@ body.custom-wizard { display: flex; justify-content: flex-end; } + + .powered-by-discourse { + display: none; + } } /* IE11 hacks */ diff --git a/plugin.rb b/plugin.rb index 15723558..db0dd9be 100644 --- a/plugin.rb +++ b/plugin.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # name: discourse-custom-wizard # about: Forms for Discourse. Better onboarding, structured posting, data enrichment, automated actions and much more. -# version: 2.6.8 +# version: 2.6.11 # authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George, Kaitlin Maddever, Juan Marcos Gutierrez Ramos # url: https://github.com/paviliondev/discourse-custom-wizard # contact_emails: development@pavilion.tech @@ -185,17 +185,17 @@ after_initialize do end end - add_to_serializer(:site, :include_wizard_required?) do + add_to_class(:site_serializer, :include_wizard_required?) do scope.is_admin? && Wizard.new(scope.user).requires_completion? end - add_to_serializer(:site, :complete_custom_wizard) do + add_to_class(:site_serializer, :complete_custom_wizard) do if scope.user && requires_completion = CustomWizard::Wizard.prompt_completion(scope.user) requires_completion.map { |w| { name: w[:name], url: "/w/#{w[:id]}" } } end end - add_to_serializer(:site, :include_complete_custom_wizard?) do + add_to_class(:site_serializer, :include_complete_custom_wizard?) do complete_custom_wizard.present? end diff --git a/test/javascripts/acceptance/field-test.js b/test/javascripts/acceptance/field-test.js index 455d420d..fdf99da5 100644 --- a/test/javascripts/acceptance/field-test.js +++ b/test/javascripts/acceptance/field-test.js @@ -1,8 +1,16 @@ -import { click, fillIn, triggerKeyEvent, visit } from "@ember/test-helpers"; +import { getOwner } from "@ember/application"; +import { + click, + fillIn, + settled, + triggerKeyEvent, + visit, +} from "@ember/test-helpers"; import { test } from "qunit"; import { acceptance, count, + createFile, exists, query, visible, @@ -11,6 +19,8 @@ import { allFieldsWizard } from "../helpers/wizard"; import tagsJson from "../fixtures/tags"; import usersJson from "../fixtures/users"; +const wizardComposerEdtiorEventPrefix = "wizard-editor"; + acceptance("Field | Fields", function (needs) { needs.pretender((server, helper) => { server.get("/w/wizard.json", () => helper.response(allFieldsWizard)); @@ -18,6 +28,28 @@ acceptance("Field | Fields", function (needs) { helper.response({ results: tagsJson["tags"] }) ); server.get("/u/search/users", () => helper.response(usersJson)); + + server.post( + "/uploads.json", + () => { + return helper.response({ + extension: "jpeg", + filesize: 126177, + height: 800, + human_filesize: "123 KB", + id: 202, + original_filename: "avatar.PNG.jpg", + retain_hours: null, + short_path: "/uploads/short-url/yoj8pf9DdIeHRRULyw7i57GAYdz.jpeg", + short_url: "upload://yoj8pf9DdIeHRRULyw7i57GAYdz.jpeg", + thumbnail_height: 320, + thumbnail_width: 690, + url: "/images/discourse-logo-sketch-small.png", + width: 1920, + }); + }, + 500 // this delay is important to slow down the uploads a bit so we can let elements of the interface update + ); }); test("Text", async function (assert) { @@ -54,6 +86,33 @@ acceptance("Field | Fields", function (needs) { "Input in composer" ); }); + + test("Composer - Upload Disables Next Button", async function (assert) { + await visit("/w/wizard"); + const appEvents = getOwner(this).lookup("service:app-events"); + const done = assert.async(); + + appEvents.on( + `${wizardComposerEdtiorEventPrefix}:all-uploads-complete`, + async () => { + await settled(); + assert.ok(!exists(".wizard-btn.next.primary:disabled")); + done(); + } + ); + + appEvents.on( + `${wizardComposerEdtiorEventPrefix}:upload-started`, + async () => { + await settled(); + assert.ok(exists(".wizard-btn.next.primary:disabled")); + } + ); + + const image = createFile("avatar.png"); + appEvents.trigger(`${wizardComposerEdtiorEventPrefix}:add-files`, image); + }); + test("Composer - Hyperlink", async function (assert) { await visit("/w/wizard"); assert.ok(