0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-24 23:48:13 -05:00

Updated audio card with basic upload behavior

refs https://github.com/TryGhost/Team/issues/1230

- adds thumbnail upload on audio card
- cleans up card design in edit mode
This commit is contained in:
Rishabh 2021-12-01 14:34:32 +05:30
parent 1b4c556837
commit e0eff4436c
2 changed files with 200 additions and 71 deletions

View file

@ -24,84 +24,147 @@
>
<div style="display:flex">
<div style="width: 150px;">
<GhUploader
@uploadUrl="/media/upload/"
@resourceName="media"
@files={{this.files}}
@accept={{this.audioMimeTypes}}
@extensions={{this.audioExtensions}}
@onStart={{this.audioUploadStarted}}
@onComplete={{this.audioUploadCompleted}}
@onFailed={{this.audioUploadFailed}}
as |uploader|
>
<div class="relative{{unless (or this.previewThumbnailSrc @payload.thumbnailSrc) " bg-whitegrey-l2"}}">
{{#if (or this.previewThumbnailSrc @payload.thumbnailSrc)}}
<img src={{or this.previewThumbnailSrc @payload.thumbnailSrc}} class="{{kg-style this.kgImgStyle}}">
{{#if this.isDraggedOver}}
<div class="absolute absolute--fill flex items-center bg-black-60 pe-none">
<span class="db center sans-serif fw7 f7 white">
Drop to replace audio
</span>
{{#if (not @payload.src)}}
<GhUploader
@uploadUrl="/media/upload/"
@resourceName="media"
@files={{this.files}}
@accept={{this.audioMimeTypes}}
@extensions={{this.audioExtensions}}
@onStart={{this.audioUploadStarted}}
@onComplete={{this.audioUploadCompleted}}
@onFailed={{this.audioUploadFailed}}
as |uploader|
>
<div class="relative{{unless @payload.src " bg-whitegrey-l2"}}">
{{#if (or uploader.errors uploader.isUploading)}}
<div class="relative miw-100 flex items-center {{if (not this.previewThumbnailSrc @payload.src) "kg-media-placeholder ba b--whitegrey" "absolute absolute--fill bg-white-50"}}">
{{#if uploader.errors}}
<span class="db absolute top-0 right-0 left-0 flex items-center h8 pl2 pr2 bg-red white sans-serif f7">
{{uploader.errors.firstObject.message}}
</span>
{{/if}}
{{#if this.isDraggedOver}}
<span class="db center sans-serif fw7 f7 middarkgrey">
Drop it like it's hot 🔥
</span>
{{else if uploader.isUploading}}
{{uploader.progressBar}}
{{/if}}
</div>
{{else}}
<div class="relative miw-100 flex items-center kg-media-placeholder ba b--whitegrey">
{{#if this.isDraggedOver}}
<span class="db center sans-serif fw7 f7 middarkgrey">
Drop it like it's hot 🔥
</span>
{{else if uploader.isUploading}}
{{uploader.progressBar}}
{{else}}
<button class="flex flex-column items-center center sans-serif fw4 f7 middarkgrey kg-image-button" {{on "click" this.triggerAudioFileDialog}}>
{{svg-jar this.placeholder class="kg-placeholder-image" style="transform:scale(0.5)"}}
</button>
{{/if}}
</div>
{{/if}}
{{/if}}
</div>
{{#if (or uploader.errors uploader.isUploading (not @payload.src))}}
<div class="relative miw-100 flex items-center {{if (not this.previewThumbnailSrc @payload.src) "kg-media-placeholder ba b--whitegrey" "absolute absolute--fill bg-white-50"}}">
{{#if uploader.errors}}
<span class="db absolute top-0 right-0 left-0 flex items-center h8 pl2 pr2 bg-red white sans-serif f7">
{{uploader.errors.firstObject.message}}
</span>
{{/if}}
<div style="display:none">
<GhFileInput @multiple={{false}} @action={{uploader.setFiles}} @accept={{this.audioMimeTypes}} @onInsert={{this.registerAudioFileInput}} />
</div>
</GhUploader>
{{#if this.isDraggedOver}}
<span class="db center sans-serif fw7 f7 middarkgrey">
Drop it like it's hot 🔥
</span>
{{else if uploader.isUploading}}
{{uploader.progressBar}}
{{else if (not this.previewThumbnailSrc @payload.src)}}
<button class="flex flex-column items-center center sans-serif fw4 f7 middarkgrey kg-image-button" {{on "click" this.triggerAudioFileDialog}}>
{{svg-jar this.placeholder class="kg-placeholder-image" style="transform:scale(0.5)"}}
{{!-- <span class="mt2 midgrey">Click to select an audio</span> --}}
</button>
{{/if}}
</div>
{{else}}
<div class="relative miw-100 flex items-center {{if (not this.previewThumbnailSrc @payload.src) "kg-media-placeholder ba b--whitegrey" "absolute absolute--fill bg-white-50"}}">
{{#if this.isDraggedOver}}
<span class="db center sans-serif fw7 f7 middarkgrey">
Drop it like it's hot 🔥
</span>
{{else if uploader.isUploading}}
{{uploader.progressBar}}
{{else if (not this.previewThumbnailSrc @payload.src)}}
<button class="flex flex-column items-center center sans-serif fw4 f7 middarkgrey kg-image-button" {{on "click" this.triggerAudioFileDialog}}>
{{svg-jar this.placeholder class="kg-placeholder-image" style="transform:scale(0.5)"}}
{{!-- <span class="mt2 midgrey">Click to select an audio</span> --}}
</button>
{{/if}}
</div>
{{/if}}
</div>
{{else}}
<GhUploader
@uploadUrl="/media/thumbnail/upload/"
@resourceName="media"
@requestMethod="put"
@accept={{this.imageMimeTypes}}
@extensions={{this.imageExtensions}}
@onStart={{this.audioThumbnailUploadStarted}}
@onComplete={{this.audioThumbnailUploadCompleted}}
@onFailed={{this.audioThumbnailUploadFailed}}
@paramsHash={{hash url=@payload.src}}
as |uploader|
>
<div class="relative{{unless @payload.thumbnailSrc " bg-whitegrey-l2"}}">
<div style="display:none">
<GhFileInput @multiple={{false}} @action={{uploader.setFiles}} @accept={{this.audioMimeTypes}} @onInsert={{this.registerAudioFileInput}} />
</div>
</GhUploader>
{{#if (or uploader.errors uploader.isUploading)}}
<div class="relative miw-100 flex items-center {{if (not @payload.thumbnailSrc) "kg-media-placeholder ba b--whitegrey" "absolute absolute--fill bg-white-50"}}">
{{#if uploader.errors}}
<span class="db absolute top-0 right-0 left-0 flex items-center h8 pl2 pr2 bg-red white sans-serif f7">
{{uploader.errors.firstObject.message}}
</span>
{{/if}}
{{#if this.isDraggedOver}}
<span class="db center sans-serif fw7 f7 middarkgrey">
Drop it like it's hot 🔥
</span>
{{else if uploader.isUploading}}
{{uploader.progressBar}}
{{else if (not @payload.thumbnailSrc)}}
<button class="flex flex-column items-center center sans-serif fw4 f7 middarkgrey kg-image-button" {{on "click" this.triggerThumbnailFileDialog}}>
{{svg-jar this.placeholder class="kg-placeholder-image" style="transform:scale(0.5)"}}
{{!-- <span class="mt2 midgrey">Click to select an audio</span> --}}
</button>
{{else}}
<button class="flex flex-column items-center center sans-serif fw4 f7 middarkgrey kg-image-button" {{on "click" this.triggerThumbnailFileDialog}}>
<img src={{@payload.thumbnailSrc}} class="{{kg-style this.kgImgStyle}}">
{{!-- <span class="mt2 midgrey">Click to select an audio</span> --}}
</button>
{{#if this.isDraggedOver}}
<div class="absolute absolute--fill flex items-center bg-black-60 pe-none">
<span class="db center sans-serif fw7 f7 white">
Drop to replace thumbnail
</span>
</div>
{{/if}}
{{/if}}
</div>
{{else}}
<div class="relative miw-100 flex items-center kg-media-placeholder ba b--whitegrey">
{{#if this.isDraggedOver}}
<span class="db center sans-serif fw7 f7 middarkgrey">
Drop it like it's hot 🔥
</span>
{{else if uploader.isUploading}}
{{uploader.progressBar}}
{{else if @payload.thumbnailSrc}}
<button class="flex flex-column items-center center sans-serif fw4 f7 middarkgrey kg-image-button" {{on "click" this.triggerThumbnailFileDialog}}>
<img src={{@payload.thumbnailSrc}} style="width: 150px" />
</button>
{{else}}
<button class="flex flex-column items-center center sans-serif fw4 f7 middarkgrey kg-image-button" {{on "click" this.triggerThumbnailFileDialog}}>
{{svg-jar this.placeholder class="kg-placeholder-image" style="transform:scale(0.5)"}}
</button>
{{/if}}
</div>
{{/if}}
</div>
<div style="display:none">
<GhFileInput @multiple={{false}} @action={{uploader.setFiles}} @accept={{this.imageMimeTypes}} @onInsert={{this.registerAudioThumbnailFileInput}} />
</div>
</GhUploader>
{{/if}}
</div>
<div style="flex-grow: 1;display: flex;align-items: center;margin-left: 12px">
Click to upload an audio file
</div>
{{#if @payload.src}}
<div style="flex-grow: 1;display: flex;justify-content: center;margin-left: 12px;line-height:3rem">
<GhTextInput
@value={{@payload.fileName}}
@input={{action "setAudioTitle" value="target.value"}}
@class="w-100 fw4 bn bg-transparent bw0"
style="border-width: 0"
/>
</div>
{{else}}
<div style="flex-grow: 1;display: flex;align-items: center;margin-left: 12px;line-height:3rem">
Click to upload an audio file
</div>
{{/if}}
</div>
{{!-- {{#if (or @isSelected (clean-basic-html @payload.caption))}}
<card.CaptionInput
@caption={{@payload.caption}}
@update={{fn this.updatePayloadAttr "caption"}}
@placeholder="Type caption for audio (optional)" />
{{/if}} --}}
{{#if (and @isEditing @payload.src)}}
<KoenigSettingsPanel>

View file

@ -1,4 +1,8 @@
import Component from '@glimmer/component';
import {
IMAGE_EXTENSIONS,
IMAGE_MIME_TYPES
} from 'ghost-admin/components/gh-image-uploader';
import {TrackedObject} from 'tracked-built-ins';
import {action} from '@ember/object';
import {bind} from '@ember/runloop';
@ -8,7 +12,6 @@ import {inject as service} from '@ember/service';
import {set} from '@ember/object';
import {task} from 'ember-concurrency-decorators';
import {tracked} from '@glimmer/tracking';
export const AUDIO_EXTENSIONS = ['mp4', 'mp3', 'wav'];
export const AUDIO_MIME_TYPES = ['audio/mp4', 'audio/mpeg', 'audio/ogg'];
@ -37,6 +40,7 @@ export default class KoenigCardAudioComponent extends Component {
@service ghostPaths;
@tracked files;
@tracked thumbnailFiles;
@tracked isDraggedOver = false;
@tracked previewThumbnailSrc;
@ -46,6 +50,8 @@ export default class KoenigCardAudioComponent extends Component {
audioExtensions = AUDIO_EXTENSIONS;
audioMimeTypes = AUDIO_MIME_TYPES;
imageExtensions = IMAGE_EXTENSIONS;
imageMimeTypes = IMAGE_MIME_TYPES;
placeholder = PLACEHOLDERS[Math.floor(Math.random() * PLACEHOLDERS.length)]
payloadAudioAttrs = ['src', 'fileName', 'width', 'height', 'duration', 'mimeType', 'thumbnailSrc', 'thumbnailWidth', 'thumbnailHeight'];
@ -91,6 +97,11 @@ export default class KoenigCardAudioComponent extends Component {
});
}
_afterRender() {
// this._placeCursorAtEnd();
// this._focusInput();
}
@action
didInsert(element) {
// required for snippet rects to be calculated - editor reaches in to component,
@ -121,6 +132,11 @@ export default class KoenigCardAudioComponent extends Component {
this._audioFileInput = input;
}
@action
registerAudioThumbnailFileInput(input) {
this._audioThumbnailFileInput = input;
}
@action
triggerAudioFileDialog(event) {
if (this._audioFileInput) {
@ -137,11 +153,32 @@ export default class KoenigCardAudioComponent extends Component {
}
}
@action
triggerThumbnailFileDialog(event) {
if (this._audioThumbnailFileInput) {
return this._audioThumbnailFileInput.click();
}
const target = event?.target || this.element;
const cardElem = target.closest('.__mobiledoc-card');
const fileInput = cardElem?.querySelector('input[type="file"]');
if (fileInput) {
fileInput.click();
}
}
@action
async audioUploadStarted() {
// TODO: Placeholder for any processing on audio upload
}
@action
async audioThumbnailUploadStarted() {
// TODO: Placeholder for any processing on audio upload
}
@action
async audioUploadCompleted([audio]) {
this.previewPayload.src = audio.url;
@ -158,6 +195,22 @@ export default class KoenigCardAudioComponent extends Component {
this.previewPayload = new TrackedObject({});
}
@action
async audioThumbnailUploadCompleted([audio]) {
this.previewPayload.thumbnailSrc = audio.url;
// save preview payload attrs into actual payload and create undo snapshot
this.args.editor.run(() => {
this.updatePayloadAttr('thumbnailSrc', this.previewPayload.thumbnailSrc);
// this.payloadAudioAttrs.forEach((attr) => {
// this.updatePayloadAttr(attr, this.previewPayload[attr]);
// });
});
// reset preview so we're back to rendering saved data
this.previewPayload = new TrackedObject({});
}
@action
audioUploadFailed() {
// reset all attrs, creating an undo snapshot
@ -168,6 +221,19 @@ export default class KoenigCardAudioComponent extends Component {
});
}
@action
setAudioTitle(content) {
this.updatePayloadAttr('fileName', content);
}
@action
audioThumbnailUploadFailed() {
this.previewPayload.thumbnailSrc = null;
this.args.editor.run(() => {
this.updatePayloadAttr('thumbnailSrc', this.previewPayload.thumbnailSrc);
});
}
@task
*uploadThumbnailFromBlobTask(audioUrl, fileBlob) {
const formData = new FormData();