ESLint: Don't use observers if possible

closes https://github.com/TryGhost/Ghost/issues/8690
- https://github.com/ember-cli/eslint-plugin-ember/blob/master/docs/rules/no-observers.md
- remove observers where possible
- move night shift toggle behaviour to the feature service
	- refresh feature service after import to fix stuck night shift toggle
This commit is contained in:
Kevin Ansfield 2018-01-11 14:16:42 +00:00
parent b6ae61c22f
commit f2da8a20b8
14 changed files with 75 additions and 73 deletions

View File

@ -63,6 +63,9 @@ export default Component.extend({
tagToAdd = this.get('store').createRecord('tag', {
name: tagName
});
// set to public/internal based on the tag name
tagToAdd.updateVisibility();
}
// push tag onto post relationship

View File

@ -2,8 +2,6 @@ import Component from '@ember/component';
import {computed} from '@ember/object';
import {equal, reads} from '@ember/object/computed';
import {isBlank} from '@ember/utils';
import {observer} from '@ember/object';
import {run} from '@ember/runloop';
import {inject as service} from '@ember/service';
export default Component.extend({
@ -20,7 +18,12 @@ export default Component.extend({
init() {
this._super(...arguments);
run.schedule('actions', this, this.fireMobileChangeActions);
this.get('mediaQueries').on('change', this, this._fireMobileChangeActions);
},
willDestroyElement() {
this._super(...arguments);
this.get('mediaQueries').off('change', this, this._fireMobileChangeActions);
},
displaySettingsPane: computed('isEmpty', 'selectedTag', 'isMobile', function () {
@ -42,12 +45,15 @@ export default Component.extend({
return true;
}),
fireMobileChangeActions: observer('isMobile', function () {
if (!this.get('isMobile')) {
let action = this.get('leftMobile');
if (action) {
action();
_fireMobileChangeActions(key, value) {
if (key === 'maxWidth600') {
let leftMobileAction = this.get('leftMobile');
this.set('isMobile', value);
if (!value && leftMobileAction) {
leftMobileAction();
}
}
})
}
});

View File

@ -2,7 +2,6 @@ import Component from '@ember/component';
import {computed} from '@ember/object';
import {invokeAction} from 'ember-invoke-action';
import {isBlank} from '@ember/utils';
import {observer} from '@ember/object';
import {reads} from '@ember/object/computed';
import {task, timeout} from 'ember-concurrency';
@ -39,10 +38,18 @@ const GhTaskButton = Component.extend({
failureText: 'Retry',
failureClass: 'gh-btn-red',
isRunning: reads('task.last.isRunning'),
init() {
this._super(...arguments);
this._initialPerformCount = this.get('task.performCount');
},
// hasRun is needed so that a newly rendered button does not show the last
// state of the associated task
hasRun: false,
isRunning: reads('task.last.isRunning'),
hasRun: computed('task.performCount', function () {
return this.get('task.performCount') > this._initialPerformCount;
}),
isIdleClass: computed('isIdle', function () {
if (this.get('isIdle')) {
@ -115,17 +122,6 @@ const GhTaskButton = Component.extend({
return false;
},
setSize: observer('isRunning', function () {
if (this.get('isRunning')) {
this.set('hasRun', true);
// this.$().width(this.$().width());
// this.$().height(this.$().height());
} else {
// this.$().width('');
// this.$().height('');
}
}),
// when local validation fails there's no transition from failed->running
// so we want to restart the retry spinner animation to show something
// has happened when the button is clicked

View File

@ -7,7 +7,6 @@ import {
IMAGE_MIME_TYPES
} from 'ghost-admin/components/gh-image-uploader';
import {computed} from '@ember/object';
import {observer} from '@ember/object';
import {run} from '@ember/runloop';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency';
@ -45,13 +44,6 @@ export default Controller.extend({
}
}),
generatePassword: observer('settings.isPrivate', function () {
this.get('settings.errors').remove('password');
if (this.get('settings.isPrivate') && this.get('settings.hasDirtyAttributes')) {
this.get('settings').set('password', randomPassword());
}
}),
privateRSSUrl: computed('config.blogUrl', 'settings.publicHash', function () {
let blogUrl = this.get('config.blogUrl');
let publicHash = this.get('settings.publicHash');
@ -143,6 +135,16 @@ export default Controller.extend({
}
},
toggleIsPrivate(isPrivate) {
this.set('settings.isPrivate', isPrivate);
this.get('settings.errors').remove('password');
// set a new random password when isPrivate is enabled
if (isPrivate && this.get('settings.hasDirtyAttributes')) {
this.get('settings').set('password', randomPassword());
}
},
toggleLeaveSettingsModal(transition) {
let leaveTransition = this.get('leaveSettingsTransition');

View File

@ -38,6 +38,7 @@ export default Controller.extend({
ajax: service(),
config: service(),
feature: service(),
ghostPaths: service(),
notifications: service(),
session: service(),
@ -166,6 +167,7 @@ export default Controller.extend({
// reload settings
return this.get('settings').reload().then((settings) => {
this.get('feature').fetch();
this.get('config').set('blogTitle', settings.get('title'));
});
});

View File

@ -37,6 +37,7 @@ export default Mixin.create({
this.set('hasError', false);
},
// eslint-disable-next-line ghost/ember/no-observers
hasErrorObserver: observer('errors.[]', 'property', 'hasValidated.[]', function () {
run.once(this, 'setHasError');
// this.setHasError();

View File

@ -223,6 +223,8 @@ export default Model.extend(Comparable, ValidationEngine, {
}
},
// TODO: is there a better way to handle this?
// eslint-disable-next-line ghost/ember/no-observers
_setPublishedAtBlogTZ: observer('publishedAtUTC', 'settings.activeTimezone', function () {
let publishedAtUTC = this.get('publishedAtUTC');
this._setPublishedAtBlogStrings(publishedAtUTC);

View File

@ -2,7 +2,6 @@ import Model from 'ember-data/model';
import ValidationEngine from 'ghost-admin/mixins/validation-engine';
import attr from 'ember-data/attr';
import {equal} from '@ember/object/computed';
import {observer} from '@ember/object';
import {inject as service} from '@ember/service';
export default Model.extend(ValidationEngine, {
@ -27,21 +26,15 @@ export default Model.extend(ValidationEngine, {
feature: service(),
setVisibility() {
updateVisibility() {
let internalRegex = /^#.?/;
this.set('visibility', internalRegex.test(this.get('name')) ? 'internal' : 'public');
},
save() {
if (this.get('changedAttributes.name') && !this.get('isDeleted')) {
this.setVisibility();
this.updateVisibility();
}
return this._super(...arguments);
},
setVisibilityOnNew: observer('isNew', 'isSaving', 'name', function () {
if (this.get('isNew') && !this.get('isSaving')) {
this.setVisibility();
}
}).on('init')
}
});

View File

@ -1,4 +1,3 @@
import $ from 'jquery';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
import AuthConfiguration from 'ember-simple-auth/configuration';
import RSVP from 'rsvp';
@ -18,7 +17,6 @@ import {
isMaintenanceError,
isVersionMismatchError
} from 'ghost-admin/services/ajax';
import {observer} from '@ember/object';
import {run} from '@ember/runloop';
import {inject as service} from '@ember/service';
@ -38,7 +36,6 @@ export default Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
config: service(),
feature: service(),
lazyLoader: service(),
notifications: service(),
settings: service(),
tour: service(),
@ -73,12 +70,7 @@ export default Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
}
}
let featurePromise = this.get('feature').fetch().then(() => {
if (this.get('feature.nightShift')) {
return this._setAdminTheme();
}
});
let featurePromise = this.get('feature').fetch();
let settingsPromise = this.get('settings').fetch();
let privateConfigPromise = this.get('config').fetchPrivate();
let tourPromise = this.get('tour').fetchViewed();
@ -124,19 +116,6 @@ export default Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
}
},
_nightShift: observer('feature.nightShift', function () {
this._setAdminTheme();
}),
_setAdminTheme() {
let nightShift = this.get('feature.nightShift');
return this.get('lazyLoader').loadStyle('dark', 'assets/ghost-dark.css', true).then(() => {
$('link[title=dark]').prop('disabled', !nightShift);
$('link[title=light]').prop('disabled', nightShift);
});
},
actions: {
closeMenus() {
this.get('ui').closeMenus();
@ -150,10 +129,6 @@ export default Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
signedIn() {
this.get('notifications').clearAll();
this.send('loadServerNotifications', true);
if (this.get('feature.nightShift')) {
this._setAdminTheme();
}
},
invalidateSession() {

View File

@ -1,3 +1,4 @@
import $ from 'jquery';
import Ember from 'ember';
import EmberError from '@ember/error';
import RSVP from 'rsvp';
@ -5,7 +6,8 @@ import Service, {inject as service} from '@ember/service';
import {computed} from '@ember/object';
import {set} from '@ember/object';
export function feature(name, user = false) {
export function feature(name, options = {}) {
let {user, onChange} = options;
let watchedProps = user ? [`accessibility.${name}`] : [`config.${name}`, `labs.${name}`];
return computed.apply(Ember, watchedProps.concat({
@ -22,6 +24,13 @@ export function feature(name, user = false) {
},
set(key, value) {
this.update(key, value, user);
if (onChange) {
// value must be passed here because the value isn't set until
// the setter function returns
this.get(onChange).bind(this)(value);
}
return value;
}
}));
@ -33,10 +42,11 @@ export default Service.extend({
session: service(),
settings: service(),
notifications: service(),
lazyLoader: service(),
publicAPI: feature('publicAPI'),
subscribers: feature('subscribers'),
nightShift: feature('nightShift', true),
nightShift: feature('nightShift', {user: true, onChange: '_setAdminTheme'}),
_user: null,
@ -66,8 +76,7 @@ export default Service.extend({
user: this.get('session.user')
}).then(({user}) => {
this.set('_user', user);
return true;
return this._setAdminTheme().then(() => true);
});
},
@ -100,5 +109,14 @@ export default Service.extend({
return this.get(`${serviceProperty}.${key}`);
});
},
_setAdminTheme(enabled) {
let nightShift = enabled || this.get('nightShift');
return this.get('lazyLoader').loadStyle('dark', 'assets/ghost-dark.css', true).then(() => {
$('link[title=dark]').prop('disabled', !nightShift);
$('link[title=light]').prop('disabled', nightShift);
});
}
});

View File

@ -243,7 +243,7 @@
<div class="gh-setting-action">
<div class="for-checkbox">
<label class="checkbox" for="settings-private">
{{one-way-checkbox settings.isPrivate id="settings-private" type="checkbox" update=(action (mut settings.isPrivate)) data-test-private-checkbox=true}}
{{one-way-checkbox settings.isPrivate id="settings-private" type="checkbox" update=(action "toggleIsPrivate") data-test-private-checkbox=true}}
<span class="input-toggle-component"></span>
</label>
</div>

View File

@ -9,6 +9,8 @@ export default Component.extend({
layout,
hasRendered: false,
// TODO: remove observer
// eslint-disable-next-line ghost/ember/no-observers
save: observer('doSave', function () {
let payload = this.get('payload');
payload.wordcount = counter(payload.html);

View File

@ -27,6 +27,8 @@ export default Component.extend({
return formatMarkdown([this.get('payload').markdown]);
}),
// TODO: remove observer
// eslint-disable-next-line ghost/ember/no-observers
save: observer('doSave', function () {
let payload = this.get('payload');
payload.markdown = this.$('textarea').val();

View File

@ -65,7 +65,7 @@ function stubUser(server, accessibility, validSave = true) {
function addTestFlag() {
FeatureService.reopen({
testFlag: feature('testFlag'),
testUserFlag: feature('testUserFlag', true)
testUserFlag: feature('testUserFlag', {user: true})
});
}