diff --git a/app/assets/javascripts/admin/addon/components/admin-config-areas/look-and-feel-themes.gjs b/app/assets/javascripts/admin/addon/components/admin-config-areas/look-and-feel-themes.gjs new file mode 100644 index 00000000000..12ba5fce070 --- /dev/null +++ b/app/assets/javascripts/admin/addon/components/admin-config-areas/look-and-feel-themes.gjs @@ -0,0 +1,69 @@ +import Component from "@glimmer/component"; +import { action } from "@ember/object"; +import { service } from "@ember/service"; +import I18n from "discourse-i18n"; +import AdminPageSubheader from "admin/components/admin-page-subheader"; +import InstallThemeModal from "admin/components/modal/install-theme"; +import ThemesGrid from "admin/components/themes-grid"; + +export default class AdminConfigAreasLookAndFeelThemes extends Component { + @service modal; + @service router; + @service toasts; + + @action + installModal() { + this.modal.show(InstallThemeModal, { + model: { ...this.installThemeOptions() }, + }); + } + + // TODO (martin) These install methods may not belong here and they + // are incomplete or have stubbed or omitted properties. We may want + // to move this to the new config route or a dedicated component + // that sits in the route. + installThemeOptions() { + return { + selectedType: "theme", + userId: null, + content: [], + installedThemes: this.args.themes, + addTheme: this.addTheme, + updateSelectedType: () => {}, + }; + } + + @action + addTheme(theme) { + this.toasts.success({ + data: { + message: I18n.t("admin.customize.theme.install_success", { + theme: theme.name, + }), + }, + duration: 2000, + }); + this.router.refresh(); + } + + +} diff --git a/app/assets/javascripts/admin/addon/components/modal/install-theme.js b/app/assets/javascripts/admin/addon/components/modal/install-theme.js index a2107ac5a6e..047369f7a61 100644 --- a/app/assets/javascripts/admin/addon/components/modal/install-theme.js +++ b/app/assets/javascripts/admin/addon/components/modal/install-theme.js @@ -10,7 +10,7 @@ import { COMPONENTS, THEMES } from "admin/models/theme"; const MIN_NAME_LENGTH = 4; -export default class InstallTheme extends Component { +export default class InstallThemeModal extends Component { @service store; @tracked selection = this.args.model.selection || "popular"; @@ -184,9 +184,11 @@ export default class InstallTheme extends Component { } if (this.remote || this.popular || this.directRepoInstall) { - const duplicate = this.args.model.content.find((theme) => - this.themeHasSameUrl(theme, this.uploadUrl) - ); + const duplicate = + this.args.model.content && + this.args.model.content.find((theme) => + this.themeHasSameUrl(theme, this.uploadUrl) + ); if (duplicate && !this.duplicateRemoteThemeWarning) { const warning = I18n.t("admin.customize.theme.duplicate_remote_theme", { name: duplicate.name, diff --git a/app/assets/javascripts/admin/addon/components/themes-grid-card.gjs b/app/assets/javascripts/admin/addon/components/themes-grid-card.gjs index 815f6155bf3..a3f8f0e8ec8 100644 --- a/app/assets/javascripts/admin/addon/components/themes-grid-card.gjs +++ b/app/assets/javascripts/admin/addon/components/themes-grid-card.gjs @@ -1,13 +1,16 @@ import Component from "@glimmer/component"; +import { tracked } from "@glimmer/tracking"; +import { array } from "@ember/helper"; import { action } from "@ember/object"; import { service } from "@ember/service"; -import { htmlSafe } from "@ember/template"; import DButton from "discourse/components/d-button"; -import concatClass from "discourse/helpers/concat-class"; +import DropdownMenu from "discourse/components/dropdown-menu"; +import { popupAjaxError } from "discourse/lib/ajax-error"; import icon from "discourse-common/helpers/d-icon"; import i18n from "discourse-common/helpers/i18n"; import I18n from "discourse-i18n"; import AdminConfigAreaCard from "admin/components/admin-config-area-card"; +import DMenu from "float-kit/components/d-menu"; import ThemesGridPlaceholder from "./themes-grid-placeholder"; // NOTE (martin): We will need to revisit and improve this component @@ -20,24 +23,28 @@ export default class ThemeCard extends Component { @service siteSettings; @service toasts; + @tracked isUpdating = false; + + get themeCardClasses() { + return [ + "theme-card", + this.args.theme.get("default") ? "-active" : "", + this.isUpdating ? "--updating" : "", + ].join(" "); + } + get themeRouteModels() { return ["themes", this.args.theme.id]; } - get childrenString() { - return this.args.theme.childThemes.reduce((acc, theme, idx) => { - if (idx === this.args.theme.childThemes.length - 1) { - return acc + theme.name; - } else { - return acc + theme.name + ", "; - } - }, ""); - } - get themePreviewUrl() { return `/admin/themes/${this.args.theme.id}/preview`; } + get footerActionIcon() { + return this.args.theme.isPendingUpdates ? "sync" : "ellipsis-h"; + } + // NOTE: inspired by -> https://github.com/discourse/discourse/blob/24caa36eef826bcdaed88aebfa7df154413fb349/app/assets/javascripts/admin/addon/controllers/admin-customize-themes-show.js#L366 // // Will also need some cleanup when refactoring other theme code. @@ -75,12 +82,43 @@ export default class ThemeCard extends Component { }); } + @action + updateTheme() { + if (this.isUpdating) { + return; + } + + this.isUpdating = true; + this.args.theme + .updateToLatest() + .then(() => { + this.toasts.success({ + data: { + message: I18n.t("admin.customize.theme.update_success", { + theme: this.args.theme.name, + }), + }, + duration: 2000, + }); + }) + .catch(popupAjaxError) + .finally(() => { + this.isUpdating = false; + }); + } +