Added a separate settings page for the announcement bar

refs https://github.com/TryGhost/Team/issues/3080
This commit is contained in:
Sodbileg Gansukh 2023-04-20 22:21:07 +01:00
parent 44206fad38
commit e5066252bd
11 changed files with 333 additions and 23 deletions

View File

@ -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>

View File

@ -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}}

View File

@ -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>

View 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>

View 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();
}
}
}

View File

@ -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>

View File

@ -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');
}
}

View File

@ -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');

View 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']
};
}
}

View File

@ -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>

View File

@ -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>