Ghost/ghost/admin/app/components/modal-install-theme.js

164 lines
4.6 KiB
JavaScript

import ModalBase from 'ghost-admin/components/modal-base';
import classic from 'ember-classic-decorator';
import {action} from '@ember/object';
import {isThemeValidationError} from 'ghost-admin/services/ajax';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency-decorators';
import {tracked} from '@glimmer/tracking';
import {MARKETPLACE_THEMES} from 'ghost-admin/controllers/settings/theme';
// TODO: update modals to work fully with Glimmer components
@classic
export default class ModalInstallThemeComponent extends ModalBase {
@service ajax;
@service ghostPaths;
@service store;
@tracked model;
@tracked theme;
@tracked installError = '';
@tracked validationWarnings = [];
@tracked validationErrors = [];
@tracked fatalValidationErrors = [];
get themeName() {
return this.model.ref.split('/')[1];
}
get marketplaceTheme() {
return MARKETPLACE_THEMES.find(theme => theme.name.toLowerCase() === this.themeName.toLowerCase());
}
get currentThemeNames() {
return this.model.themes.mapBy('name');
}
get willOverwriteDefault() {
return this.themeName.toLowerCase() === 'casper';
}
get willOverwriteExisting() {
return this.model.themes.findBy('name', this.themeName.toLowerCase());
}
get installSuccess() {
return !!this.theme;
}
get installFailure() {
return !this.installSuccess && (this.validationErrors.length || this.fatalValidationErrors.length);
}
get isReady() {
return !this.installSuccess && !this.installError && !this.installFailure && !this.willOverwriteDefault;
}
get hasWarningsOrErrors() {
return this.validationWarnings.length > 0 || this.validationErrors.length > 0;
}
get shouldShowInstall() {
return !this.installSuccess && !this.installFailure && !this.willOverwriteDefault;
}
get shouldShowActivate() {
return this.installSuccess && !this.theme.active;
}
get hasActionButton() {
return this.shouldShowInstall || this.shouldShowActivate;
}
@action
close() {
this.closeModal();
}
@action
reset() {
this.theme = null;
this.resetErrors();
}
actions = {
confirm() {
// noop - needed to override ModalBase.actions.confirm
},
// needed because ModalBase uses .send() for keyboard events
closeModal() {
this.closeModal();
}
}
@task({drop: true})
*installTask() {
try {
const url = this.ghostPaths.url.api('themes/install') + `?source=github&ref=${this.model.ref}`;
const result = yield this.ajax.post(url);
this.installError = '';
if (result.themes) {
// show theme in list immediately
this.store.pushPayload(result);
this.theme = this.store.peekRecord('theme', result.themes[0].name);
this.validationWarnings = this.theme.warnings || [];
this.validationErrors = this.theme.errors || [];
this.fatalValidationErrors = [];
return true;
}
} catch (error) {
if (isThemeValidationError(error)) {
this.resetErrors();
let errors = error.payload.errors[0].details.errors;
let fatalErrors = [];
let normalErrors = [];
// to have a proper grouping of fatal errors and none fatal, we need to check
// our errors for the fatal property
if (errors && errors.length > 0) {
for (let i = 0; i < errors.length; i += 1) {
if (errors[i].fatal) {
fatalErrors.push(errors[i]);
} else {
normalErrors.push(errors[i]);
}
}
}
this.fatalValidationErrors = fatalErrors;
this.validationErrors = normalErrors;
return false;
}
if (error.payload?.errors) {
this.installError = error.payload.errors[0].message;
return false;
}
this.installError = error.message;
throw error;
}
}
@task({drop: true})
*activateTask() {
yield this.theme.activate();
this.closeModal();
}
resetErrors() {
this.installError = '';
this.validationWarnings = [];
this.validationErrors = [];
this.fatalValidationErrors = [];
}
}