diff --git a/app/assets/stylesheets/wizard.scss b/app/assets/stylesheets/wizard.scss index c40d1e159493b..0509c91026c48 100644 --- a/app/assets/stylesheets/wizard.scss +++ b/app/assets/stylesheets/wizard.scss @@ -59,7 +59,7 @@ body.wizard { .discourse-logo svg { position: relative; - height: 70px; + height: 50px; width: auto; @include viewport.until(sm) { @@ -72,12 +72,6 @@ body.wizard { } } } - - .wizard-canvas { - position: fixed; - top: 0; - pointer-events: none; - } } // Refactored SCSS @@ -98,8 +92,7 @@ body.wizard { &__step { margin-top: 1em; - max-width: 50%; - min-width: 35%; + max-width: 700px; width: 100%; @include viewport.until(lg) { @@ -118,30 +111,10 @@ body.wizard { width: 100%; } - &__step-counter { - text-align: center; - font-weight: 700; - color: var(--primary-high); - text-shadow: 1px 1px 12px var(--secondary); - } - - &__step-description { - font-size: var(--font-up-2); - flex: 1 0 40%; - - @include viewport.until(md) { - font-size: var(--font-up-2); - } - } - &__field { margin-bottom: 1em; } - &__field.checkbox-field { - margin-bottom: 1.5em; - } - &__field.invalid input { outline: 0; border: 3px solid var(--danger); @@ -165,43 +138,19 @@ body.wizard { } } - &__sidebar { - width: 230px; - box-sizing: border-box; - margin-right: 1em; - - @media only screen and (width <= 925px) { - width: 100%; - margin-left: auto; - margin-right: auto; - } - - + .wizard-container__fields { - padding: 1em; - background: var(--primary-very-low); - width: auto; - border-radius: 0.5em; - margin-top: -1em; - - @media only screen and (width <= 925px) { - display: none; - } - } - } - &__step-header { text-align: center; margin-bottom: 3em; &--emoji img { - width: 30px; - height: 30px; + width: 40px; + height: 40px; margin-bottom: 1em; } } &__step-title { - font-size: 2.75em; + font-size: 2.5em; color: var(--primary); line-height: var(--line-height-medium); margin: 0 0 0.5em 0; @@ -227,28 +176,6 @@ body.wizard { } } - &__buttons-left { - display: flex; - flex-wrap: wrap; - gap: 1em; - align-items: center; - - @include viewport.until(sm) { - order: 2; - } - } - - &__buttons-right { - display: flex; - align-items: center; - font-weight: bold; - - @include viewport.until(sm) { - margin-right: 0; - flex-direction: column; - } - } - &__step-text { display: inline; @@ -300,71 +227,9 @@ body.wizard { background-color: var(--primary-medium); } - &__button.finish { - @include viewport.until(sm) { - order: 2; - } - } - - &__button.next { - min-width: 70px; - margin-left: 1em; - - @include viewport.until(sm) { - order: 1; - margin-left: 0; - } - } - - &__button.danger { - background-color: var(--danger); - color: var(--secondary); - } - - &__button.danger:hover, - &__button.danger:focus { - background-color: var(--danger-hover); - } - - &__button.danger:active { - background-color: var(--danger-medium); - } - - &__button.danger:disabled { - background-color: var(--danger-low); - } - - &__button-upload { - display: block; - background-color: transparent; - margin-top: 1em; - border: 1px solid var(--tertiary-high); - text-align: center; - color: var(--tertiary-high); - } - - &__button-upload:hover { - background-color: transparent; - border-color: var(--tertiary-hover); - color: var(--tertiary-hover); - } - - &__button-upload svg { - margin-left: 0.5em; - } - - .wizard-hidden-upload-field { - visibility: hidden; - position: absolute; - } - - &__button:last-child { - margin-right: 0; - } - &__step-footer { display: flex; - justify-content: space-between; + justify-content: space-around; align-items: center; @include viewport.until(sm) { @@ -428,92 +293,6 @@ body.wizard { border-radius: 4px; } - &__field.checkbox-field .wizard-container__label { - cursor: pointer; - display: inline-block; - } - - &__checkbox-slider { - display: inline-block; - background: var(--primary-low-mid); - border-radius: 16px; - width: 50px; - height: 28px; - margin-right: 0.5em; - position: relative; - vertical-align: middle; - transition: background 0.25s; - - @media only screen and (width <= 568px) { - height: 20px; - width: 35px; - } - } - - &__checkbox-slider::before, - &__checkbox-slider::after { - content: ""; - display: block; - position: absolute; - } - - &__checkbox-slider::after { - content: "\2713"; // checkmark - color: var(--secondary); - top: 4px; - left: 9px; - - @media only screen and (width <= 568px) { - top: 3px; - left: 5px; - font-size: var(--font-down-3); - } - } - - &__checkbox-slider::before { - background: var(--secondary); - border-radius: 50%; - width: 20px; - height: 20px; - top: 4px; - left: 4px; - transition: left 0.25s; - - @media only screen and (width <= 568px) { - height: 12px; - width: 12px; - } - } - - &__field.checkbox-field - .wizard-container__label:hover - .wizard-container__checkbox-slider::before { - background: var(--secondary); - box-shadow: 0 0 0 1px rgb(0, 0, 0, 0.15); - } - - &__checkbox:checked + .wizard-container__checkbox-slider { - background: var(--tertiary); - } - - &__checkbox:checked + .wizard-container__checkbox-slider::before { - left: 26px; - - @media only screen and (width <= 568px) { - left: 20px; - } - } - - &__checkbox { - position: absolute; - visibility: hidden; - } - - &__checkbox-label { - position: relative; - top: 2px; - } - &__radio { position: absolute; visibility: hidden; diff --git a/app/controllers/steps_controller.rb b/app/controllers/steps_controller.rb index aeed15ca36afb..058ffe3f0843c 100644 --- a/app/controllers/steps_controller.rb +++ b/app/controllers/steps_controller.rb @@ -13,7 +13,6 @@ def update if updater.success? result = { success: "OK" } - result[:refresh_required] = true if updater.refresh_required? render json: result else errors = [] diff --git a/app/serializers/wizard_field_choice_serializer.rb b/app/serializers/wizard_field_choice_serializer.rb index 0a1fdf3e8925f..ec3950252623f 100644 --- a/app/serializers/wizard_field_choice_serializer.rb +++ b/app/serializers/wizard_field_choice_serializer.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class WizardFieldChoiceSerializer < ApplicationSerializer - attributes :id, :label, :extra_label, :description, :icon, :data + attributes :id, :label, :extra_label, :description, :data def id object.id @@ -36,14 +36,6 @@ def include_description? description.present? end - def icon - object.icon - end - - def include_icon? - object.icon.present? - end - def data result = object.data.dup result.delete(:id) diff --git a/app/serializers/wizard_field_serializer.rb b/app/serializers/wizard_field_serializer.rb index b4cb8a3b56377..51d7d0839f902 100644 --- a/app/serializers/wizard_field_serializer.rb +++ b/app/serializers/wizard_field_serializer.rb @@ -1,17 +1,7 @@ # frozen_string_literal: true class WizardFieldSerializer < ApplicationSerializer - attributes :id, - :type, - :required, - :value, - :label, - :placeholder, - :description, - :extra_description, - :icon, - :disabled, - :show_in_sidebar + attributes :id, :type, :required, :value, :label, :placeholder, :description, :extra_description has_many :choices, serializer: WizardFieldChoiceSerializer, embed: :objects def id @@ -77,30 +67,6 @@ def include_extra_description? extra_description.present? end - def icon - object.icon - end - - def include_icon? - object.icon.present? - end - - def disabled - object.disabled - end - - def include_disabled? - object.disabled - end - - def show_in_sidebar - object.show_in_sidebar - end - - def include_show_in_sidebar? - object.show_in_sidebar.present? - end - def include_choices? object.type == "dropdown" || object.type == "radio" end diff --git a/app/serializers/wizard_step_serializer.rb b/app/serializers/wizard_step_serializer.rb index 1fe6a15cafab6..78a53bbb7ecfb 100644 --- a/app/serializers/wizard_step_serializer.rb +++ b/app/serializers/wizard_step_serializer.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class WizardStepSerializer < ApplicationSerializer - attributes :id, :next, :previous, :description, :title, :index, :emoji + attributes :id, :title, :index, :emoji has_many :fields, serializer: WizardFieldSerializer, embed: :objects def id @@ -12,22 +12,6 @@ def index object.index end - def next - object.next.id if object.next.present? - end - - def include_next? - object.next.present? - end - - def previous - object.previous.id if object.previous.present? - end - - def include_previous? - object.previous.present? - end - def i18n_key @i18n_key ||= "wizard.step.#{object.id}".underscore end @@ -39,15 +23,6 @@ def translate(sub_key, vars = nil) vars.nil? ? I18n.t(key) : I18n.t(key, vars) end - def description - key = object.disabled ? "disabled" : "description" - translate(key, object.description_vars) - end - - def include_description? - description.present? - end - def title translate("title") end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index f9e96ed295ab2..8482e83004b26 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -8585,83 +8585,4 @@ en: wizard_js: wizard: - jump_in: "Jump in!" - finish: "Exit setup" - back: "Back" - next: "Next" - configure_more: "Configure more…" - step-text: "Step" - step: "%{current} of %{total}" - upload: "Upload file" - uploading: "Uploading…" - upload_error: "Sorry, there was an error uploading that file. Please try again." - - staff_count: - one: "Your community has %{count} staff (you)." - other: "Your community has %{count} staff, including you." - - invites: - add_user: "add" - none_added: "You haven’t invited any staff. Are you sure you want to continue?" - roles: - admin: "Admin" - moderator: "Moderator" - regular: "Regular User" - - homepage_choices: - custom: - label: "Custom" - description: "Display a %{type}-focused homepage with users landing on %{landingPage}" - style_type: - categories: "category" - topics: "topic" - - top_menu_items: - new: "New" - unread: "Unread" - top: "Top" - latest: "Latest" - hot: "Hot" - categories: "Categories" - unseen: "Unseen" - read: "Read" - bookmarks: "Bookmarks" - - previews: - topic_title: "What books are you reading?" - share_button: "Share" - reply_button: "Reply" - topic_preview: "Topic preview" - homepage_preview: "Homepage preview" - - homepage_preview: - nav_buttons: - all_categories: "all categories" - topic_titles: - what_books: "What books are you reading?" - what_movies: "What movies have you seen recently?" - random_fact: "Random fact of the day" - tv_show: "Recommend a TV show" - what_hobbies: "What are your hobbies?" - what_music: "What are you listening to right now?" - funniest_thing: "Funniest thing you've seen today" - share_art: "Share your art!" - topic_ops: - what_books: | - We all love to read, let's use this topic to share our - current or recent reads. I'm a fantasy fan and I've been - re-reading The Lord of the Rings for the 100th time. - What about you? - category_descriptions: - icebreakers: "Get to know your fellow community members with fun questions." - news: "Discuss the latest news and events." - site_feedback: "Share your thoughts on the community and suggest improvements." - category_names: - icebreakers: "Icebreakers" - news: "News" - site_feedback: "Site Feedback" - table_headers: - topic: "Topic" - replies: "Replies" - views: "Views" - activity: "Activity" + jump_in: "Let's go!" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 37afcf179f59a..a110afba27ecf 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -5526,24 +5526,16 @@ en: wizard: title: "Discourse Setup" step: - introduction: - title: "About your site" - description: "These will be shown on your login and any public pages. You can always change them later." + setup: + title: "Getting started" + description: "Let's get your community set up with the basics. You can always change these later." fields: title: label: "Community name" - placeholder: "Jane’s Hangout" - site_description: - label: "Describe your community in a sentence" - placeholder: "A place for Jane and her friends to discuss cool stuff" + placeholder: "Jane's Hangout" default_locale: label: "Language" - - privacy: - title: "Member access" - - fields: login_required: label: "Visibility" description: "Is your community public or private?" @@ -5567,89 +5559,6 @@ en: label: "No, new members can join immediately" "yes": label: "Yes, new members must be approved by moderators" - chat_enabled: - placeholder: "Enable chat" - extra_description: "Engage with your members in real time" - - ready: - title: "Your site is ready!" - description: "That's it! You've done the basics to setup your community. Now you can jump in and have a look around, write a welcome topic, and send invites!

