mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-28 22:43:30 +03:00
Fixed design preview and settings not updating when changing theme
refs https://github.com/TryGhost/Team/issues/1111 We now have a situation where we have modals on modals and we've lost the straightforward built-in "Data down, actions up" communication methods that we'd have workaround across nested routes/controllers. The upshot of that is we didn't have a way to trigger a refresh of the preview when a new theme was activated. - moved the task responsible for fetching preview html from the design modal onto the `theme-management` service and adjusted it to set a tracked `previewHtml` property rather than updating an iframe directly - added a `<GhHtmlIframe>` component that renders a basic iframe element and updates it's contents each time the `@html` argument changes - updated design modal preview to use the new iframe component
This commit is contained in:
parent
640f028ae9
commit
221db9f11e
5
ghost/admin/app/components/gh-html-iframe.hbs
Normal file
5
ghost/admin/app/components/gh-html-iframe.hbs
Normal file
@ -0,0 +1,5 @@
|
||||
<iframe
|
||||
{{did-insert this.registerIframe}}
|
||||
{{did-update this.replaceIframeContents @html}}
|
||||
...attributes>
|
||||
</iframe>
|
20
ghost/admin/app/components/gh-html-iframe.js
Normal file
20
ghost/admin/app/components/gh-html-iframe.js
Normal file
@ -0,0 +1,20 @@
|
||||
import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
|
||||
export default class GhHtmlIframeComponent extends Component {
|
||||
iframe = null;
|
||||
|
||||
@action
|
||||
registerIframe(iframe) {
|
||||
this.iframe = iframe;
|
||||
}
|
||||
|
||||
@action
|
||||
replaceIframeContents(iframe, html) {
|
||||
if (this.iframe) {
|
||||
this.iframe.contentWindow.document.open();
|
||||
this.iframe.contentWindow.document.write(html);
|
||||
this.iframe.contentWindow.document.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@
|
||||
{{#if (set-has this.openSections "brand")}}
|
||||
<div class="pt4">
|
||||
<Settings::Design::GeneralSettingsForm
|
||||
@updatePreview={{perform this.updatePreviewTask}}
|
||||
@updatePreview={{perform this.themeManagement.updatePreviewHtmlTask}}
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
@ -32,7 +32,7 @@
|
||||
<div class="pt4">
|
||||
<Settings::Design::ThemeSettingsForm
|
||||
@themeSettings={{this.siteWideSettings}}
|
||||
@updatePreview={{perform this.updatePreviewTask}}
|
||||
@updatePreview={{perform this.themeManagement.updatePreviewHtmlTask}}
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
@ -50,7 +50,7 @@
|
||||
<div class="pt4">
|
||||
<Settings::Design::ThemeSettingsForm
|
||||
@themeSettings={{this.homepageSettings}}
|
||||
@updatePreview={{perform this.updatePreviewTask}}
|
||||
@updatePreview={{perform this.themeManagement.updatePreviewHtmlTask}}
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
@ -68,7 +68,7 @@
|
||||
<div class="pt4">
|
||||
<Settings::Design::ThemeSettingsForm
|
||||
@themeSettings={{this.postPageSettings}}
|
||||
@updatePreview={{perform this.updatePreviewTask}}
|
||||
@updatePreview={{perform this.themeManagement.updatePreviewHtmlTask}}
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
@ -101,11 +101,7 @@
|
||||
|
||||
<div style="height: calc(100% - 96px)">
|
||||
<GhBrowserPreview @icon={{this.settings.icon}} @title={{this.config.blogTitle}}>
|
||||
<iframe
|
||||
id="site=frame"
|
||||
class="site-frame gh-branding-settings-preview"
|
||||
{{did-insert this.registerPreviewIframe}}
|
||||
></iframe>
|
||||
<GhHtmlIframe id="site-frame" class="site-frame gh-branding-settings-preview" @html={{this.themeManagement.previewHtml}} />
|
||||
</GhBrowserPreview>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -1,5 +1,4 @@
|
||||
import Component from '@glimmer/component';
|
||||
import config from 'ghost-admin/config/environment';
|
||||
import {TrackedSet} from 'tracked-built-ins';
|
||||
import {action} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
@ -7,18 +6,16 @@ import {task} from 'ember-concurrency-decorators';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
export default class ModalsDesignCustomizeComponent extends Component {
|
||||
@service ajax;
|
||||
@service config;
|
||||
@service customThemeSettings;
|
||||
@service settings;
|
||||
@service themeManagement;
|
||||
|
||||
@tracked openSections = new TrackedSet();
|
||||
|
||||
previewIframe = null;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.fetchThemeSettingsTask.perform();
|
||||
this.themeManagement.updatePreviewHtmlTask.perform();
|
||||
}
|
||||
|
||||
get themeSettings() {
|
||||
@ -37,76 +34,13 @@ export default class ModalsDesignCustomizeComponent extends Component {
|
||||
return this.customThemeSettings.settings.filter(setting => setting.group === 'post');
|
||||
}
|
||||
|
||||
get previewData() {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
params.append('c', this.settings.get('accentColor') || '#ffffff');
|
||||
params.append('d', this.settings.get('description'));
|
||||
params.append('icon', this.settings.get('icon'));
|
||||
params.append('logo', this.settings.get('logo'));
|
||||
params.append('cover', this.settings.get('coverImage'));
|
||||
|
||||
params.append('custom', JSON.stringify(this.customThemeSettings.keyValueObject));
|
||||
|
||||
return params.toString();
|
||||
}
|
||||
|
||||
@action
|
||||
toggleSection(section) {
|
||||
this.openSections.has(section) ? this.openSections.delete(section) : this.openSections.add(section);
|
||||
}
|
||||
|
||||
@action
|
||||
registerPreviewIframe(iframe) {
|
||||
this.previewIframe = iframe;
|
||||
this.updatePreviewTask.perform();
|
||||
}
|
||||
|
||||
@action
|
||||
replacePreviewContents(html) {
|
||||
if (this.previewIframe) {
|
||||
this.previewIframe.contentWindow.document.open();
|
||||
this.previewIframe.contentWindow.document.write(html);
|
||||
this.previewIframe.contentWindow.document.close();
|
||||
}
|
||||
}
|
||||
|
||||
@task
|
||||
*fetchThemeSettingsTask() {
|
||||
yield this.customThemeSettings.load();
|
||||
}
|
||||
|
||||
@task
|
||||
*updatePreviewTask() {
|
||||
// skip during testing because we don't have mocks for the front-end
|
||||
if (config.environment === 'test' || !this.previewIframe) {
|
||||
return;
|
||||
}
|
||||
|
||||
// grab the preview html
|
||||
const ajaxOptions = {
|
||||
contentType: 'text/html;charset=utf-8',
|
||||
dataType: 'text',
|
||||
headers: {
|
||||
'x-ghost-preview': this.previewData
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: config.blogUrl always removes trailing slash - switch to always have trailing slash
|
||||
const frontendUrl = `${this.config.get('blogUrl')}/`;
|
||||
const previewContents = yield this.ajax.post(frontendUrl, ajaxOptions);
|
||||
|
||||
// inject extra CSS to disable navigation and prevent clicks
|
||||
const injectedCss = `html { pointer-events: none; }`;
|
||||
|
||||
const domParser = new DOMParser();
|
||||
const htmlDoc = domParser.parseFromString(previewContents, 'text/html');
|
||||
|
||||
const stylesheet = htmlDoc.querySelector('style');
|
||||
const originalCSS = stylesheet.innerHTML;
|
||||
stylesheet.innerHTML = `${originalCSS}\n\n${injectedCss}`;
|
||||
|
||||
// replace the iframe contents with the doctored preview html
|
||||
this.replacePreviewContents(htmlDoc.documentElement.innerHTML);
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,20 @@
|
||||
import Service from '@ember/service';
|
||||
import config from 'ghost-admin/config/environment';
|
||||
import {isEmpty} from '@ember/utils';
|
||||
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';
|
||||
|
||||
export default class ThemeManagementService extends Service {
|
||||
@service ajax;
|
||||
@service config;
|
||||
@service customThemeSettings;
|
||||
@service limit;
|
||||
@service modals;
|
||||
@service settings;
|
||||
|
||||
@tracked previewHtml;
|
||||
|
||||
@task
|
||||
*activateTask(theme) {
|
||||
@ -35,6 +43,9 @@ export default class ThemeManagementService extends Service {
|
||||
try {
|
||||
const activatedTheme = yield theme.activate();
|
||||
|
||||
this.updatePreviewHtmlTask.perform();
|
||||
this.customThemeSettings.load();
|
||||
|
||||
const {warnings, errors} = activatedTheme;
|
||||
|
||||
if (!isEmpty(warnings) || !isEmpty(errors)) {
|
||||
@ -83,4 +94,52 @@ export default class ThemeManagementService extends Service {
|
||||
resultModal?.close();
|
||||
}
|
||||
}
|
||||
|
||||
@task
|
||||
*updatePreviewHtmlTask() {
|
||||
// skip during testing because we don't have mocks for the front-end
|
||||
if (config.environment === 'test') {
|
||||
return;
|
||||
}
|
||||
|
||||
// grab the preview html
|
||||
const ajaxOptions = {
|
||||
contentType: 'text/html;charset=utf-8',
|
||||
dataType: 'text',
|
||||
headers: {
|
||||
'x-ghost-preview': this.previewData
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: config.blogUrl always removes trailing slash - switch to always have trailing slash
|
||||
const frontendUrl = `${this.config.get('blogUrl')}/`;
|
||||
const previewContents = yield this.ajax.post(frontendUrl, ajaxOptions);
|
||||
|
||||
// inject extra CSS to disable navigation and prevent clicks
|
||||
const injectedCss = `html { pointer-events: none; }`;
|
||||
|
||||
const domParser = new DOMParser();
|
||||
const htmlDoc = domParser.parseFromString(previewContents, 'text/html');
|
||||
|
||||
const stylesheet = htmlDoc.querySelector('style');
|
||||
const originalCSS = stylesheet.innerHTML;
|
||||
stylesheet.innerHTML = `${originalCSS}\n\n${injectedCss}`;
|
||||
|
||||
// replace the iframe contents with the doctored preview html
|
||||
this.previewHtml = htmlDoc.documentElement.innerHTML;
|
||||
}
|
||||
|
||||
get previewData() {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
params.append('c', this.settings.get('accentColor') || '#ffffff');
|
||||
params.append('d', this.settings.get('description'));
|
||||
params.append('icon', this.settings.get('icon'));
|
||||
params.append('logo', this.settings.get('logo'));
|
||||
params.append('cover', this.settings.get('coverImage'));
|
||||
|
||||
params.append('custom', JSON.stringify(this.customThemeSettings.keyValueObject));
|
||||
|
||||
return params.toString();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user