David Taylor 979325c500
DEV: Move discourse-common/ helpers to discourse/ (#30728)
`discourse-common` was created in the past to share logic between the
'wizard' app and the main 'discourse' app. Since then, the wizard has
been consolidated into the main app, so the separation of
`discourse-common` is no longer useful.

This commit moves `discourse-common/helpers/*` into
`discourse/helpers/*`, removes `discourse-common` from the Ember
resolver config, and adds shims for the imports.
2025-01-13 09:36:11 +00:00

214 lines
6.8 KiB
Plaintext

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 DButton from "discourse/components/d-button";
import DropdownMenu from "discourse/components/dropdown-menu";
import icon from "discourse/helpers/d-icon";
import { popupAjaxError } from "discourse/lib/ajax-error";
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
// over time.
//
// Much of the existing theme logic in /admin/customize/themes has old patterns
// and technical debt, so anything copied from there to here is subject
// to change as we improve this incrementally.
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 themePreviewUrl() {
return `/admin/themes/${this.args.theme.id}/preview`;
}
get footerActionIcon() {
return this.args.theme.isPendingUpdates ? "arrows-rotate" : "ellipsis";
}
// 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.
@action
async setDefault() {
let oldDefaultThemeId;
this.args.theme.set("default", true);
this.args.allThemes.forEach((theme) => {
if (theme.id !== this.args.theme.id) {
if (theme.get("default")) {
oldDefaultThemeId = theme.id;
}
theme.set("default", !this.args.theme.get("default"));
}
});
const changesSaved = await this.args.theme.saveChanges("default");
if (!changesSaved) {
this.args.allThemes
.find((theme) => theme.id === oldDefaultThemeId)
.set("default", true);
this.args.theme.set("default", false);
return;
}
this.toasts.success({
data: {
message: i18n("admin.customize.theme.set_default_success", {
theme: this.args.theme.name,
}),
},
duration: 2000,
});
}
@action
updateTheme() {
if (this.isUpdating) {
return;
}
this.isUpdating = true;
this.args.theme
.updateToLatest()
.then(() => {
this.toasts.success({
data: {
message: i18n("admin.customize.theme.update_success", {
theme: this.args.theme.name,
}),
},
duration: 2000,
});
})
.catch(popupAjaxError)
.finally(() => {
this.isUpdating = false;
});
}
<template>
<AdminConfigAreaCard
class={{this.themeCardClasses}}
@translatedHeading={{@theme.name}}
>
<:content>
{{#if @theme.isPendingUpdates}}
<span
title={{i18n "admin.customize.theme.updates_available_tooltip"}}
class="theme-card__update-available"
>{{icon "circle-info"}}</span>
{{/if}}
<div class="theme-card__image-wrapper">
{{#if @theme.screenshot_url}}
<img
class="theme-card__image"
src={{@theme.screenshot_url}}
alt={{@theme.name}}
/>
{{else}}
<ThemesGridPlaceholder @theme={{@theme}} />
{{/if}}
</div>
<div class="theme-card__content">
{{#if @theme.description}}
<p class="theme-card__description">{{@theme.description}}</p>
{{/if}}
</div>
<div class="theme-card__footer">
<DButton
@translatedLabel={{i18n "admin.customize.theme.edit"}}
@route="adminCustomizeThemes.show"
@routeModels={{this.themeRouteModels}}
class="btn-primary theme-card__button"
@preventFocus={{true}}
/>
<div class="theme-card__footer-actions">
<DMenu
@identifier="theme-card__footer-menu"
@triggerClass="theme-card__footer-menu btn-flat"
@modalForMobile={{true}}
@icon={{this.footerActionIcon}}
@label={{if
this.isUpdating
(i18n "admin.customize.theme.updating")
""
}}
@triggers={{array "click"}}
>
<:content>
<DropdownMenu as |dropdown|>
{{#if @theme.isPendingUpdates}}
<dropdown.item>
<DButton
@action={{this.updateTheme}}
@icon="download"
class="theme-card__button -update"
@preventFocus={{true}}
@translatedLabel={{i18n
"admin.customize.theme.update_to_latest"
}}
/>
</dropdown.item>
{{/if}}
{{! TODO: Jordan
solutions for broken, disabled states }}
<dropdown.item>
<DButton
@action={{this.setDefault}}
@preventFocus={{true}}
@icon={{if
@theme.default
"far-square-check"
"far-square"
}}
class="theme-card__button"
@translatedLabel={{i18n
(if
@theme.default
"admin.customize.theme.default_theme"
"admin.customize.theme.set_default_theme"
)
}}
@disabled={{@theme.default}}
/>
</dropdown.item>
<dropdown.item>
<a
href={{this.themePreviewUrl}}
title={{i18n "admin.customize.explain_preview"}}
rel="noopener noreferrer"
target="_blank"
class="btn btn-transparent theme-card__button"
>{{icon "eye"}} {{i18n "admin.customize.theme.preview"}}</a>
</dropdown.item>
</DropdownMenu>
</:content>
</DMenu>
</div>
</div>
</:content>
</AdminConfigAreaCard>
</template>
}