Skip to content
This repository has been archived by the owner on Nov 28, 2022. It is now read-only.

Commit

Permalink
Made Koenig the default editor, removed Koenig labs flag
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinansfield committed Aug 14, 2018
1 parent 05eb7db commit 11e301b
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 270 deletions.
35 changes: 0 additions & 35 deletions app/controllers/editor.js
Expand Up @@ -3,7 +3,6 @@ import PostModel from 'ghost-admin/models/post';
import boundOneWay from 'ghost-admin/utils/bound-one-way';
import config from 'ghost-admin/config/environment';
import isNumber from 'ghost-admin/utils/isNumber';
import {BLANK_MARKDOWN} from 'ghost-admin/models/post';
import {alias, mapBy, reads} from '@ember/object/computed';
import {computed} from '@ember/object';
import {inject as controller} from '@ember/controller';
Expand Down Expand Up @@ -100,7 +99,6 @@ export default Controller.extend({
showDeletePostModal: false,
showLeaveEditorModal: false,
showReAuthenticateModal: false,
useKoenig: false,

// koenig related properties
wordcount: null,
Expand Down Expand Up @@ -131,14 +129,6 @@ export default Controller.extend({

_tagNames: mapBy('post.tags', 'name'),

markdown: computed('post.mobiledoc', function () {
if (this.get('post').isCompatibleWithMarkdownEditor()) {
let mobiledoc = this.get('post.mobiledoc');
let markdown = mobiledoc.cards[0][1].markdown;
return markdown;
}
}),

hasDirtyAttributes: computed(...watchedProps, {
get() {
return this._hasDirtyAttributes();
Expand Down Expand Up @@ -170,15 +160,6 @@ export default Controller.extend({
// force save at 60 seconds
this.get('_timedSave').perform();
},

// TODO: Only used by the markdown editor, ensure it's removed when
// we switch to Koenig
updateMarkdown(markdown) {
let mobiledoc = copy(BLANK_MARKDOWN, true);
mobiledoc.cards[0][1].markdown = markdown;
this.send('updateScratch', mobiledoc);
},

updateTitleScratch(title) {
this.set('post.titleScratch', title);
},
Expand Down Expand Up @@ -526,16 +507,6 @@ export default Controller.extend({

// called by the new/edit routes to change the post model
setPost(post) {
// switch between markdown/koenig depending on feature flag and post
// compatibility
let koenigEnabled = this.get('feature.koenigEditor');
let postIsMarkdownCompatible = post.isCompatibleWithMarkdownEditor();
if (koenigEnabled || !postIsMarkdownCompatible) {
this.set('useKoenig', true);
} else {
this.set('useKoenig', false);
}

// don't do anything else if we're setting the same post
if (post === this.get('post')) {
// set autofocus as change signal to the persistent editor on new->edit
Expand All @@ -548,12 +519,6 @@ export default Controller.extend({

this.set('post', post);

// display an info message if Koenig is disabled by we had to use it
// for post compatibility
if (!koenigEnabled && this.useKoenig) {
// this.set('infoMessage', 'This post can only be edited with the Koenig editor.');
}

// autofocus the editor if we have a new post
this.set('shouldFocusEditor', post.get('isNew'));

Expand Down
61 changes: 0 additions & 61 deletions app/models/post.js
Expand Up @@ -4,33 +4,16 @@ import ValidationEngine from 'ghost-admin/mixins/validation-engine';
import attr from 'ember-data/attr';
import boundOneWay from 'ghost-admin/utils/bound-one-way';
import moment from 'moment';
import {BLANK_DOC as BLANK_MOBILEDOC} from 'koenig-editor/components/koenig-editor';
import {belongsTo, hasMany} from 'ember-data/relationships';
import {compare} from '@ember/utils';
import {computed, observer} from '@ember/object';
import {copy} from '@ember/object/internals';
import {equal, filterBy} from '@ember/object/computed';
import {isBlank} from '@ember/utils';
import {inject as service} from '@ember/service';

// ember-cli-shims doesn't export these so we must get them manually
const {Comparable} = Ember;

// for our markdown-only editor we need to build a blank mobiledoc
const MOBILEDOC_VERSION = '0.3.1';
export const BLANK_MARKDOWN = {
version: MOBILEDOC_VERSION,
markups: [],
atoms: [],
cards: [
['card-markdown', {
cardName: 'card-markdown',
markdown: ''
}]
],
sections: [[10, 0]]
};

function statusCompare(postA, postB) {
let status1 = postA.get('status');
let status2 = postB.get('status');
Expand Down Expand Up @@ -135,30 +118,6 @@ export default Model.extend(Comparable, ValidationEngine, {
return this.get('authors.firstObject');
}),

init() {
// HACK: we can't use the defaultValue property on the mobiledoc attr
// because it won't have access to `this` for the feature check so we do
// it manually here instead
if (!this.get('mobiledoc')) {
let defaultValue;

if (this.get('feature.koenigEditor')) {
defaultValue = copy(BLANK_MOBILEDOC, true);
} else {
defaultValue = copy(BLANK_MARKDOWN, true);
}

// using this.set() adds the property to the changedAttributes list
// which means the editor always sees new posts as dirty. By setting
// the internal model data property first it's not seen as having
// changed so the changedAttributes key is removed
this._internalModel._data.mobiledoc = defaultValue;
this.set('mobiledoc', defaultValue);
}

this._super(...arguments);
},

scratch: null,
titleScratch: null,

Expand Down Expand Up @@ -360,25 +319,5 @@ export default Model.extend(Comparable, ValidationEngine, {
let publishedAtBlogTZ = this.get('publishedAtBlogTZ');
let publishedAtUTC = publishedAtBlogTZ ? publishedAtBlogTZ.utc() : null;
this.set('publishedAtUTC', publishedAtUTC);
},

// the markdown editor expects a very specific mobiledoc format, if it
// doesn't match then we'll need to handle it by forcing Koenig
isCompatibleWithMarkdownEditor() {
let mobiledoc = this.get('mobiledoc');

if (mobiledoc
&& mobiledoc.markups.length === 0
&& mobiledoc.cards.length === 1
&& mobiledoc.cards[0][0] === 'card-markdown'
&& mobiledoc.sections.length === 1
&& mobiledoc.sections[0].length === 2
&& mobiledoc.sections[0][0] === 10
&& mobiledoc.sections[0][1] === 0
) {
return true;
}

return false;
}
});
41 changes: 14 additions & 27 deletions app/routes/editor.js
Expand Up @@ -21,39 +21,26 @@ export default AuthenticatedRoute.extend(ShortcutsRoute, {

activate() {
this._super(...arguments);
if (this.feature.koenigEditor) {
this.ui.set('isFullScreen', true);
}
this.ui.set('isFullScreen', true);
},

setupController() {
this._super(...arguments);

// display a warning if we detect an unsupported browser
if (this.feature.koenigEditor) {
// IE is definitely not supported and will not work at all in Ghost 2.0
if (this.userAgent.browser.isIE) {
this.notifications.showAlert(
htmlSafe('Internet Explorer is not supported in Koenig and will no longer work in Ghost 2.0. Please switch to <a href="https://ghost.org/downloads/" target="_blank" rel="noopener">Ghost Desktop</a> or a recent version of Chrome/Firefox/Safari.'),
{type: 'info', key: 'koenig.browserSupport'}
);
}

// edge has known issues
if (this.userAgent.browser.isEdge) {
this.notifications.showAlert(
htmlSafe('Microsoft Edge is not currently supported in Koenig. Please switch to <a href="https://ghost.org/downloads/" target="_blank" rel="noopener">Ghost Desktop</a> or a recent version of Chrome/Firefox/Safari.'),
{type: 'info', key: 'koenig.browserSupport'}
);
}
// edge has known issues
if (this.userAgent.browser.isEdge) {
this.notifications.showAlert(
htmlSafe('Microsoft Edge is not currently supported in Koenig. Please switch to <a href="https://ghost.org/downloads/" target="_blank" rel="noopener">Ghost Desktop</a> or a recent version of Chrome/Firefox/Safari.'),
{type: 'info', key: 'koenig.browserSupport'}
);
}

// mobile browsers are not currently supported
if (this.userAgent.device.isMobile || this.userAgent.device.isTablet) {
this.notifications.showAlert(
htmlSafe('Mobile editing is not currently supported in Koenig. Please use a desktop browser or <a href="https://ghost.org/downloads/" target="_blank" rel="noopener">Ghost Desktop</a>.'),
{type: 'info', key: 'koenig.browserSupport'}
);
}
// mobile browsers are not currently supported
if (this.userAgent.device.isMobile || this.userAgent.device.isTablet) {
this.notifications.showAlert(
htmlSafe('Mobile editing is not currently supported in Koenig. Please use a desktop browser or <a href="https://ghost.org/downloads/" target="_blank" rel="noopener">Ghost Desktop</a>.'),
{type: 'info', key: 'koenig.browserSupport'}
);
}
},

Expand Down
1 change: 0 additions & 1 deletion app/services/feature.js
Expand Up @@ -50,7 +50,6 @@ export default Service.extend({
notifications: service(),
lazyLoader: service(),

koenigEditor: feature('koenigEditor'),
publicAPI: feature('publicAPI'),
subscribers: feature('subscribers'),
nightShift: feature('nightShift', {user: true, onChange: '_setAdminTheme'}),
Expand Down
1 change: 1 addition & 0 deletions app/templates/components/gh-koenig-editor.hbs
Expand Up @@ -14,6 +14,7 @@
focus-out=(action "onTitleFocusOut")
keyDown=(action "onTitleKeydown")
didCreateTextarea=(action "onTitleCreated")
data-test-editor-title-input=true
}}

{{koenig-editor
Expand Down
151 changes: 25 additions & 126 deletions app/templates/editor.hbs
Expand Up @@ -66,134 +66,33 @@
</section>
</header>

{{#if useKoenig}}
{{!--
gh-koenig-editor acts as a wrapper around the title input and
koenig editor canvas to support Ghost-specific editor behaviour
--}}
{{gh-koenig-editor
title=(readonly post.titleScratch)
titlePlaceholder="Story Title"
onTitleChange=(action "updateTitleScratch")
onTitleBlur=(action (perform saveTitle))
body=(readonly post.scratch)
bodyPlaceholder="Begin writing your story..."
bodyAutofocus=shouldFocusEditor
onBodyChange=(action "updateScratch")
headerOffset=editor.headerHeight
scrollContainerSelector=".gh-koenig-editor"
scrollOffsetTopSelector=".gh-editor-header-small"
scrollOffsetBottomSelector=".gh-mobile-nav-bar"
onEditorCreated=(action "setKoenigEditor")
onWordCountChange=(action "updateWordCount")
}}
{{!--
gh-koenig-editor acts as a wrapper around the title input and
koenig editor canvas to support Ghost-specific editor behaviour
--}}
{{gh-koenig-editor
title=(readonly post.titleScratch)
titlePlaceholder="Story Title"
onTitleChange=(action "updateTitleScratch")
onTitleBlur=(action (perform saveTitle))
body=(readonly post.scratch)
bodyPlaceholder="Begin writing your story..."
bodyAutofocus=shouldFocusEditor
onBodyChange=(action "updateScratch")
headerOffset=editor.headerHeight
scrollContainerSelector=".gh-koenig-editor"
scrollOffsetTopSelector=".gh-editor-header-small"
scrollOffsetBottomSelector=".gh-mobile-nav-bar"
onEditorCreated=(action "setKoenigEditor")
onWordCountChange=(action "updateWordCount")
}}

<div class="absolute flex items-center br3 bg-white {{if editor.headerClass "right-4 bottom-4" "right-6 bottom-6"}}">
<div class="midgrey-l2 {{if editor.headerClass "f-supersmall pl2 pr2" "f8 pl4 pr3"}} fw3">
{{pluralize wordCount.wordCount "word"}}
</div>
<a href="https://help.ghost.org/article/29-ghost-editor-overview" class="flex {{if editor.headerClass "pa2" "pa3"}}" target="_blank">{{svg-jar "help" class="w4 h4 stroke-midgrey-l2"}}</a>
<div class="absolute flex items-center br3 bg-white {{if editor.headerClass "right-4 bottom-4" "right-6 bottom-6"}}">
<div class="midgrey-l2 {{if editor.headerClass "f-supersmall pl2 pr2" "f8 pl4 pr3"}} fw3">
{{pluralize wordCount.wordCount "word"}}
</div>

{{else}}

{{!--
NOTE: title is part of the markdown editor container so that it has
access to the markdown editor's "focus" action
--}}
{{#gh-markdown-editor
tabindex="2"
placeholder="Begin writing your story..."
autofocus=shouldFocusEditor
uploadedImageUrls=editor.uploadedImageUrls
markdown=(readonly markdown)
isFullScreen=editor.isFullScreen
onChange=(action "updateMarkdown")
onFullScreenToggle=(action editor.toggleFullScreen)
onPreviewToggle=(action editor.togglePreview)
onSplitScreenToggle=(action editor.toggleSplitScreen)
onImageFilesSelected=(action editor.uploadImages)
imageMimeTypes=editor.imageMimeTypes
as |markdown|
}}
<div class="gh-markdown-editor-pane">
{{gh-textarea
class="gh-editor-title"
placeholder="Post Title"
tabindex="1"
autoExpand=".gh-markdown-editor-pane"
value=(readonly post.titleScratch)
input=(action "updateTitleScratch" value="target.value")
focus-out=(action (perform saveTitle))
keyEvents=(hash
Tab=(action markdown.focus 'bottom')
Enter=(action markdown.focus 'top')
)
data-test-editor-title-input=true
}}
{{markdown.editor}}
</div>

{{#if markdown.isSplitScreen}}
<div class="gh-markdown-editor-preview">
<h1 class="gh-markdown-editor-preview-title">{{post.titleScratch}}</h1>
<div class="gh-markdown-editor-preview-content"></div>
</div>
{{/if}}

{{gh-tour-item "using-the-editor"
target=".gh-editor-footer"
throbberAttachment="top left"
throbberOffset="0 20%"
popoverTriangleClass="bottom"
}}
{{/gh-markdown-editor}}

{{!-- TODO: put tool/status bar in here so that scroll area can be fixed --}}
<footer class="gh-editor-footer"></footer>

{{!-- files are dragged over editor pane --}}
{{#if editor.isDraggedOver}}
<div class="drop-target gh-editor-drop-target">
<div class="drop-target-message">
<h3>Drop image(s) here to upload</h3>
</div>
</div>
{{/if}}

{{!-- files have been dropped ready to be uploaded --}}
{{#if editor.droppedFiles}}
{{#gh-uploader
files=editor.droppedFiles
accept=editor.imageMimeTypes
extensions=editor.imageExtensions
onComplete=(action editor.uploadComplete)
onCancel=(action editor.uploadCancelled)
as |upload|
}}
<div class="gh-editor-image-upload {{if upload.errors "-error"}}">
<div class="gh-editor-image-upload-content">
{{#if upload.errors}}
<h3>Upload failed</h3>

{{#each upload.errors as |error|}}
<div class="failed">{{error.fileName}} - {{error.message}}</div>
{{/each}}

<button class="gh-btn gh-btn-grey gh-btn-icon" {{action upload.cancel}}>
<span>{{svg-jar "close"}} Close</span>
</button>
{{else}}

<h3>Uploading {{pluralize upload.files.length "image"}}...</h3>
{{upload.progressBar}}
{{/if}}
</div>
</div>
{{/gh-uploader}}
{{/if}}

{{/if}} {{!-- end Koenig conditional --}}
<a href="https://help.ghost.org/article/29-ghost-editor-overview" class="flex {{if editor.headerClass "pa2" "pa3"}}" target="_blank">{{svg-jar "help" class="w4 h4 stroke-midgrey-l2"}}</a>
</div>

{{/gh-editor}}

Expand Down

0 comments on commit 11e301b

Please sign in to comment.