Have fun!" - - styling: - title: "Look and feel" - fields: - color_scheme: - label: "Color palette" - body_font: - label: "Body font" - site_font: - label: "Font" - heading_font: - label: "Heading font" - styling_preview: - label: "Preview" - homepage_style: - label: "Homepage style" - choices: - latest: - label: "Latest" - description: "Displays the most recently active topics in all categories, helping members stay up-to-date with discussions they care about" - hot: - label: "Hot" - description: "Surfaces trending topics by blending recent and overall popularity, showcases what members are talking about in your community right now" - categories_and_latest_topics: - label: "Categories with latest topics" - description: "Combines the recently active topics across all categories with a list of categories, their description, and total topics" - categories_boxes: - label: "Category boxes" - description: "Displays the categories and their description in a grid, ideal for members to see an overview of the sub-communities of your site" - - branding: - title: "Site logo" - fields: - logo: - label: "Primary logo" - description: "Recommended size: 600 x 200" - logo_small: - label: "Square logo" - description: "Recommended size: 512 x 512. Also used as the favicon and mobile home screen app icon." - - corporate: - title: "Your organization" - description: "The following information will be used in your Terms of Service and About pages. Feel free to skip if no company exists." - - fields: - company_name: - label: "Company name" - placeholder: "Acme Organization" - governing_law: - label: "Governing law" - placeholder: "California law" - contact_url: - label: "Web page" - placeholder: "https://www.example.com/contact-us" - city_for_disputes: - label: "City for disputes" - placeholder: "San Francisco, California" - site_contact: - label: "Automated messages" - description: "All automated Discourse personal messages will be sent from this user, such as flag warnings and backup completion notices." - contact_email: - label: "Point of contact" - placeholder: "example@user.com" - description: "Email address of key contact responsible for this site. Used for critical notifications, and listed on your your about page for urgent matters." - - invites: - title: "Invite Staff" - description: "You’re almost done! Let’s invite some people to help seed your discussions with interesting topics and replies to get your community started." - disabled: "Since local logins are disabled, it’s not possible to send invites to anyone. Please proceed to the next step." - - finished: - title: "Your Discourse is Ready!" - description: | -

If you ever feel like changing these settings, re-run this wizard any time, or visit your admin section; find it next to the wrench icon in the site menu.

-

It is easy to customize your Discourse even further using our powerful theming system. For examples, check out the top themes and components on meta.discourse.org.

-

Have fun, and good luck building your new community!

