Ghost/ghost/admin/app/components/modal-post-preview/social.js
Kevin Ansfield ff09a20ac8 Made social/meta text and images in preview modal editable (#1850)
refs https://github.com/TryGhost/Team/issues/482

- replace titles and descriptions with text fields when clicked
    - save on blur
    - blur+save on <kbd>Enter</kbd>
    - blur+restore on <kbd>Escape</kbd>
    - create newline with <kbd>Shift+Enter</kbd> in description fields
- if there's no available image or fallback show a "+ Add image" header in the previews when hovering
- if there is an image show an upload/change image button when hovering
- show a delete image button when hovering once a custom image has been uploaded

Co-authored-by: Sanne de Vries <sannedv@protonmail.com>
2021-02-23 18:37:12 +00:00

216 lines
5.4 KiB
JavaScript

import Component from '@glimmer/component';
import {
IMAGE_EXTENSIONS,
IMAGE_MIME_TYPES
} from 'ghost-admin/components/gh-image-uploader';
import {action} from '@ember/object';
import {inject as service} from '@ember/service';
import {tracked} from '@glimmer/tracking';
export default class ModalPostPreviewSocialComponent extends Component {
@service config;
@service settings;
@service ghostPaths;
@tracked editingFacebookTitle = false;
@tracked editingFacebookDescription = false;
@tracked editingTwitterTitle = false;
@tracked editingTwitterDescription = false;
@tracked editingMetaTitle = false;
@tracked editingMetaDescription = false;
imageExtensions = IMAGE_EXTENSIONS;
imageMimeTypes = IMAGE_MIME_TYPES;
get _fallbackDescription() {
return this.args.post.customExcerpt ||
this.serpDescription ||
this.settings.get('description');
}
@action
blurElement(event) {
if (!event.shiftKey) {
event.preventDefault();
event.target.blur();
}
}
@action
triggerFileDialog(name) {
const input = document.querySelector(`#${name}FileInput input`);
if (input) {
input.click();
}
}
// SERP
get serpTitle() {
return this.args.post.metaTitle || this.args.post.title || '(Untitled)';
}
get serpURL() {
const blogUrl = this.config.get('blogUrl');
const seoSlug = this.args.post.slug || '';
const canonicalUrl = this.args.post.canonicalUrl || '';
if (canonicalUrl) {
if (canonicalUrl.match(/^\//)) {
return `${blogUrl}${canonicalUrl}`;
} else {
return canonicalUrl;
}
} else {
const seoURL = `${blogUrl}/${seoSlug}`;
// only append a slash to the URL if the slug exists
if (seoSlug) {
return `${seoURL}/`;
}
return seoURL;
}
}
get serpDescription() {
return this.args.post.metaDescription || this.args.post.excerpt;
}
@action
editMetaTitle() {
this.editingMetaTitle = true;
}
@action
setMetaTitle(event) {
const title = event.target.value;
this.args.post.metaTitle = title.trim();
this.args.post.save();
this.editingMetaTitle = false;
}
@action
editMetaDescription() {
this.editingMetaDescription = true;
}
@action
setMetaDescription(event) {
const description = event.target.value;
this.args.post.metaDescription = description.trim();
this.args.post.save();
this.editingMetaDescription = false;
}
// Facebook
get facebookTitle() {
return this.args.post.ogTitle || this.serpTitle;
}
get facebookDescription() {
return this.args.post.ogDescription || this._fallbackDescription;
}
get facebookImage() {
return this.args.post.ogImage || this.args.post.featureImage || this.settings.get('ogImage') || this.settings.get('coverImage');
}
@action
editFacebookTitle() {
this.editingFacebookTitle = true;
}
@action
cancelEdit(property, event) {
event.preventDefault();
event.target.value = this.args.post[property];
event.target.blur();
}
@action
setFacebookTitle(event) {
const title = event.target.value;
this.args.post.ogTitle = title.trim();
this.args.post.save();
this.editingFacebookTitle = false;
}
@action
editFacebookDescription() {
this.editingFacebookDescription = true;
}
@action
setFacebookDescription() {
const description = event.target.value;
this.args.post.ogDescription = description.trim();
this.args.post.save();
this.editingFacebookDescription = false;
}
@action
setFacebookImage([image]) {
this.args.post.ogImage = image.url;
this.args.post.save();
}
@action
clearFacebookImage() {
this.args.post.ogImage = null;
this.args.post.save();
}
// Twitter
get twitterTitle() {
return this.args.post.twitterTitle || this.serpTitle;
}
get twitterDescription() {
return this.args.post.twitterDescription || this._fallbackDescription;
}
get twitterImage() {
return this.args.post.twitterImage || this.args.post.featureImage || this.settings.get('twitterImage') || this.settings.get('coverImage');
}
@action
editTwitterTitle() {
this.editingTwitterTitle = true;
}
@action
setTwitterTitle(event) {
const title = event.target.value;
this.args.post.twitterTitle = title.trim();
this.args.post.save();
this.editingTwitterTitle = false;
}
@action
editTwitterDescription() {
this.editingTwitterDescription = true;
}
@action
setTwitterDescription() {
const description = event.target.value;
this.args.post.twitterDescription = description.trim();
this.args.post.save();
this.editingTwitterDescription = false;
}
@action
setTwitterImage([image]) {
this.args.post.twitterImage = image.url;
this.args.post.save();
}
@action
clearTwitterImage() {
this.args.post.twitterImage = null;
this.args.post.save();
}
}