diff --git a/ghost/admin/app/components/gh-post-settings-menu.js b/ghost/admin/app/components/gh-post-settings-menu.js index 98ba07c667..072f0a0ec8 100644 --- a/ghost/admin/app/components/gh-post-settings-menu.js +++ b/ghost/admin/app/components/gh-post-settings-menu.js @@ -40,7 +40,9 @@ export default Component.extend(SettingsMenuMixin, { this._super(...arguments); this.get('store').query('user', {limit: 'all'}).then((users) => { - this.set('authors', users.sortBy('name')); + if (!this.get('isDestroyed')) { + this.set('authors', users.sortBy('name')); + } }); this.get('model.author').then((author) => { diff --git a/ghost/admin/app/components/gh-textarea.js b/ghost/admin/app/components/gh-textarea.js index c1e46ce33d..f572ec0a5e 100644 --- a/ghost/admin/app/components/gh-textarea.js +++ b/ghost/admin/app/components/gh-textarea.js @@ -48,8 +48,10 @@ export default OneWayTextarea.extend(TextInputMixin, { // collapse the element first so that we can shrink as well as expand // then set the height to match the text height - el.style.height = 0; - el.style.height = `${el.scrollHeight}px`; + if (el) { + el.style.height = 0; + el.style.height = `${el.scrollHeight}px`; + } }, _setupAutoExpand() { diff --git a/ghost/admin/app/controllers/editor/new.js b/ghost/admin/app/controllers/editor/new.js index aad4758223..6fbc8d2edd 100644 --- a/ghost/admin/app/controllers/editor/new.js +++ b/ghost/admin/app/controllers/editor/new.js @@ -1,23 +1,6 @@ import Controller from 'ember-controller'; import EditorControllerMixin from 'ghost-admin/mixins/editor-base-controller'; -function K() { - return this; -} - export default Controller.extend(EditorControllerMixin, { - // Overriding autoSave on the base controller, as the new controller shouldn't be autosaving - autoSave: K, - actions: { - /** - * Redirect to editor after the first save - */ - save(options) { - return this._super(options).then((model) => { - if (model.get('id')) { - this.replaceRoute('editor.edit', model); - } - }); - } - } + }); diff --git a/ghost/admin/app/mixins/editor-base-controller.js b/ghost/admin/app/mixins/editor-base-controller.js index aa5e11e45a..dab29644b3 100644 --- a/ghost/admin/app/mixins/editor-base-controller.js +++ b/ghost/admin/app/mixins/editor-base-controller.js @@ -3,7 +3,7 @@ import Mixin from 'ember-metal/mixin'; import PostModel from 'ghost-admin/models/post'; import RSVP from 'rsvp'; import boundOneWay from 'ghost-admin/utils/bound-one-way'; -import computed, {alias, mapBy, reads} from 'ember-computed'; +import computed, {mapBy, reads} from 'ember-computed'; import ghostPaths from 'ghost-admin/utils/ghost-paths'; import injectController from 'ember-controller/inject'; import injectService from 'ember-service/inject'; @@ -28,6 +28,11 @@ const watchedProps = ['model.scratch', 'model.titleScratch', 'model.hasDirtyAttr const DEFAULT_TITLE = '(Untitled)'; const TITLE_DEBOUNCE = testing ? 10 : 700; +// time in ms to save after last content edit +const AUTOSAVE_TIMEOUT = 3000; +// time in ms to force a save if the user is continuously typing +const TIMEDSAVE_TIMEOUT = 60000; + PostModel.eachAttribute(function (name) { watchedProps.push(`model.${name}`); }); @@ -52,9 +57,6 @@ export default Mixin.create({ editor: null, editorMenuIsOpen: false, - shouldFocusTitle: alias('model.isNew'), - shouldFocusEditor: false, - navIsClosed: reads('application.autoNav'), init() { @@ -65,12 +67,12 @@ export default Mixin.create({ }, _canAutosave: computed('model.{isDraft,isNew}', function () { - return !testing && this.get('model.isDraft') && !this.get('model.isNew'); + return !testing && this.get('model.isDraft'); }), // save 3 seconds after the last edit _autosave: task(function* () { - yield timeout(3000); + yield timeout(AUTOSAVE_TIMEOUT); if (this.get('_canAutosave')) { yield this.get('autosave').perform(); @@ -81,7 +83,7 @@ export default Mixin.create({ _timedSave: task(function* () { // eslint-disable-next-line no-constant-condition while (!testing && true) { - yield timeout(60000); + yield timeout(TIMEDSAVE_TIMEOUT); if (this.get('_canAutosave')) { yield this.get('autosave').perform(); @@ -170,6 +172,12 @@ export default Mixin.create({ this.get('model').set('statusScratch', null); + // redirect to edit route if saving a new record + if (isNew && model.get('id')) { + this.replaceRoute('editor.edit', model); + return; + } + return model; }); @@ -570,6 +578,12 @@ export default Mixin.create({ }, toggleLeaveEditorModal(transition) { + // cancel autosave when showing the modal to prevent the "leave" + // action failing due to deletion of in-flight records + if (!this.get('showLeaveEditorModal')) { + this.send('cancelAutosave'); + } + this.set('leaveEditorTransition', transition); this.toggleProperty('showLeaveEditorModal'); }, diff --git a/ghost/admin/app/mixins/editor-base-route.js b/ghost/admin/app/mixins/editor-base-route.js index 4cd92ae5ed..29981bb5fc 100644 --- a/ghost/admin/app/mixins/editor-base-route.js +++ b/ghost/admin/app/mixins/editor-base-route.js @@ -50,9 +50,10 @@ export default Mixin.create(styleBody, ShortcutsRoute, { // so we abort the transition and retry after the save has completed. if (state.isSaving) { transition.abort(); - controller.get('generateSlug.last').then(() => { + controller.get('saveTasks.last').then(() => { transition.retry(); }); + return; } fromNewToEdit = this.get('routeName') === 'editor.new' diff --git a/ghost/admin/app/routes/editor/edit.js b/ghost/admin/app/routes/editor/edit.js index dc4b88b485..91f00c0bf6 100644 --- a/ghost/admin/app/routes/editor/edit.js +++ b/ghost/admin/app/routes/editor/edit.js @@ -5,12 +5,6 @@ import base from 'ghost-admin/mixins/editor-base-route'; export default AuthenticatedRoute.extend(base, { titleToken: 'Editor', - beforeModel(transition) { - this.set('_transitionedFromNew', transition.data.fromNew); - - this._super(...arguments); - }, - model(params) { /* eslint-disable camelcase */ let query = { @@ -42,11 +36,6 @@ export default AuthenticatedRoute.extend(base, { }); }, - setupController(controller) { - this._super(...arguments); - controller.set('shouldFocusEditor', this.get('_transitionedFromNew')); - }, - actions: { authorizationFailed() { this.get('controller').send('toggleReAuthenticateModal'); diff --git a/ghost/admin/app/routes/editor/new.js b/ghost/admin/app/routes/editor/new.js index b13e5699b6..901139dfdd 100644 --- a/ghost/admin/app/routes/editor/new.js +++ b/ghost/admin/app/routes/editor/new.js @@ -17,15 +17,5 @@ export default AuthenticatedRoute.extend(base, { controller, model }); - }, - - actions: { - willTransition(transition) { - // decorate the transition object so the editor.edit route - // knows this was the previous active route - transition.data.fromNew = true; - - this._super(...arguments); - } } }); diff --git a/ghost/admin/app/templates/editor/edit.hbs b/ghost/admin/app/templates/editor/edit.hbs index 9197251e12..0c4c930863 100644 --- a/ghost/admin/app/templates/editor/edit.hbs +++ b/ghost/admin/app/templates/editor/edit.hbs @@ -35,8 +35,8 @@ --}} {{#gh-markdown-editor tabindex="2" - placeholder="Click here to start..." - autofocus=shouldFocusEditor + placeholder="Now begin writing your story..." + autofocus=true uploadedImageUrls=editor.uploadedImageUrls mobiledoc=(readonly model.scratch) isFullScreen=editor.isFullScreen @@ -54,7 +54,6 @@ class="gh-editor-title" placeholder="Your Post Title" tabindex="1" - shouldFocus=shouldFocusTitle autoExpand=".gh-markdown-editor-pane" focusOut=(action "saveTitle") update=(action (perform updateTitle))