search_logs: graph_title: "Search Count" diff --git a/frontend/discourse/app/static/wizard/components/fields/checkbox.gjs b/frontend/discourse/app/static/wizard/components/fields/checkbox.gjs deleted file mode 100644 index 67e13b21f3f5b..0000000000000 --- a/frontend/discourse/app/static/wizard/components/fields/checkbox.gjs +++ /dev/null @@ -1,37 +0,0 @@ -/* eslint-disable ember/no-classic-components */ -import Component, { Input } from "@ember/component"; -import { tagName } from "@ember-decorators/component"; -import PluginOutlet from "discourse/components/plugin-outlet"; -import icon from "discourse/helpers/d-icon"; -import lazyHash from "discourse/helpers/lazy-hash"; - -@tagName("") -export default class Checkbox extends Component { - -} diff --git a/frontend/discourse/app/static/wizard/components/fields/checkboxes.gjs b/frontend/discourse/app/static/wizard/components/fields/checkboxes.gjs deleted file mode 100644 index 21a2f2f6c0ef5..0000000000000 --- a/frontend/discourse/app/static/wizard/components/fields/checkboxes.gjs +++ /dev/null @@ -1,55 +0,0 @@ -/* eslint-disable ember/no-classic-components */ -import Component, { Input } from "@ember/component"; -import { on } from "@ember/modifier"; -import { action, set } from "@ember/object"; -import icon from "discourse/helpers/d-icon"; - -export default class Checkboxes extends Component { - init(...args) { - super.init(...args); - this.set("field.value", this.field.value || []); - - for (let choice of this.field.choices) { - if (this.field.value.includes(choice.id)) { - set(choice, "checked", true); - } - } - } - - @action - changed(checkbox) { - let newFieldValue = this.field.value; - const checkboxValue = checkbox.parentElement - .getAttribute("value") - .toLowerCase(); - - if (checkbox.checked) { - newFieldValue.push(checkboxValue); - } else { - const index = newFieldValue.indexOf(checkboxValue); - if (index > -1) { - newFieldValue.splice(index, 1); - } - } - this.set("field.value", newFieldValue); - } - - -} diff --git a/frontend/discourse/app/static/wizard/components/fields/image-previews/generic.gjs b/frontend/discourse/app/static/wizard/components/fields/image-previews/generic.gjs deleted file mode 100644 index 2e02299c4567b..0000000000000 --- a/frontend/discourse/app/static/wizard/components/fields/image-previews/generic.gjs +++ /dev/null @@ -1,10 +0,0 @@ -/* eslint-disable ember/no-classic-components */ -import Component from "@ember/component"; -import { classNameBindings } from "@ember-decorators/component"; - -@classNameBindings(":wizard-image-preview", "fieldClass") -export default class Generic extends Component { - -} diff --git a/frontend/discourse/app/static/wizard/components/fields/image-previews/index.js b/frontend/discourse/app/static/wizard/components/fields/image-previews/index.js deleted file mode 100644 index c6b89d1f9e1bc..0000000000000 --- a/frontend/discourse/app/static/wizard/components/fields/image-previews/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import Generic from "./generic"; -import Logo from "./logo"; -import LogoSmall from "./logo-small"; - -export default { - generic: Generic, - logo: Logo, - "logo-small": LogoSmall, -}; diff --git a/frontend/discourse/app/static/wizard/components/fields/image-previews/logo-small.js b/frontend/discourse/app/static/wizard/components/fields/image-previews/logo-small.js deleted file mode 100644 index 537835a85b456..0000000000000 --- a/frontend/discourse/app/static/wizard/components/fields/image-previews/logo-small.js +++ /dev/null @@ -1,94 +0,0 @@ -import { action } from "@ember/object"; -import { i18n } from "discourse-i18n"; -import { drawHeader } from "../../../lib/preview"; -import PreviewBaseComponent from "../styling-preview/-preview-base"; - -export default class LogoSmall extends PreviewBaseComponent { - width = 375; - height = 100; - image = null; - - didInsertElement() { - super.didInsertElement(...arguments); - this.field.addListener(this.imageChanged); - } - - willDestroyElement() { - super.willDestroyElement(...arguments); - this.field.removeListener(this.imageChanged); - } - - @action - imageChanged() { - this.reload(); - } - - images() { - return { image: this.field.value }; - } - - paint(options) { - const { ctx, colors, font, headingFont, width, height } = options; - const headerHeight = height / 2; - - drawHeader(ctx, colors, width, headerHeight); - - const image = this.image; - const headerMargin = headerHeight * 0.2; - const maxWidth = headerHeight - headerMargin * 2.0; - let imageWidth = image.width; - let ratio = 1.0; - - if (imageWidth > maxWidth) { - ratio = maxWidth / imageWidth; - imageWidth = maxWidth; - } - - this.scaleImage( - image, - headerMargin, - headerMargin, - imageWidth, - image.height * ratio - ); - - const afterLogo = headerMargin * 1.7 + imageWidth; - const fontSize = Math.round(headerHeight * 0.3); - - ctx.font = `Bold ${fontSize}px '${headingFont}'`; - ctx.fillStyle = colors.primary; - const title = i18n("wizard.homepage_preview.topic_titles.what_books"); - ctx.fillText( - title, - headerMargin + imageWidth + 10, - headerHeight - fontSize * 1.8 - ); - - const category = this.categories()[0]; - const badgeSize = height / 13.0; - ctx.beginPath(); - ctx.fillStyle = category.color; - ctx.rect(afterLogo + 2, headerHeight * 0.6, badgeSize, badgeSize); - ctx.fill(); - - ctx.font = `Bold ${badgeSize * 1.2}px '${font}'`; - ctx.fillStyle = colors.primary; - ctx.fillText( - category.name, - afterLogo + badgeSize * 1.5, - headerHeight * 0.6 + badgeSize * 0.9 - ); - - const LINE_HEIGHT = 12; - ctx.font = `${LINE_HEIGHT}px '${font}'`; - const opFirstSentenceLines = i18n( - "wizard.homepage_preview.topic_ops.what_books" - ) - .split(".")[0] - .split("\n"); - for (let i = 0; i < 2; i++) { - const line = height * 0.7 + i * (LINE_HEIGHT * 1.5); - ctx.fillText(opFirstSentenceLines[i], afterLogo, line); - } - } -} diff --git a/frontend/discourse/app/static/wizard/components/fields/image-previews/logo.js b/frontend/discourse/app/static/wizard/components/fields/image-previews/logo.js deleted file mode 100644 index e7fb0e3d4252a..0000000000000 --- a/frontend/discourse/app/static/wizard/components/fields/image-previews/logo.js +++ /dev/null @@ -1,50 +0,0 @@ -import { action } from "@ember/object"; -import { drawHeader } from "../../../lib/preview"; -import PreviewBaseComponent from "../styling-preview/-preview-base"; - -export default class Logo extends PreviewBaseComponent { - width = 400; - height = 100; - image = null; - - didInsertElement() { - super.didInsertElement(...arguments); - this.field.addListener(this.imageChanged); - } - - willDestroyElement() { - super.willDestroyElement(...arguments); - this.field.removeListener(this.imageChanged); - } - - @action - imageChanged() { - this.reload(); - } - - images() { - return { image: this.field.value }; - } - - paint({ ctx, colors, font, width, height }) { - const headerHeight = height / 2; - - drawHeader(ctx, colors, width, headerHeight); - - const image = this.image; - - const headerMargin = headerHeight * 0.2; - - const imageHeight = headerHeight - headerMargin * 2; - const ratio = imageHeight / image.height; - this.scaleImage( - image, - headerMargin, - headerMargin, - image.width * ratio, - imageHeight - ); - - this.drawPills(colors, font, height / 2, { homepageStyle: "hot" }); - } -} diff --git a/frontend/discourse/app/static/wizard/components/fields/image.gjs b/frontend/discourse/app/static/wizard/components/fields/image.gjs deleted file mode 100644 index ae720fd395174..0000000000000 --- a/frontend/discourse/app/static/wizard/components/fields/image.gjs +++ /dev/null @@ -1,128 +0,0 @@ -/* eslint-disable ember/no-classic-components */ -import Component from "@ember/component"; -import { warn } from "@ember/debug"; -import { service } from "@ember/service"; -import { dasherize } from "@ember/string"; -import { classNames } from "@ember-decorators/component"; -import Uppy from "@uppy/core"; -import DropTarget from "@uppy/drop-target"; -import XHRUpload from "@uppy/xhr-upload"; -import concatClass from "discourse/helpers/concat-class"; -import discourseComputed from "discourse/lib/decorators"; -import getUrl from "discourse/lib/get-url"; -import { i18n } from "discourse-i18n"; -import imagePreviews from "./image-previews"; - -@classNames("wizard-container__image-upload") -export default class Image extends Component { - @service dialog; - - uploading = false; - - @discourseComputed("field.id") - previewComponent(id) { - return imagePreviews[dasherize(id)] ?? imagePreviews.generic; - } - - didInsertElement() { - super.didInsertElement(...arguments); - this.setupUploads(); - } - - @discourseComputed("uploading", "field.value") - hasUpload() { - return ( - !this.uploading && - this.field.value && - !this.field.value.includes("discourse-logo-sketch-small.png") - ); - } - - setupUploads() { - const id = this.field.id; - this._uppyInstance = new Uppy({ - id: `wizard-field-image-${id}`, - meta: { upload_type: `wizard_${id}` }, - autoProceed: true, - }); - - this._uppyInstance.use(XHRUpload, { - endpoint: getUrl("/uploads.json"), - headers: { - "X-CSRF-Token": this.session.csrfToken, - }, - }); - - this._uppyInstance.use(DropTarget, { target: this.element }); - - this._uppyInstance.on("upload", () => { - this.set("uploading", true); - }); - - this._uppyInstance.on("upload-success", (file, response) => { - this.set("field.value", response.body.url); - this.set("uploading", false); - }); - - this._uppyInstance.on("upload-error", (file, error, response) => { - let message = i18n("wizard.upload_error"); - if (response.body.errors) { - message = response.body.errors.join("\n"); - } - - this.dialog.alert(message); - this.set("uploading", false); - }); - - this.element - .querySelector(".wizard-hidden-upload-field") - .addEventListener("change", (event) => { - const files = Array.from(event.target.files); - files.forEach((file) => { - try { - this._uppyInstance.addFile({ - source: `${this.id} file input`, - name: file.name, - type: file.type, - data: file, - }); - } catch (err) { - warn(`error adding files to uppy: ${err}`, { - id: "discourse.upload.uppy-add-files-error", - }); - } - }); - }); - } - - -} diff --git a/frontend/discourse/app/static/wizard/components/fields/index.js b/frontend/discourse/app/static/wizard/components/fields/index.js index 6127a0281dc21..e78858a597eb5 100644 --- a/frontend/discourse/app/static/wizard/components/fields/index.js +++ b/frontend/discourse/app/static/wizard/components/fields/index.js @@ -1,17 +1,9 @@ -import Checkbox from "./checkbox"; -import Checkboxes from "./checkboxes"; import Dropdown from "./dropdown"; -import Image from "./image"; import Radio from "./radio"; -import StylingPreview from "./styling-preview"; import Text from "./text"; export default { - checkbox: Checkbox, - checkboxes: Checkboxes, - "styling-preview": StylingPreview, dropdown: Dropdown, - image: Image, text: Text, radio: Radio, }; diff --git a/frontend/discourse/app/static/wizard/components/fields/styling-preview/-homepage-preview.js b/frontend/discourse/app/static/wizard/components/fields/styling-preview/-homepage-preview.js deleted file mode 100644 index 9b0bb6ac7c097..0000000000000 --- a/frontend/discourse/app/static/wizard/components/fields/styling-preview/-homepage-preview.js +++ /dev/null @@ -1,540 +0,0 @@ -import { i18n } from "discourse-i18n"; -import { darkLightDiff } from "../../../lib/preview"; -import PreviewBaseComponent from "./-preview-base"; - -export default class HomepagePreview extends PreviewBaseComponent { - width = 628; - height = 322; - logo = null; - avatar = null; - - didUpdateAttrs() { - super.didUpdateAttrs(...arguments); - - this.triggerRepaint(); - } - - images() { - return { - logo: this.wizard.logoUrl, - avatar: "/images/wizard/trout.png", - }; - } - - paint({ ctx, colors, font, width, height }) { - this.drawFullHeader(colors, font, this.logo); - - const homepageStyle = this.getHomepageStyle(); - - if ( - !homepageStyle.startsWith("categories") && - !homepageStyle.startsWith("category") - ) { - this.drawPills(colors, font, height * 0.15, { homepageStyle }); - this.renderNonCategoryHomepage( - ctx, - colors, - font, - width, - height, - homepageStyle - ); - } else if ( - ["categories_only", "categories_with_featured_topics"].includes( - homepageStyle - ) - ) { - this.drawPills(colors, font, height * 0.15, { homepageStyle }); - this.renderCategories(ctx, colors, font, width, height); - } else if ( - ["categories_boxes", "categories_boxes_with_topics"].includes( - homepageStyle - ) - ) { - this.drawPills(colors, font, height * 0.15, { homepageStyle }); - const topics = homepageStyle === "categories_boxes_with_topics"; - this.renderCategoriesBoxes(ctx, colors, font, width, height, { topics }); - } else { - this.drawPills(colors, font, height * 0.15, { homepageStyle }); - this.renderCategoriesWithTopics(ctx, colors, font, width, height); - } - } - - renderCategoriesBoxes(ctx, colors, font, width, height, opts) { - opts = opts || {}; - - const borderColor = darkLightDiff( - colors.primary, - colors.secondary, - 90, - -75 - ); - const textColor = darkLightDiff(colors.primary, colors.secondary, 50, 50); - const margin = height * 0.03; - const bodyFontSize = height / 440.0; - const boxHeight = height * 0.7 - margin * 2; - const descriptions = this.getDescriptions(); - const boxesSpacing = 15; - const boxWidth = (width - margin * 2 - boxesSpacing * 2) / 3; - - this.categories().forEach((category, index) => { - const boxStartX = margin + index * boxWidth + index * boxesSpacing; - const boxStartY = height * 0.33; - - this.drawSquare( - ctx, - { x: boxStartX, y: boxStartY }, - { x: boxStartX + boxWidth, y: boxStartY + boxHeight }, - [ - { color: borderColor }, - { color: borderColor }, - { color: borderColor }, - { color: category.color, width: 5 }, - ] - ); - - ctx.font = `700 ${bodyFontSize * 1.3}em '${font.label}'`; - ctx.fillStyle = colors.primary; - ctx.textAlign = "center"; - ctx.fillText(category.name, boxStartX + boxWidth / 2, boxStartY + 25); - ctx.textAlign = "left"; - - if (opts.topics) { - let startY = boxStartY + 60; - this.getTitles().forEach((title) => { - ctx.font = `${bodyFontSize * 1}em '${font.label}'`; - ctx.fillStyle = colors.tertiary; - startY += - this.fillTextMultiLine( - ctx, - title.split("\n").join(" "), - boxStartX + 10, - startY, - 13, - boxWidth * 0.95 - ) + 8; - }); - } else { - ctx.font = `${bodyFontSize * 1}em '${font.label}'`; - ctx.fillStyle = textColor; - ctx.textAlign = "center"; - this.fillTextMultiLine( - ctx, - descriptions[index], - boxStartX + boxWidth / 2, - boxStartY + 60, - 13, - boxWidth * 0.8 - ); - ctx.textAlign = "left"; - } - }); - } - - renderCategories(ctx, colors, font, width, height) { - const textColor = darkLightDiff(colors.primary, colors.secondary, 50, 50); - const margin = height * 0.03; - const bodyFontSize = height / 440.0; - const titles = this.getTitles(); - let categoryHeight = height / 6; - - const drawLine = (x, y) => { - ctx.beginPath(); - ctx.strokeStyle = darkLightDiff( - colors.primary, - colors.secondary, - 90, - -75 - ); - ctx.moveTo(margin + x, y); - ctx.lineTo(width - margin, y); - ctx.stroke(); - }; - - const cols = [0.025, 0.45, 0.53, 0.58, 0.94, 0.96].map((c) => c * width); - - const headingY = height * 0.33; - ctx.font = `${bodyFontSize * 0.9}em '${font.label}'`; - ctx.fillStyle = textColor; - ctx.fillText("Category", cols[0], headingY); - - const homepageStyle = this.getHomepageStyle(); - - if (homepageStyle === "categories_only") { - ctx.fillText("Topics", cols[4], headingY); - } else { - ctx.fillText("Topics", cols[1], headingY); - ctx.fillText("Latest", cols[2], headingY); - categoryHeight = height / 5; - } - - let y = headingY + bodyFontSize * 12; - ctx.lineWidth = 2; - drawLine(0, y); - drawLine(width / 2, y); - - // Categories - this.categories().forEach((category, idx) => { - const textPos = y + categoryHeight * 0.35; - ctx.font = `700 ${bodyFontSize * 1.1}em '${font.label}'`; - ctx.fillStyle = colors.primary; - ctx.fillText(category.name, cols[0], textPos); - - ctx.font = `${bodyFontSize * 0.8}em '${font.label}'`; - ctx.fillStyle = textColor; - ctx.fillText( - titles[idx], - cols[0] - margin * 0.1, - textPos + categoryHeight * 0.36 - ); - - ctx.beginPath(); - ctx.moveTo(margin, y); - ctx.strokeStyle = category.color; - ctx.lineWidth = 3.5; - ctx.lineTo(margin, y + categoryHeight); - ctx.stroke(); - - if (homepageStyle === "categories_with_featured_topics") { - ctx.font = `${bodyFontSize}em '${font.label}'`; - ctx.fillText( - Math.floor(Math.random() * 90) + 10, - cols[1] + 15, - textPos - ); - } else { - ctx.font = `${bodyFontSize}em '${font.label}'`; - ctx.fillText(Math.floor(Math.random() * 90) + 10, cols[5], textPos); - } - - y += categoryHeight; - ctx.lineWidth = 1; - drawLine(0, y); - }); - - // Featured Topics - if (homepageStyle === "categories_with_featured_topics") { - const topicHeight = height / 15; - - y = headingY + bodyFontSize * 22; - ctx.lineWidth = 1; - ctx.fillStyle = colors.tertiary; - - titles.forEach((title) => { - ctx.font = `${bodyFontSize}em '${font.label}'`; - const textPos = y + topicHeight * 0.35; - ctx.fillStyle = colors.tertiary; - ctx.fillText(`${title}`, cols[2], textPos); - y += topicHeight; - }); - } - } - - renderCategoriesWithTopics(ctx, colors, font, width, height) { - const textColor = darkLightDiff(colors.primary, colors.secondary, 50, 50); - const margin = height * 0.03; - const bodyFontSize = height / 440.0; - - const drawLine = (x, y) => { - ctx.beginPath(); - ctx.strokeStyle = darkLightDiff( - colors.primary, - colors.secondary, - 90, - -75 - ); - ctx.moveTo(margin + x, y); - ctx.lineTo(margin + x + (width * 0.9) / 2, y); - ctx.stroke(); - }; - - const cols = [0.025, 0.42, 0.53, 0.58, 0.94].map((c) => c * width); - - const headingY = height * 0.33; - ctx.font = `${bodyFontSize * 0.9}em '${font.label}'`; - ctx.fillStyle = textColor; - ctx.fillText("Category", cols[0], headingY); - ctx.fillText("Topics", cols[1], headingY); - if (this.getHomepageStyle() === "categories_and_latest_topics") { - ctx.fillText("Latest", cols[2], headingY); - } else { - ctx.fillText("Top", cols[2], headingY); - } - - let y = headingY + bodyFontSize * 12; - ctx.lineWidth = 2; - drawLine(0, y); - drawLine(width / 2, y); - - const categoryHeight = height / 6; - const titles = this.getTitles(); - - // Categories - this.categories().forEach((category, idx) => { - const textPos = y + categoryHeight * 0.35; - ctx.font = `700 ${bodyFontSize * 1.1}em '${font.label}'`; - ctx.fillStyle = colors.primary; - ctx.fillText(category.name, cols[0], textPos); - - ctx.font = `${bodyFontSize * 0.8}em '${font.label}'`; - ctx.fillStyle = textColor; - ctx.fillText( - titles[idx], - cols[0] - margin * 0.25, - textPos + categoryHeight * 0.36 - ); - - ctx.beginPath(); - ctx.moveTo(margin, y); - ctx.strokeStyle = category.color; - ctx.lineWidth = 3.5; - ctx.lineTo(margin, y + categoryHeight); - ctx.stroke(); - - ctx.font = `${bodyFontSize}em '${font.label}'`; - ctx.fillText(Math.floor(Math.random() * 90) + 10, cols[1] + 15, textPos); - - y += categoryHeight; - ctx.lineWidth = 1; - drawLine(0, y); - }); - - // Latest/Top Topics - const topicHeight = height / 8; - const avatarSize = topicHeight * 0.7; - y = headingY + bodyFontSize * 12; - ctx.lineWidth = 1; - ctx.fillStyle = textColor; - - titles.forEach((title) => { - const category = this.categories()[0]; - ctx.font = `${bodyFontSize}em '${font.label}'`; - const textPos = y + topicHeight * 0.45; - ctx.fillStyle = colors.primary; - this.scaleImage( - this.avatar, - cols[2], - y + margin * 0.6, - avatarSize, - avatarSize - ); - ctx.fillText(title, cols[3], textPos); - - ctx.font = `700 ${bodyFontSize}em '${font.label}'`; - ctx.fillText(Math.floor(Math.random() * 90) + 10, cols[4], textPos); - ctx.font = `${bodyFontSize}em '${font.label}'`; - ctx.fillText(`1h`, cols[4], textPos + topicHeight * 0.4); - - ctx.beginPath(); - ctx.fillStyle = category.color; - const badgeSize = topicHeight * 0.1; - ctx.font = `700 ${bodyFontSize * 0.5}em '${font.label}'`; - ctx.rect( - cols[3] + margin * 0.25, - y + topicHeight * 0.65, - badgeSize, - badgeSize - ); - ctx.fill(); - - ctx.fillStyle = colors.primary; - ctx.fillText( - category.name, - cols[3] + badgeSize * 2, - y + topicHeight * 0.76 - ); - y += topicHeight; - - drawLine(width / 2, y); - }); - } - - getHomepageStyle() { - return this.step.valueFor("homepage_style"); - } - - getTitles() { - return [ - i18n("wizard.homepage_preview.topic_titles.what_books"), - i18n("wizard.homepage_preview.topic_titles.what_movies"), - i18n("wizard.homepage_preview.topic_titles.random_fact"), - i18n("wizard.homepage_preview.topic_titles.tv_show"), - ]; - } - - getHotTitles() { - return [ - i18n("wizard.homepage_preview.topic_titles.what_hobbies"), - i18n("wizard.homepage_preview.topic_titles.what_music"), - i18n("wizard.homepage_preview.topic_titles.funniest_thing"), - i18n("wizard.homepage_preview.topic_titles.share_art"), - ]; - } - - getDescriptions() { - return [ - i18n("wizard.homepage_preview.category_descriptions.icebreakers"), - i18n("wizard.homepage_preview.category_descriptions.news"), - i18n("wizard.homepage_preview.category_descriptions.site_feedback"), - ]; - } - - renderNonCategoryHomepage(ctx, colors, font, width, height, homepageStyle) { - const rowHeight = height / 6.6; - // accounts for hard-set color variables in solarized themes - const textColor = - colors.primary_medium || - darkLightDiff(colors.primary, colors.secondary, 50, 50); - const bodyFontSize = height / 440.0; - - ctx.font = `${bodyFontSize}em '${font.label}'`; - - const margin = height * 0.03; - - const drawLine = (y) => { - ctx.beginPath(); - // accounts for hard-set color variables in solarized themes - ctx.strokeStyle = - colors.primary_low || - darkLightDiff(colors.primary, colors.secondary, 90, -75); - ctx.moveTo(margin, y); - ctx.lineTo(width - margin, y); - ctx.stroke(); - }; - - const cols = [0.02, 0.66, 0.75, 0.83, 0.9].map((c) => c * width); - - // Headings - const headingY = height * 0.33; - - ctx.fillStyle = textColor; - ctx.font = `${bodyFontSize * 0.9}em '${font.label}'`; - ctx.fillText( - i18n("wizard.homepage_preview.table_headers.topic"), - cols[0], - headingY - ); - ctx.fillText( - i18n("wizard.homepage_preview.table_headers.replies"), - cols[2], - headingY - ); - ctx.fillText( - i18n("wizard.homepage_preview.table_headers.views"), - cols[3], - headingY - ); - ctx.fillText( - i18n("wizard.homepage_preview.table_headers.activity"), - cols[4], - headingY - ); - - // Topics - let y = headingY + rowHeight / 2.6; - ctx.lineWidth = 2; - drawLine(y); - - ctx.font = `${bodyFontSize}em '${font.label}'`; - ctx.lineWidth = 1; - - const titles = - homepageStyle === "hot" ? this.getHotTitles() : this.getTitles(); - titles.forEach((title) => { - const textPos = y + rowHeight * 0.4; - ctx.fillStyle = colors.primary; - ctx.fillText(title, cols[0], textPos); - - // Category badge - const category = this.categories()[0]; - ctx.beginPath(); - ctx.fillStyle = category.color; - const badgeSize = rowHeight * 0.15; - ctx.font = `700 ${bodyFontSize * 0.75}em '${font.label}'`; - ctx.rect(cols[0] + 4, y + rowHeight * 0.6, badgeSize, badgeSize); - ctx.fill(); - - ctx.fillStyle = colors.primary; - ctx.fillText( - category.name, - cols[0] + badgeSize * 2, - y + rowHeight * 0.73 - ); - this.scaleImage( - this.avatar, - cols[1], - y + rowHeight * 0.25, - rowHeight * 0.5, - rowHeight * 0.5 - ); - - ctx.fillStyle = textColor; - ctx.font = `${bodyFontSize}em '${font.label}'`; - for (let colIndex = 2; colIndex <= 4; colIndex++) { - // Give Hot a higher range of random values to make it look like - // more activity is happening. - const randomValue = - homepageStyle === "hot" - ? Math.floor(Math.random() * (660 - 220) + 220) + 10 - : Math.floor(Math.random() * 90) + 10; - - ctx.fillText( - // Last column is relative activity time, others are random numbers. - colIndex === 4 ? "1h" : randomValue, - cols[colIndex] + margin, - y + rowHeight * 0.6 - ); - } - drawLine(y + rowHeight * 1); - y += rowHeight; - }); - } - - fillTextMultiLine(ctx, text, x, y, lineHeight, maxWidth) { - const words = text.split(" ").filter((f) => f); - let line = ""; - let totalHeight = 0; - - words.forEach((word) => { - if (ctx.measureText(`${line} ${word} `).width >= maxWidth) { - ctx.fillText(line, x, y + totalHeight); - totalHeight += lineHeight; - line = word.trim(); - } else { - line = `${line} ${word}`.trim(); - } - }); - - ctx.fillText(line, x, y + totalHeight); - totalHeight += lineHeight; - - return totalHeight; - } - - // Edges expected in this order: NW to NE -> NE to SE -> SE to SW -> SW to NW - drawSquare(ctx, from, to, edges = []) { - const edgeConfiguration = (index) => { - const edge = edges[index] || {}; - - return { - width: edge.width || 1, - color: edge.color || "#333", - }; - }; - - [ - { from: { x: from.x, y: from.y }, to: { x: to.x, y: from.y } }, - { from: { x: to.x, y: from.y }, to: { x: to.x, y: to.y } }, - { from: { x: to.x, y: to.y }, to: { x: from.x, y: to.y } }, - { from: { x: from.x, y: to.y }, to: { x: from.x, y: from.y } }, - ].forEach((path, index) => { - const configuration = edgeConfiguration(index); - ctx.beginPath(); - ctx.moveTo(path.from.x, path.from.y); - ctx.strokeStyle = configuration.color; - ctx.lineWidth = configuration.width; - ctx.lineTo(path.to.x, path.to.y); - ctx.stroke(); - }); - } -} diff --git a/frontend/discourse/app/static/wizard/components/fields/styling-preview/-preview-base.gjs b/frontend/discourse/app/static/wizard/components/fields/styling-preview/-preview-base.gjs deleted file mode 100644 index 6976eecb7a2f4..0000000000000 --- a/frontend/discourse/app/static/wizard/components/fields/styling-preview/-preview-base.gjs +++ /dev/null @@ -1,472 +0,0 @@ -/* eslint-disable ember/no-classic-components */ -/*eslint no-bitwise:0 */ -import Component from "@ember/component"; -import { action } from "@ember/object"; -import { scheduleOnce } from "@ember/runloop"; -import { htmlSafe } from "@ember/template"; -import { Promise } from "rsvp"; -import getUrl from "discourse/lib/get-url"; -import PreloadStore from "discourse/lib/preload-store"; -import { i18n } from "discourse-i18n"; -import { darkLightDiff, drawHeader } from "../../../lib/preview"; - -const scaled = {}; - -function canvasFor(image, w, h) { - w = Math.ceil(w); - h = Math.ceil(h); - - const scale = window.devicePixelRatio; - - const can = document.createElement("canvas"); - can.width = w * scale; - can.height = h * scale; - - const ctx = can.getContext("2d"); - ctx.scale(scale, scale); - ctx.drawImage(image, 0, 0, w, h); - return can; -} - -const scale = window.devicePixelRatio; - -export default class PreviewBase extends Component { - ctx = null; - loaded = false; - loadingFontVariants = false; - - get elementWidth() { - return this.width * scale; - } - - get elementHeight() { - return this.height * scale; - } - - get canvasStyle() { - return htmlSafe(`width:${this.width}px;height:${this.height}px`); - } - - didInsertElement() { - super.didInsertElement(...arguments); - this.fontMap = PreloadStore.get("fontMap"); - this.loadedFonts = new Set(); - const c = this.element.querySelector("canvas"); - this.ctx = c.getContext("2d"); - this.ctx.scale(scale, scale); - - if (this.step) { - this.step.findField("color_scheme")?.addListener(this.themeChanged); - this.step.findField("homepage_style")?.addListener(this.themeChanged); - this.step.findField("body_font")?.addListener(this.themeBodyFontChanged); - this.step.findField("site_font")?.addListener(this.themeFontChanged); - this.step - .findField("heading_font") - ?.addListener(this.themeHeadingFontChanged); - } - - this.reload(); - } - - willDestroyElement() { - super.willDestroyElement(...arguments); - - if (this.step) { - this.step.findField("color_scheme")?.removeListener(this.themeChanged); - this.step.findField("homepage_style")?.removeListener(this.themeChanged); - this.step - .findField("body_font") - ?.removeListener(this.themeBodyFontChanged); - this.step.findField("site_font")?.removeListener(this.themeFontChanged); - this.step - .findField("heading_font") - ?.removeListener(this.themeHeadingFontChanged); - } - } - - @action - themeChanged() { - this.triggerRepaint(); - } - - @action - themeBodyFontChanged() { - if (!this.loadingFontVariants) { - this.loadFontVariants(this.wizard.font); - } - } - - @action - themeHeadingFontChanged() { - if (!this.loadingFontVariants) { - this.loadFontVariants(this.wizard.headingFont); - } - } - - @action - themeFontChanged() { - if (!this.loadingFontVariants) { - this.loadFontVariants(this.wizard.font); - } - } - - loadFontVariants(font) { - if (!font) { - return Promise.resolve(); - } - - const fontVariantData = this.fontMap[font.id]; - - // System font for example does not need to load from a remote source. - if (!fontVariantData) { - this.loadedFonts.add(font.id); - } - - if (fontVariantData && !this.loadedFonts.has(font.id)) { - this.loadingFontVariants = true; - const fontFaces = fontVariantData.map((fontVariant) => { - return new FontFace(font.label, `url(${fontVariant.url})`, { - style: "normal", - weight: fontVariant.weight, - }); - }); - - return Promise.all( - fontFaces.map((fontFace) => - fontFace.load().then((loadedFont) => { - document.fonts.add(loadedFont); - - // We use our own Set because, though document.fonts.check is available, - // it does not seem very reliable, returning false for fonts that have - // definitely been loaded. - this.loadedFonts.add(font.id); - }) - ) - ) - .then(() => { - this.triggerRepaint(); - }) - .finally(() => { - this.loadingFontVariants = false; - }); - } else if (this.loadedFonts.has(font.id)) { - return Promise.resolve(this.triggerRepaint()); - } - } - - images() {} - - // NOTE: This works for fonts included in a style that is actually using the - // @font-faces on load, but for fonts that we aren't using yet we need to - // make sure they are loaded before rendering the canvas via loadFontVariants. - loadFonts() { - return document.fonts.ready; - } - - loadImages() { - const images = this.images(); - if (images) { - return Promise.all( - Object.keys(images).map((id) => { - return loadImage(images[id]).then((img) => (this[id] = img)); - }) - ); - } - return Promise.resolve(); - } - - reload() { - Promise.all([this.loadFonts(), this.loadImages()]).then(() => { - // NOTE: This must be done otherwise the "bold" variant of the body font - // will not be loaded for some reason before rendering the canvas. - // - // The header font does not suffer from this issue. - this.loadFontVariants(this.wizard.font).then(() => { - this.loaded = true; - this.triggerRepaint(); - }); - }); - } - - triggerRepaint() { - scheduleOnce("afterRender", this, "repaint"); - } - - repaint() { - if (!this.loaded) { - return false; - } - - const colorsArray = this.wizard.currentColors; - if (!colorsArray) { - return; - } - - let colors = {}; - colorsArray.forEach(function (c) { - const name = c.name; - colors[name] = `#${c.hex}`; - }); - - const { font, headingFont } = this.wizard; - if (!font) { - return; - } - - const { ctx } = this; - - ctx.fillStyle = colors.secondary; - ctx.fillRect(0, 0, this.width, this.height); - - const options = { - ctx, - colors, - font, - headingFont, - width: this.width, - height: this.height, - }; - this.paint(options); - } - - categories() { - return [ - { - name: i18n("wizard.homepage_preview.category_names.icebreakers"), - color: "#652D90", - }, - { - name: i18n("wizard.homepage_preview.category_names.news"), - color: "#3AB54A", - }, - { - name: i18n("wizard.homepage_preview.category_names.site_feedback"), - color: "#25AAE2", - }, - ]; - } - - scaleImage(image, x, y, w, h) { - w = Math.floor(w); - h = Math.floor(h); - - const { ctx } = this; - - const key = `${image.src}-${w}-${h}`; - - if (!scaled[key]) { - let copy = image; - let ratio = copy.width / copy.height; - let newH = copy.height * 0.5; - while (newH > h) { - copy = canvasFor(copy, ratio * newH, newH); - newH = newH * 0.5; - } - - scaled[key] = copy; - } - - ctx.drawImage(scaled[key], x, y, w, h); - } - - get headerHeight() { - return this.height * 0.15; - } - - drawFullHeader(colors, font, logo) { - const { ctx } = this; - - drawHeader(ctx, colors, this.width, this.headerHeight); - - const avatarSize = this.height * 0.1; - const headerMargin = this.headerHeight * 0.2; - - if (logo) { - const logoHeight = this.headerHeight - headerMargin * 2; - - const ratio = logoHeight / logo.height; - this.scaleImage( - logo, - headerMargin, - headerMargin, - logo.width * ratio, - logoHeight - ); - - this.scaleImage(logo, this.width, headerMargin); - } - - // Top right menu - this.scaleImage( - this.avatar, - this.width - avatarSize - headerMargin, - headerMargin, - avatarSize, - avatarSize - ); - - // accounts for hard-set color variables in solarized themes - ctx.fillStyle = - colors.primary_low_mid || - darkLightDiff(colors.primary, colors.secondary, 45, 55); - - const pathScale = this.headerHeight / 1200; - const searchIcon = new Path2D( - "M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z" - ); - const hamburgerIcon = new Path2D( - "M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z" - ); - const chatIcon = new Path2D( - "M512 240c0 114.9-114.6 208-256 208c-37.1 0-72.3-6.4-104.1-17.9c-11.9 8.7-31.3 20.6-54.3 30.6C73.6 471.1 44.7 480 16 480c-6.5 0-12.3-3.9-14.8-9.9c-2.5-6-1.1-12.8 3.4-17.4c0 0 0 0 0 0s0 0 0 0s0 0 0 0c0 0 0 0 0 0l.3-.3c.3-.3 .7-.7 1.3-1.4c1.1-1.2 2.8-3.1 4.9-5.7c4.1-5 9.6-12.4 15.2-21.6c10-16.6 19.5-38.4 21.4-62.9C17.7 326.8 0 285.1 0 240C0 125.1 114.6 32 256 32s256 93.1 256 208z" - ); - ctx.save(); // Save the previous state for translation and scale - ctx.translate( - this.width - avatarSize * 2 - headerMargin * 0.5, - avatarSize / 2 - ); - // need to scale paths otherwise they're too large - ctx.scale(pathScale, pathScale); - ctx.fill(searchIcon); - ctx.restore(); - ctx.save(); - ctx.translate( - this.width - avatarSize * 3 - headerMargin * 0.5, - avatarSize / 2 - ); - ctx.scale(pathScale, pathScale); - ctx.fill(chatIcon); - ctx.restore(); - ctx.save(); - ctx.translate(headerMargin * 1.75, avatarSize / 2); - ctx.scale(pathScale, pathScale); - ctx.fill(hamburgerIcon); - ctx.restore(); - } - - drawPills(colors, font, headerHeight, opts) { - opts = opts || {}; - - const { ctx } = this; - - const badgeHeight = headerHeight * 2 * 0.25; - const headerMargin = headerHeight * 0.2; - const fontSize = Math.round(badgeHeight * 0.5); - ctx.font = `${fontSize}px '${font.label}'`; - - const allCategoriesText = i18n( - "wizard.homepage_preview.nav_buttons.all_categories" - ); - const categoriesWidth = ctx.measureText(allCategoriesText).width; - const categoriesBoxWidth = categoriesWidth + headerMargin * 2; - - // Box around "all categories >" - ctx.beginPath(); - ctx.strokeStyle = colors.primary; - ctx.lineWidth = 0.5; - ctx.rect( - headerMargin, - headerHeight + headerMargin, - categoriesBoxWidth, - badgeHeight - ); - ctx.stroke(); - - ctx.fillStyle = colors.primary; - ctx.fillText( - allCategoriesText, - headerMargin * 1.5, - headerHeight + headerMargin * 1.4 + fontSize - ); - - // Caret (>) at the end of "all categories" box - const pathScale = badgeHeight / 1000; - const caretIcon = new Path2D( - "M0 384.662V127.338c0-17.818 21.543-26.741 34.142-14.142l128.662 128.662c7.81 7.81 7.81 20.474 0 28.284L34.142 398.804C21.543 411.404 0 402.48 0 384.662z" - ); - - ctx.save(); - ctx.translate( - categoriesBoxWidth, - headerHeight + headerMargin + badgeHeight / 4 - ); - ctx.scale(pathScale, pathScale); - ctx.fill(caretIcon); - ctx.restore(); - - const categoryHomepage = - opts.homepageStyle.includes("category") || - opts.homepageStyle.includes("categories"); - - // First top menu item - const otherHomepageText = i18n( - `wizard.top_menu_items.${opts.homepageStyle}` - ); - - const firstTopMenuItemText = categoryHomepage - ? i18n("wizard.top_menu_items.categories") - : otherHomepageText; - - const newText = i18n("wizard.top_menu_items.new"); - const unreadText = i18n("wizard.top_menu_items.unread"); - const topText = i18n("wizard.top_menu_items.top"); - - ctx.beginPath(); - ctx.fillStyle = colors.tertiary; - ctx.rect( - categoriesBoxWidth + headerMargin * 2, - headerHeight + headerMargin, - ctx.measureText(firstTopMenuItemText).width + headerMargin * 2, - badgeHeight - ); - ctx.fill(); - - ctx.font = `${fontSize}px '${font.label}'`; - ctx.fillStyle = colors.secondary; - const pillButtonTextY = headerHeight + headerMargin * 1.4 + fontSize; - const firstTopMenuItemX = headerMargin * 3.0 + categoriesBoxWidth; - ctx.fillText( - firstTopMenuItemText, - firstTopMenuItemX, - pillButtonTextY, - ctx.measureText(firstTopMenuItemText).width - ); - - ctx.fillStyle = colors.primary; - - const newTextX = - firstTopMenuItemX + - ctx.measureText(firstTopMenuItemText).width + - headerMargin * 2.0; - ctx.fillText(newText, newTextX, pillButtonTextY); - - const unreadTextX = - newTextX + ctx.measureText(newText).width + headerMargin * 2.0; - ctx.fillText(unreadText, unreadTextX, pillButtonTextY); - - const topTextX = - unreadTextX + ctx.measureText(unreadText).width + headerMargin * 2.0; - ctx.fillText(topText, topTextX, pillButtonTextY); - } - - -} - -function loadImage(src) { - if (!src) { - return Promise.resolve(); - } - - const img = new Image(); - img.src = getUrl(src); - return new Promise((resolve) => (img.onload = () => resolve(img))); -} diff --git a/frontend/discourse/app/static/wizard/components/fields/styling-preview/index.gjs b/frontend/discourse/app/static/wizard/components/fields/styling-preview/index.gjs deleted file mode 100644 index d95e8f3b50f6b..0000000000000 --- a/frontend/discourse/app/static/wizard/components/fields/styling-preview/index.gjs +++ /dev/null @@ -1,284 +0,0 @@ -import { on } from "@ember/modifier"; -import { action } from "@ember/object"; -import { observes } from "@ember-decorators/object"; -import { bind } from "discourse/lib/decorators"; -import { i18n } from "discourse-i18n"; -import { - chooseDarker, - darkLightDiff, - resizeTextLinesToFitRect, -} from "../../../lib/preview"; -import HomepagePreview from "./-homepage-preview"; -import PreviewBaseComponent from "./-preview-base"; - -export default class Index extends PreviewBaseComponent { - width = 630; - height = 380; - logo = null; - avatar = null; - previewTopic = true; - draggingActive = false; - startX = 0; - scrollLeft = 0; - HomepagePreview = HomepagePreview; - - init() { - super.init(...arguments); - this.step - .findField("homepage_style") - ?.addListener(this.onHomepageStyleChange); - } - - willDestroy() { - super.willDestroy(...arguments); - this.step - .findField("homepage_style") - ?.removeListener(this.onHomepageStyleChange); - } - - didInsertElement() { - super.didInsertElement(...arguments); - this.element.addEventListener("mouseleave", this.handleMouseLeave); - this.element.addEventListener("mousemove", this.handleMouseMove); - } - - willDestroyElement() { - super.willDestroyElement(...arguments); - this.element.removeEventListener("mouseleave", this.handleMouseLeave); - this.element.removeEventListener("mousemove", this.handleMouseMove); - } - - mouseDown(e) { - const slider = this.element.querySelector(".previews"); - this.setProperties({ - draggingActive: true, - startX: e.pageX - slider.offsetLeft, - scrollLeft: slider.scrollLeft, - }); - } - - @bind - handleMouseLeave() { - this.set("draggingActive", false); - } - - mouseUp() { - this.set("draggingActive", false); - } - - @bind - handleMouseMove(e) { - if (!this.draggingActive) { - return; - } - e.preventDefault(); - - const slider = this.element.querySelector(".previews"), - x = e.pageX - slider.offsetLeft, - walk = (x - this.startX) * 1.5; - - slider.scrollLeft = this.scrollLeft - walk; - - if (slider.scrollLeft < 50) { - this.set("previewTopic", true); - } - if (slider.scrollLeft > slider.offsetWidth - 50) { - this.set("previewTopic", false); - } - } - - didUpdateAttrs() { - super.didUpdateAttrs(...arguments); - - this.triggerRepaint(); - } - - @bind - onHomepageStyleChange() { - this.set("previewTopic", false); - } - - @observes("previewTopic") - scrollPreviewArea() { - const el = this.element.querySelector(".previews"); - el.scrollTo({ - top: 0, - left: this.previewTopic ? 0 : el.scrollWidth - el.offsetWidth, - behavior: "smooth", - }); - } - - images() { - return { - logo: this.wizard.logoUrl, - avatar: "/images/wizard/trout.png", - }; - } - - paint({ ctx, colors, font, headingFont, width, height }) { - this.drawFullHeader(colors, headingFont, this.logo); - - const margin = 20; - const avatarSize = height * 0.1 + 5; - const lineHeight = height / 14; - const leftHandTextGutter = margin + avatarSize + margin; - const timelineX = width * 0.86; - - // Draw a fake topic - this.scaleImage( - this.avatar, - margin, - this.headerHeight + height * 0.22, - avatarSize, - avatarSize - ); - - const titleFontSize = this.headerHeight / 30; - - // Topic title - ctx.beginPath(); - ctx.fillStyle = colors.primary; - ctx.font = `700 ${titleFontSize}em '${headingFont.label}'`; - ctx.fillText(i18n("wizard.previews.topic_title"), margin, height * 0.3); - - // Topic OP text - const bodyFontSize = 1; - ctx.font = `${bodyFontSize}em '${font.label}'`; - - let verticalLinePos = 0; - const topicOp = i18n("wizard.homepage_preview.topic_ops.what_books"); - const topicOpLines = topicOp.split("\n"); - - resizeTextLinesToFitRect( - topicOpLines, - timelineX - leftHandTextGutter, - ctx, - bodyFontSize, - font, - (textLine, idx) => { - verticalLinePos = height * 0.4 + idx * lineHeight; - ctx.fillText(textLine, leftHandTextGutter, verticalLinePos); - } - ); - - ctx.font = `${bodyFontSize}em '${font.label}'`; - - // Share button - const shareButtonWidth = - Math.round(ctx.measureText(i18n("wizard.previews.share_button")).width) + - margin; - - ctx.beginPath(); - ctx.rect(margin, verticalLinePos, shareButtonWidth, height * 0.1); - // accounts for hard-set color variables in solarized themes - ctx.fillStyle = - colors.primary_low || - darkLightDiff(colors.primary, colors.secondary, 90, 65); - ctx.fill(); - ctx.fillStyle = chooseDarker(colors.primary, colors.secondary); - ctx.fillText( - i18n("wizard.previews.share_button"), - margin + 10, - verticalLinePos + lineHeight * 0.9 - ); - - // Reply button - const replyButtonWidth = - Math.round(ctx.measureText(i18n("wizard.previews.reply_button")).width) + - margin; - - ctx.beginPath(); - ctx.rect( - shareButtonWidth + margin + 10, - verticalLinePos, - replyButtonWidth, - height * 0.1 - ); - ctx.fillStyle = colors.tertiary; - ctx.fill(); - ctx.fillStyle = colors.secondary; - ctx.fillText( - i18n("wizard.previews.reply_button"), - shareButtonWidth + margin * 2, - verticalLinePos + lineHeight * 0.9 - ); - - // Draw timeline - ctx.beginPath(); - ctx.strokeStyle = colors.tertiary; - ctx.lineWidth = 0.5; - ctx.moveTo(timelineX, height * 0.3); - ctx.lineTo(timelineX, height * 0.7); - ctx.stroke(); - - // Timeline handle - ctx.beginPath(); - ctx.strokeStyle = colors.tertiary; - ctx.lineWidth = 3; - ctx.moveTo(timelineX, height * 0.3 + 10); - ctx.lineTo(timelineX, height * 0.4); - ctx.stroke(); - - // Timeline post count - const postCountY = height * 0.3 + margin + 10; - ctx.beginPath(); - ctx.font = `700 ${bodyFontSize}em '${font.label}'`; - ctx.fillStyle = colors.primary; - ctx.fillText("1 / 20", timelineX + margin / 2, postCountY); - - // Timeline post date - ctx.beginPath(); - ctx.font = `${bodyFontSize * 0.9}em '${font.label}'`; - ctx.fillStyle = darkLightDiff(colors.primary, colors.secondary, 70, 65); - ctx.fillText( - "Nov 22", - timelineX + margin / 2, - postCountY + lineHeight * 0.75 - ); - } - - @action - setPreviewHomepage(event) { - event?.preventDefault(); - this.set("previewTopic", false); - } - - @action - setPreviewTopic(event) { - event?.preventDefault(); - this.set("previewTopic", true); - } - - -} diff --git a/frontend/discourse/app/static/wizard/components/wizard-canvas.gjs b/frontend/discourse/app/static/wizard/components/wizard-canvas.gjs deleted file mode 100644 index 49efecf180790..0000000000000 --- a/frontend/discourse/app/static/wizard/components/wizard-canvas.gjs +++ /dev/null @@ -1,188 +0,0 @@ -import Component from "@glimmer/component"; -import didInsert from "@ember/render-modifiers/modifiers/did-insert"; -import willDestroy from "@ember/render-modifiers/modifiers/will-destroy"; -import { bind } from "discourse/lib/decorators"; - -const MAX_PARTICLES = 150; - -const SIZE = 144; - -const COLORS = [ - "--tertiary", - "--tertiary-high", - "--tertiary-medium", - "--tertiary-low", -]; - -class Particle { - constructor(width, height) { - this.reset(width, height); - } - - reset(width, height) { - this.y = Math.random() * (height + SIZE) - SIZE; - this.origX = Math.random() * (width + SIZE); - this.speed = 0.5 + Math.random(); - this.ang = Math.random() * 2 * Math.PI; - this.scale = Math.random() * 0.4 + 0.2; - this.radius = Math.random() * 25 + 25; - const colorVar = COLORS[Math.floor(Math.random() * COLORS.length)]; - this.color = getComputedStyle(document.body).getPropertyValue(colorVar); - this.flipped = Math.random() > 0.5 ? 1 : -1; - } - - move(width, height) { - this.y += this.speed; - - if (this.y > height + SIZE) { - this.reset(width, height); - // start at the top - this.y = -SIZE; - } - - this.ang += this.speed / 30.0; - if (this.ang > 2 * Math.PI) { - this.ang = 0; - } - - this.x = this.origX + this.radius * Math.sin(this.ang); - } -} - -export default class WizardCanvasComponent extends Component { - canvas = null; - particles = null; - - get ready() { - return this.canvas !== null; - } - - get ctx() { - return this.canvas.getContext("2d"); - } - - @bind - setup(canvas) { - this.canvas = canvas; - this.resized(); - - let { width, height } = canvas; - - this.particles = []; - for (let i = 0; i < MAX_PARTICLES; i++) { - this.particles.push(new Particle(width, height)); - } - - this.paint(width, height); - - window.addEventListener("resize", this.resized); - } - - @bind - teardown() { - this.canvas = null; - window.removeEventListener("resize", this.resized); - } - - @bind - resized() { - this.canvas.width = window.innerWidth; - this.canvas.height = window.innerHeight; - } - - @bind - paint() { - if (!this.ready) { - return; - } - - let { ctx } = this; - let { width, height } = this.canvas; - ctx.clearRect(0, 0, width, height); - - for (let particle of this.particles) { - particle.move(width, height); - this.drawParticle(ctx, particle); - } - - window.requestAnimationFrame(this.paint); - } - - drawParticle(c, p) { - c.save(); - c.translate(p.x - SIZE, p.y - SIZE); - c.scale(p.scale * p.flipped, p.scale); - c.fillStyle = p.color; - c.strokeStyle = p.color; - c.globalAlpha = "1.0"; - c.lineWidth = "1"; - c.lineCap = "butt"; - c.lineJoin = "round"; - c.mitterLimit = "1"; - c.beginPath(); - c.moveTo(97.9, 194.9); - c.lineTo(103.5, 162.9); - c.bezierCurveTo(88.7, 152, 84.2, 139.7, 90.2, 126.3); - c.bezierCurveTo(99.5, 105.6, 124.6, 89.6, 159.7, 100.4); - c.lineTo(159.7, 100.4); - c.bezierCurveTo(175.9, 105.4, 186.4, 111.2, 192.6, 118.5); - c.bezierCurveTo(200, 127.2, 201.6, 138.4, 197.5, 152.7); - c.bezierCurveTo(194, 165, 187.4, 173.6, 177.9, 178.3); - c.bezierCurveTo(165.6, 184.4, 148.4, 183.7, 129.4, 176.3); - c.bezierCurveTo(127.7, 175.6, 126, 174.9, 124.4, 174.2); - c.lineTo(97.9, 194.9); - c.closePath(); - c.moveTo(138, 99.3); - c.bezierCurveTo(115.4, 99.3, 99.3, 111.9, 92.4, 127.3); - c.bezierCurveTo(86.8, 139.7, 91.2, 151.2, 105.5, 161.5); - c.lineTo(106.1, 161.9); - c.lineTo(101.2, 189.4); - c.lineTo(124, 171.7); - c.lineTo(124.6, 172); - c.bezierCurveTo(126.4, 172.8, 128.3, 173.6, 130.2, 174.3); - c.bezierCurveTo(148.6, 181.4, 165.1, 182.2, 176.8, 176.4); - c.bezierCurveTo(185.7, 172, 191.9, 163.9, 195.2, 152.2); - c.bezierCurveTo(202.4, 127.2, 191.9, 112.8, 159, 102.7); - c.lineTo(159, 102.7); - c.bezierCurveTo(151.6, 100.3, 144.5, 99.3, 138, 99.3); - c.closePath(); - c.fill(); - c.stroke(); - c.beginPath(); - c.moveTo(115.7, 136.2); - c.bezierCurveTo(115.7, 137.9, 115, 139.3, 113.3, 139.3); - c.bezierCurveTo(111.6, 139.3, 110.2, 137.9, 110.2, 136.2); - c.bezierCurveTo(110.2, 134.5, 111.6, 133.1, 113.3, 133.1); - c.bezierCurveTo(115, 133, 115.7, 134.4, 115.7, 136.2); - c.closePath(); - c.fill(); - c.stroke(); - c.beginPath(); - c.moveTo(145.8, 141.6); - c.bezierCurveTo(145.8, 143.3, 144.4, 144.1, 142.7, 144.1); - c.bezierCurveTo(141, 144.1, 139.6, 143.4, 139.6, 141.6); - c.bezierCurveTo(139.6, 141.6, 141, 138.5, 142.7, 138.5); - c.bezierCurveTo(144.4, 138.5, 145.8, 139.9, 145.8, 141.6); - c.closePath(); - c.fill(); - c.stroke(); - c.beginPath(); - c.moveTo(171.6, 146.8); - c.bezierCurveTo(171.6, 148.5, 171, 149.9, 169.2, 149.9); - c.bezierCurveTo(167.5, 149.9, 166.1, 148.5, 166.1, 146.8); - c.bezierCurveTo(166.1, 145.1, 167.5, 143.7, 169.2, 143.7); - c.bezierCurveTo(171, 143.6, 171.6, 145, 171.6, 146.8); - c.closePath(); - c.fill(); - c.stroke(); - c.restore(); - } - - -} diff --git a/frontend/discourse/app/static/wizard/components/wizard-step.gjs b/frontend/discourse/app/static/wizard/components/wizard-step.gjs index 3f88d635c814f..48e68b0493afd 100644 --- a/frontend/discourse/app/static/wizard/components/wizard-step.gjs +++ b/frontend/discourse/app/static/wizard/components/wizard-step.gjs @@ -2,19 +2,14 @@ import Component from "@glimmer/component"; import { tracked } from "@glimmer/tracking"; import { on } from "@ember/modifier"; import { action } from "@ember/object"; -import didInsert from "@ember/render-modifiers/modifiers/did-insert"; -import didUpdate from "@ember/render-modifiers/modifiers/did-update"; -import { schedule } from "@ember/runloop"; -import { htmlSafe } from "@ember/template"; import concatClass from "discourse/helpers/concat-class"; import emoji from "discourse/helpers/emoji"; +import getUrl from "discourse/lib/get-url"; import { i18n } from "discourse-i18n"; import WizardField from "./wizard-field"; -const READY_STEP_INDEX = 3; - export default class WizardStepComponent extends Component { - @tracked saving = false; + @tracked hasError = false; get wizard() { return this.args.wizard; @@ -28,55 +23,6 @@ export default class WizardStepComponent extends Component { return this.step.id; } - // We don't want to show the step counter for optional steps after - // the "Ready" step. - get showStepCounter() { - return this.args.step.displayIndex < READY_STEP_INDEX; - } - - /** - * Step Back Button? Primary Action Secondary Action - * ------------------------------------------------------------------ - * First No Next N/A - * ------------------------------------------------------------------ - * ... Yes Next N/A - * ------------------------------------------------------------------ - * Ready Yes Jump In Configure More - * ------------------------------------------------------------------ - * ... Yes Next Exit Setup - * ------------------------------------------------------------------ - * Last Yes Jump In N/A - * ------------------------------------------------------------------ - * - * Back Button: without saving, go back to the last page - * Next Button: save, and if successful, go to the next page - * Configure More: re-skinned next button - * Exit Setup: without saving, go to the home page ("finish") - * Jump In: on the "ready" page, it exits the setup ("finish"), on the - * last page, it saves, and if successful, go to the home page - */ - get isFinalStep() { - return this.step.displayIndex === this.wizard.steps.length; - } - - get showBackButton() { - return this.step.index > 0; - } - - get showFinishButton() { - const ready = this.wizard.findStep("ready"); - const isReady = ready && this.step.index > ready.index; - return isReady && !this.isFinalStep; - } - - get showJumpInButton() { - return this.id === "ready" || this.isFinalStep; - } - - get includeSidebar() { - return !!this.step.fields.find((f) => f.showInSidebar); - } - get containerFontClasses() { let fontClasses = ""; @@ -92,163 +38,42 @@ export default class WizardStepComponent extends Component { } @action - stepChanged() { - this.saving = false; - this.autoFocus(); - } - - @action - onKeyUp(event) { - if (event.key === "Enter") { - if (this.showJumpInButton) { - this.jumpIn(); - } else { - this.nextStep(); - } - } - } - - @action - autoFocus() { - schedule("afterRender", () => { - const firstInvalidElement = document.querySelector( - ".wizard-container__input.invalid:nth-of-type(1) .wizard-focusable" - ); - - if (firstInvalidElement) { - return firstInvalidElement.focus(); - } - - document.querySelector(".wizard-focusable:nth-of-type(1)")?.focus(); - }); - } - - async advance() { - try { - this.saving = true; - const response = await this.step.save(); - this.args.goNext(response); - } finally { - this.saving = false; - } - } - - @action - finish(event) { - event?.preventDefault(); - - if (this.saving) { + async jumpIn() { + const valid = await this.step.validate(); + if (!valid) { + this.hasError = true; return; } - this.args.goHome(); - } - - @action - jumpIn(event) { - event?.preventDefault(); - - if (this.saving) { - return; - } - - if (this.id === "ready") { - this.finish(); - } else { - this.nextStep(); - } - } - - @action - backStep(event) { - event?.preventDefault(); - - if (this.saving) { - return; - } - - this.args.goBack(); - } - - @action - nextStep(event) { - event?.preventDefault(); - - if (this.saving) { - return; - } - - if (this.step.validate()) { - this.advance(); - } else { - this.autoFocus(); + const response = await this.step.save(); + if (response && response.success) { + // We are not using Ember routing here because we always want to reload the app + // ensure language, site title are properly set. + document.location = getUrl("/"); } }