mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-25 01:21:00 +08:00
This makes the headings in various admin lists clickable as an additional affordance, as it's somewhat natural to expect headings in lists to be clickable! This includes the following admin areas: * Themes * Theme Components * Color palettes * Plugins * User fields * Custom flags --------- Co-authored-by: Gary Pendergast <gary@pento.net>
317 lines
10 KiB
Text
Vendored
317 lines
10 KiB
Text
Vendored
import Component from "@glimmer/component";
|
|
import { tracked } from "@glimmer/tracking";
|
|
import { action } from "@ember/object";
|
|
import { service } from "@ember/service";
|
|
import { dasherize } from "@ember/string";
|
|
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 toasts;
|
|
@service dialog;
|
|
@service router;
|
|
|
|
@tracked isUpdating = false;
|
|
|
|
get themeCardClasses() {
|
|
return [
|
|
"theme-card",
|
|
this.args.theme.get("default") ? "--default" : "",
|
|
this.isUpdating ? "--updating" : "",
|
|
dasherize(this.args.theme.name),
|
|
].join(" ");
|
|
}
|
|
|
|
get themeRouteModels() {
|
|
return ["themes", this.args.theme.id];
|
|
}
|
|
|
|
get themePreviewUrl() {
|
|
return `/admin/themes/${this.args.theme.id}/preview`;
|
|
}
|
|
|
|
get destroyDisabled() {
|
|
return this.args.theme.default || this.args.theme.system;
|
|
}
|
|
|
|
get editUrl() {
|
|
return this.router.urlFor(
|
|
"adminCustomizeThemes.show",
|
|
"themes",
|
|
this.args.theme.id
|
|
);
|
|
}
|
|
|
|
@action
|
|
onRegisterApi(api) {
|
|
this.dMenu = api;
|
|
}
|
|
|
|
// 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.dMenu.close();
|
|
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,
|
|
}),
|
|
},
|
|
});
|
|
|
|
window.location.reload();
|
|
}
|
|
|
|
@action
|
|
async toggleUserSelectable() {
|
|
let oldUserSelectable = this.args.theme.user_selectable;
|
|
|
|
this.args.theme.set("user_selectable", !oldUserSelectable);
|
|
this.dMenu.close();
|
|
|
|
const changesSaved = await this.args.theme.saveChanges("user_selectable");
|
|
if (!changesSaved) {
|
|
this.args.theme.set("user_selectable", oldUserSelectable);
|
|
return;
|
|
}
|
|
|
|
this.toasts.success({
|
|
data: {
|
|
message: i18n("admin.customize.theme.setting_was_saved"),
|
|
},
|
|
});
|
|
}
|
|
|
|
@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,
|
|
}),
|
|
},
|
|
});
|
|
})
|
|
.catch(popupAjaxError)
|
|
.finally(() => {
|
|
this.isUpdating = false;
|
|
});
|
|
}
|
|
|
|
@action
|
|
destroyTheme() {
|
|
return this.dialog.deleteConfirm({
|
|
title: i18n("admin.customize.delete_confirm", {
|
|
theme_name: this.args.theme.name,
|
|
}),
|
|
didConfirm: async () => {
|
|
try {
|
|
await this.args.theme.destroyRecord();
|
|
this.args.allThemes.removeObject(this.args.theme);
|
|
|
|
this.toasts.success({
|
|
data: {
|
|
message: i18n("admin.customize.theme.delete_success", {
|
|
theme: this.args.theme.name,
|
|
}),
|
|
},
|
|
});
|
|
} catch (error) {
|
|
popupAjaxError(error);
|
|
}
|
|
},
|
|
});
|
|
}
|
|
|
|
<template>
|
|
<AdminConfigAreaCard class={{this.themeCardClasses}}>
|
|
<:content>
|
|
|
|
<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">
|
|
<a class="theme-card__title" href={{this.editUrl}}>{{@theme.name}}</a>
|
|
{{#if @theme.description}}
|
|
<p class="theme-card__description">{{@theme.description}}</p>
|
|
{{/if}}
|
|
</div>
|
|
<div class="theme-card__footer">
|
|
<div class="theme-card__badges">
|
|
{{#if @theme.isPendingUpdates}}
|
|
<span
|
|
title={{i18n "admin.customize.theme.updates_available_tooltip"}}
|
|
class="theme-card__badge"
|
|
>{{icon "arrows-rotate"}}
|
|
{{i18n "admin.customize.theme.update_available"}}</span>
|
|
{{/if}}
|
|
|
|
{{#if @theme.default}}
|
|
<span
|
|
class="theme-card__badge --default"
|
|
title={{i18n "admin.customize.theme.default_theme"}}
|
|
>{{icon "paintbrush"}}
|
|
{{i18n "admin.customize.theme.default"}}</span>
|
|
{{/if}}
|
|
|
|
{{#if @theme.user_selectable}}
|
|
<span
|
|
title={{i18n "admin.customize.theme.user_selectable"}}
|
|
class="theme-card__badge --selectable"
|
|
>{{icon "user-check"}}
|
|
{{i18n
|
|
"admin.customize.theme.user_selectable_badge_label"
|
|
}}</span>
|
|
{{/if}}
|
|
</div>
|
|
|
|
<div class="theme-card__controls">
|
|
<DButton
|
|
@translatedLabel={{i18n "admin.customize.theme.edit"}}
|
|
@route="adminCustomizeThemes.show"
|
|
@routeModels={{this.themeRouteModels}}
|
|
class="btn-secondary theme-card__button edit"
|
|
@preventFocus={{true}}
|
|
/>
|
|
|
|
<div class="theme-card__footer-actions">
|
|
<DMenu
|
|
@identifier="theme-card__footer-menu"
|
|
@triggerClass="theme-card__footer-menu btn-flat"
|
|
@onRegisterApi={{this.onRegisterApi}}
|
|
@modalForMobile={{true}}
|
|
@icon="ellipsis"
|
|
>
|
|
<:content>
|
|
<DropdownMenu as |dropdown|>
|
|
{{! TODO: Jordan
|
|
solutions for broken, disabled states }}
|
|
<dropdown.item>
|
|
<DButton
|
|
@action={{this.setDefault}}
|
|
@preventFocus={{true}}
|
|
@icon={{if @theme.default "star" "far-star"}}
|
|
class="theme-card__button set-default"
|
|
@translatedLabel={{i18n
|
|
(if
|
|
@theme.default
|
|
"admin.customize.theme.default_theme"
|
|
"admin.customize.theme.set_default_theme"
|
|
)
|
|
}}
|
|
@disabled={{@theme.default}}
|
|
/>
|
|
</dropdown.item>
|
|
{{#if @theme.isPendingUpdates}}
|
|
<dropdown.item>
|
|
<DButton
|
|
@action={{this.updateTheme}}
|
|
@icon="cloud-arrow-down"
|
|
class="theme-card__button update"
|
|
@preventFocus={{true}}
|
|
@translatedLabel={{i18n
|
|
"admin.customize.theme.update_to_latest"
|
|
}}
|
|
/>
|
|
</dropdown.item>
|
|
{{/if}}
|
|
<dropdown.item>
|
|
<DButton
|
|
@action={{this.toggleUserSelectable}}
|
|
@preventFocus={{true}}
|
|
@icon={{if
|
|
@theme.user_selectable
|
|
"user-xmark"
|
|
"user-check"
|
|
}}
|
|
class="theme-card__button set-selectable"
|
|
@translatedLabel={{i18n
|
|
(if
|
|
@theme.user_selectable
|
|
"admin.customize.theme.user_selectable_unavailable_button_label"
|
|
"admin.customize.theme.user_selectable_button_label"
|
|
)
|
|
}}
|
|
/>
|
|
</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 preview"
|
|
>{{icon "eye"}}
|
|
{{i18n "admin.customize.theme.preview"}}</a>
|
|
</dropdown.item>
|
|
<dropdown.item>
|
|
<DButton
|
|
@action={{this.destroyTheme}}
|
|
@label="admin.customize.delete"
|
|
@icon="trash-can"
|
|
@disabled={{this.destroyDisabled}}
|
|
class="theme-card__button btn-danger btn-transparent delete"
|
|
/>
|
|
</dropdown.item>
|
|
</DropdownMenu>
|
|
</:content>
|
|
</DMenu>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</:content>
|
|
</AdminConfigAreaCard>
|
|
</template>
|
|
}
|