mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-27 18:52:14 +03:00
Added a separate settings page for the announcement bar
refs https://github.com/TryGhost/Team/issues/3080
This commit is contained in:
parent
44206fad38
commit
e5066252bd
@ -1,18 +1,28 @@
|
||||
<div class="gh-stack-item {{if (eq @index 0) "gh-setting-first" "gh-setting"}}">
|
||||
<div class="flex-grow-1">
|
||||
<label class="gh-setting-title gh-theme-setting-title" for="announcement-background">
|
||||
Announcement background
|
||||
</label>
|
||||
<div class="flex justify-between items-center relative">
|
||||
<label class="gh-setting-title" for="announcement-background">
|
||||
Background color
|
||||
</label>
|
||||
|
||||
<span class="gh-select">
|
||||
<select class="ember-select" name="announcement-background" id="announcement-background" {{on "change" this.setBackground}}>
|
||||
{{#each this.options as |settingOption|}}
|
||||
<option value={{settingOption.value}} selected={{eq settingOption.value this.background}}>
|
||||
{{settingOption.label}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
{{svg-jar "arrow-down-small"}}
|
||||
</span>
|
||||
<div class="kg-settings-panel-control-input">
|
||||
<div class="gh-btn-group kg-settings-headerstyle-btn-group">
|
||||
<button class="gh-btn kg-headerstyle-btn-dark gh-btn-group-selected" type="button"></button>
|
||||
<button class="gh-btn kg-headerstyle-btn-light" type="button"></button>
|
||||
<button class="gh-btn kg-headerstyle-btn-accent" type="button"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- <span class="gh-select">
|
||||
<select class="ember-select" name="announcement-background" id="announcement-background" {{on "change" this.setBackground}}>
|
||||
{{#each this.options as |settingOption|}}
|
||||
<option value={{settingOption.value}} selected={{eq settingOption.value this.background}}>
|
||||
{{settingOption.label}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
{{svg-jar "arrow-down-small"}}
|
||||
</span> --}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,11 +1,11 @@
|
||||
<div class="gh-stack-item {{if (eq @index 0) "gh-setting-first" "gh-setting"}}">
|
||||
<div class="gh-stack-item gh-setting-first">
|
||||
<div class="flex-grow-1">
|
||||
<label class="gh-setting-title gh-theme-setting-title" for={{this.selectId}}>
|
||||
Announcement
|
||||
</label>
|
||||
<div class="gh-announcement-editor">
|
||||
<KoenigLexicalEditorInput
|
||||
@placeholderText="Breaking news, a big story, a special offer, or any other message"
|
||||
@placeholderText="Highlight breaking news, special offers, or important updates"
|
||||
@html={{this.content}}
|
||||
@onChangeHtml={{this.setContent}}
|
||||
@onBlur={{@onChange}}
|
||||
|
@ -1,10 +1,43 @@
|
||||
<div class="gh-stack-item {{if (eq @index 0) "gh-setting-first" "gh-setting"}}">
|
||||
<div class="flex-grow-1">
|
||||
<label class="gh-setting-title gh-theme-setting-title" for="announcement-visibility">
|
||||
Announcement visibility
|
||||
Visibility
|
||||
</label>
|
||||
|
||||
<span class="gh-select">
|
||||
<div class="form-group mt3 mb0 for-checkbox">
|
||||
<label class="checkbox" for="logged-out">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="logged-out"
|
||||
name="logged-out"
|
||||
class="gh-input"
|
||||
>
|
||||
<span class="input-toggle-component"></span>
|
||||
<p>Logged out visitors</p>
|
||||
</label>
|
||||
<label class="checkbox" for="free">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="free"
|
||||
name="free"
|
||||
class="gh-input"
|
||||
>
|
||||
<span class="input-toggle-component"></span>
|
||||
<p>Free members</p>
|
||||
</label>
|
||||
<label class="checkbox" for="paid">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="paid"
|
||||
name="paid"
|
||||
class="gh-input"
|
||||
>
|
||||
<span class="input-toggle-component"></span>
|
||||
<p>Paid members</p>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{{!-- <span class="gh-select">
|
||||
<select class="ember-select" name="announcement-visibility" id="announcement-visibility" {{on "change" this.setVisibility}}>
|
||||
{{#each this.options as |settingOption|}}
|
||||
<option value={{settingOption.value}} selected={{eq settingOption.value this.background}}>
|
||||
@ -13,6 +46,6 @@
|
||||
{{/each}}
|
||||
</select>
|
||||
{{svg-jar "arrow-down-small"}}
|
||||
</span>
|
||||
</span> --}}
|
||||
</div>
|
||||
</div>
|
19
ghost/admin/app/components/gh-nav-menu/announcement-bar.hbs
Normal file
19
ghost/admin/app/components/gh-nav-menu/announcement-bar.hbs
Normal file
@ -0,0 +1,19 @@
|
||||
<div class="flex flex-column h-100" {{css-transition "gh-nav-contextual"}} data-test-nav-menu="design" ...attributes>
|
||||
<header class="gh-nav-header">
|
||||
<LinkTo @route="settings" class="gh-nav-menu-back-button" data-test-link="back-to-settings">Settings</LinkTo>
|
||||
{{svg-jar "arrow-right-small"}} Announcement bar
|
||||
</header>
|
||||
<section class="gh-nav-body gh-nav-design">
|
||||
<div class="gh-nav-top">
|
||||
<div class="gh-nav-list gh-nav-main">
|
||||
<div class="gh-nav-design-settings">
|
||||
<form>
|
||||
<AnnouncementSettings::Content @onChange={{@updatePreview}} />
|
||||
<AnnouncementSettings::Background @onChange={{@updatePreview}} />
|
||||
<AnnouncementSettings::Visibility @onChange={{@updatePreview}} />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
86
ghost/admin/app/components/gh-nav-menu/announcement-bar.js
Normal file
86
ghost/admin/app/components/gh-nav-menu/announcement-bar.js
Normal file
@ -0,0 +1,86 @@
|
||||
import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
import {bind} from '@ember/runloop';
|
||||
import {inject} from 'ghost-admin/decorators/inject';
|
||||
import {isEmpty} from '@ember/utils';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
export default class AnnouncementBarMenuComponent extends Component {
|
||||
@service customThemeSettings;
|
||||
@service router;
|
||||
@service settings;
|
||||
@service store;
|
||||
@service themeManagement;
|
||||
|
||||
@inject config;
|
||||
|
||||
@tracked openSection = null;
|
||||
|
||||
themes = this.store.peekAll('theme');
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
// fetch all themes in the background so we can show the active theme
|
||||
this.store.findAll('theme');
|
||||
|
||||
if (this.router.currentRouteName === 'settings.announcement-bar.index') {
|
||||
this.openDefaultSection();
|
||||
}
|
||||
|
||||
this.routeDidChangeHandler = bind(this, this.handleRouteDidChange);
|
||||
this.router.on('routeDidChange', this.routeDidChangeHandler);
|
||||
}
|
||||
|
||||
willDestroy() {
|
||||
super.willDestroy(...arguments);
|
||||
this.router.off('routeDidChange', this.routeDidChangeHandler);
|
||||
}
|
||||
|
||||
get activeTheme() {
|
||||
return this.themes.findBy('active', true);
|
||||
}
|
||||
|
||||
@action
|
||||
toggleSection(section) {
|
||||
if (this.openSection === section) {
|
||||
this.openSection = null;
|
||||
} else {
|
||||
this.openSection = section;
|
||||
|
||||
const group = this.customThemeSettings.KNOWN_GROUPS.findBy('key', section);
|
||||
if (group && group.previewType) {
|
||||
this.themeManagement.setPreviewType(group.previewType);
|
||||
} else {
|
||||
this.themeManagement.setPreviewType('homepage');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
transitionBackToIndex() {
|
||||
if (this.router.currentRouteName !== 'settings.announcement-bar.index') {
|
||||
this.router.transitionTo('settings.announcement-bar.index');
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
closeAllSections() {
|
||||
this.openSection = null;
|
||||
}
|
||||
|
||||
openDefaultSection() {
|
||||
const noCustomSettings = isEmpty(this.customThemeSettings.settings);
|
||||
|
||||
if (noCustomSettings) {
|
||||
this.openSection = 'brand';
|
||||
}
|
||||
}
|
||||
|
||||
handleRouteDidChange(transition) {
|
||||
if (!transition.isAborted && transition.to?.name === 'settings.announcement-bar.index') {
|
||||
this.openDefaultSection();
|
||||
}
|
||||
}
|
||||
}
|
@ -17,10 +17,5 @@
|
||||
<CustomThemeSettings::Image @setting={{setting}} @index={{index}} @onChange={{@updatePreview}} />
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
{{#if (feature 'announcementBar')}}
|
||||
<AnnouncementSettings::Content @onChange={{@updatePreview}} />
|
||||
<AnnouncementSettings::Background @onChange={{@updatePreview}} />
|
||||
<AnnouncementSettings::Visibility @onChange={{@updatePreview}} />
|
||||
{{/if}}
|
||||
</form>
|
||||
</div>
|
@ -0,0 +1,63 @@
|
||||
import Controller from '@ember/controller';
|
||||
import {action} from '@ember/object';
|
||||
import {inject} from 'ghost-admin/decorators/inject';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {task} from 'ember-concurrency';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
export default class SettingsAnnouncementBarIndexController extends Controller {
|
||||
@service customThemeSettings;
|
||||
@service notifications;
|
||||
@service settings;
|
||||
@service themeManagement;
|
||||
|
||||
@inject config;
|
||||
|
||||
@tracked previewSize = 'desktop';
|
||||
|
||||
get isDesktopPreview() {
|
||||
return this.previewSize === 'desktop';
|
||||
}
|
||||
|
||||
get isMobilePreview() {
|
||||
return this.previewSize === 'mobile';
|
||||
}
|
||||
|
||||
@action
|
||||
setPreviewSize(size) {
|
||||
this.previewSize = size;
|
||||
}
|
||||
|
||||
@action
|
||||
saveFromKeyboard() {
|
||||
document.activeElement.blur?.();
|
||||
return this.saveTask.perform();
|
||||
}
|
||||
|
||||
@task
|
||||
*saveTask() {
|
||||
try {
|
||||
if (this.settings.errors.length !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
yield Promise.all([
|
||||
this.settings.save(),
|
||||
this.customThemeSettings.save()
|
||||
]);
|
||||
|
||||
// ensure task button switches to success state
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error) {
|
||||
this.notifications.showAPIError(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.previewSize = 'desktop';
|
||||
this.themeManagement.setPreviewType('homepage');
|
||||
}
|
||||
}
|
@ -56,6 +56,7 @@ Router.map(function () {
|
||||
this.route('settings.code-injection', {path: '/settings/code-injection'});
|
||||
this.route('settings.history', {path: '/settings/history'});
|
||||
this.route('settings.analytics', {path: '/settings/analytics'});
|
||||
this.route('settings.announcement-bar', {path: '/settings/announcement-bar'}, function () {});
|
||||
|
||||
// testing websockets
|
||||
this.route('websockets');
|
||||
|
52
ghost/admin/app/routes/settings/announcement-bar.js
Normal file
52
ghost/admin/app/routes/settings/announcement-bar.js
Normal file
@ -0,0 +1,52 @@
|
||||
import AdminRoute from 'ghost-admin/routes/authenticated';
|
||||
import {inject as service} from '@ember/service';
|
||||
|
||||
export default class SettingsDesignRoute extends AdminRoute {
|
||||
@service customThemeSettings;
|
||||
@service feature;
|
||||
@service modals;
|
||||
@service settings;
|
||||
@service themeManagement;
|
||||
@service ui;
|
||||
@service session;
|
||||
@service store;
|
||||
|
||||
model() {
|
||||
// background refresh of preview
|
||||
// not doing it on the 'index' route so that we don't reload going to/from the index,
|
||||
// any actions performed on child routes that need a refresh should trigger it explicitly
|
||||
this.themeManagement.updatePreviewHtmlTask.perform();
|
||||
|
||||
// wait for settings to be loaded - we need the data to be present before display
|
||||
return Promise.all([
|
||||
this.settings.reload(),
|
||||
this.customThemeSettings.load(),
|
||||
this.store.findAll('theme')
|
||||
]);
|
||||
}
|
||||
|
||||
beforeModel() {
|
||||
super.beforeModel(...arguments);
|
||||
|
||||
const user = this.session.user;
|
||||
|
||||
if (!user.isAdmin) {
|
||||
return this.transitionTo('settings.staff.user', user);
|
||||
}
|
||||
}
|
||||
|
||||
activate() {
|
||||
this.ui.contextualNavMenu = 'announcement-bar';
|
||||
}
|
||||
|
||||
deactivate() {
|
||||
this.ui.contextualNavMenu = null;
|
||||
}
|
||||
|
||||
buildRouteInfoMetadata() {
|
||||
return {
|
||||
titleToken: 'Settings - Announcement bar',
|
||||
mainClasses: ['gh-main-fullwidth']
|
||||
};
|
||||
}
|
||||
}
|
@ -49,6 +49,16 @@
|
||||
<p>Manage authors, editor and collaborators</p>
|
||||
</div>
|
||||
</LinkTo>
|
||||
|
||||
{{#if (feature 'announcementBar')}}
|
||||
<LinkTo class="gh-setting-group" @route="settings.announcement-bar" data-test-nav="announcement-bar">
|
||||
<span class="yellow">{{svg-jar "confetti"}}</span>
|
||||
<div>
|
||||
<h4>Announcement bar</h4>
|
||||
<p>Highlight breaking news, special offers, or important updates</p>
|
||||
</div>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="gh-setting-header">Members</div>
|
||||
|
@ -0,0 +1,41 @@
|
||||
<section class="gh-canvas gh-design" {{on-key "cmd+s" this.saveFromKeyboard}}>
|
||||
<GhCanvasHeader class="gh-canvas-header">
|
||||
<h2 class="gh-canvas-title">Announcement bar</h2>
|
||||
<section class="view-actions">
|
||||
<div class="gh-select gh-preview-page-selector">
|
||||
<OneWaySelect
|
||||
@value={{this.themeManagement.previewType}}
|
||||
@options={{this.themeManagement.availablePreviewTypes}}
|
||||
@optionValuePath="name"
|
||||
@optionLabelPath="label"
|
||||
@optionTargetPath="name"
|
||||
@update={{this.themeManagement.setPreviewType}}
|
||||
/>
|
||||
{{svg-jar "arrow-down-small"}}
|
||||
</div>
|
||||
|
||||
<div class="gh-contentfilter gh-btn-group">
|
||||
<button type="button" class="gh-btn gh-design-preview-mode {{if this.isDesktopPreview "gh-btn-group-selected"}}" {{on "click" (fn this.setPreviewSize "desktop")}} data-test-button="desktop-preview"><span>{{svg-jar "desktop"}}</span></button>
|
||||
<button type="button" class="gh-btn gh-design-preview-mode {{if this.isMobilePreview "gh-btn-group-selected"}}" {{on "click" (fn this.setPreviewSize "mobile")}} data-test-button="mobile-preview"><span>{{svg-jar "mobile-phone"}}</span></button>
|
||||
</div>
|
||||
|
||||
<GhTaskButton
|
||||
@buttonText="Save"
|
||||
@task={{this.saveTask}}
|
||||
@successText="Saved"
|
||||
@runningText="Saving"
|
||||
@class="gh-btn gh-btn-primary gh-btn-icon"
|
||||
data-test-button="save-settings"
|
||||
/>
|
||||
</section>
|
||||
</GhCanvasHeader>
|
||||
|
||||
<div class="view-container">
|
||||
<GhBrowserPreview @isMobilePreview={{this.isMobilePreview}}>
|
||||
<GhHtmlIframe
|
||||
class={{if this.isMobilePreview "gh-post-preview-iframe" "site-frame"}}
|
||||
@html={{this.themeManagement.previewHtml}}
|
||||
@pageId={{this.themeManagement.previewType}} />
|
||||
</GhBrowserPreview>
|
||||
</div>
|
||||
</section>
|
Loading…
Reference in New Issue
Block a user