Ghost/ghost/admin/app/components/gh-theme-table-labs.js
Kevin Ansfield 715ee08100 Added advanced theme settings modal
refs https://github.com/TryGhost/Team/issues/1111

Extracted functionality for listing, downloading, activating, and deleting from the theme controller/template into separate components and services so that they are more composable/reusable in different situations.

- moved theme activation to a new `theme-management` service that uses the `modals` service to open the theme warnings modal or limits upgrade modal as required
  - the activate process is a task so that consumers can store a reference to the task instance and cancel it to close any related warning/limit modals (eg, when navigating away from the route or closing the modal that kicked off the process)
- created new-pattern modals for custom theme limit upgrade, theme errors, and delete confirmation so that we can treat them as promises and close where needed from parent
- duplicated theme table component as `<GhThemeTableLabs>` with an actions redesign and a refactor to handle download, activation, and deletion itself making use of the new theme-management service and modals
- fixed some oddities with design modal's transition/modal close handling by simplifying the async behaviour and being more explicit
- added advanced design modal that contains the new theme table component and linked to it from footer of design modal's sidebar
2021-10-05 20:44:27 +01:00

100 lines
2.9 KiB
JavaScript

import Component from '@glimmer/component';
import {action} from '@ember/object';
import {get} from '@ember/object';
import {inject as service} from '@ember/service';
export default class GhThemeTableComponent extends Component {
@service ghostPaths;
@service modals;
@service themeManagement;
@service utils;
activateTaskInstance = null;
confirmDeleteModal = null;
willDestroy() {
super.willDestroy(...arguments);
this.confirmDeleteModal?.close();
this.activateTaskInstance?.cancel();
}
get sortedThemes() {
let themes = this.args.themes.map((t) => {
let theme = {};
let themePackage = get(t, 'package');
theme.model = t;
theme.name = get(t, 'name');
theme.label = themePackage ? `${themePackage.name}` : theme.name;
theme.version = themePackage ? `${themePackage.version}` : '1.0';
theme.package = themePackage;
theme.active = get(t, 'active');
theme.isDeletable = !theme.active;
return theme;
});
let duplicateThemes = [];
themes.forEach((theme) => {
let duplicateLabels = themes.filterBy('label', theme.label);
if (duplicateLabels.length > 1) {
duplicateThemes.pushObject(theme);
}
});
duplicateThemes.forEach((theme) => {
if (theme.name !== 'casper') {
theme.label = `${theme.label} (${theme.name})`;
}
});
// "(default)" needs to be added to casper manually as it's always
// displayed and would mess up the duplicate checking if added earlier
let casper = themes.findBy('name', 'casper');
if (casper) {
casper.label = `${casper.label} (default)`;
casper.isDefault = true;
casper.isDeletable = false;
}
// sorting manually because .sortBy('label') has a different sorting
// algorithm to [...strings].sort()
return themes.sort((themeA, themeB) => {
let a = themeA.label.toLowerCase();
let b = themeB.label.toLowerCase();
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
});
}
@action
downloadTheme(themeName, dropdown) {
dropdown?.actions.close();
this.utils.downloadFile(`${this.ghostPaths.apiRoot}/themes/${themeName}/download/`);
}
@action
activateTheme(theme, dropdown) {
dropdown?.actions.close();
this.activateTaskInstance = this.themeManagement.activateTask.perform(theme);
}
@action
deleteTheme(theme, dropdown) {
dropdown?.actions.close();
this.confirmDeleteModal = this.modals.open('modals/design/confirm-delete-theme', {
theme
}).finally(() => {
this.confirmDeleteModal = null;
});
}
}