Skip to content

Instantly share code, notes, and snippets.

@timkelty
Last active June 2, 2021 12:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timkelty/30b4b15472a312bdad62d7f7a7db594e to your computer and use it in GitHub Desktop.
Save timkelty/30b4b15472a312bdad62d7f7a7db594e to your computer and use it in GitHub Desktop.
<?php
namespace modules\appmodule\controllers;
use Craft;
use craft\elements\Asset;
use craft\web\Controller;
use spicyweb\embeddedassets\Plugin as EmbeddedAssets;
use yii\web\Response;
class AssetsController extends Controller
{
/**
* Can't simply use Craft's assets/thumb because we don't have the UID.
* Also, we want to get the URL, so assets aren't generated and we get our cloud resized version.
*/
public function actionThumb(string $id, int $width, int $height): Response
{
$asset = Asset::find()->id($id)->one();
$embeddedAsset = EmbeddedAssets::$plugin->methods->getEmbeddedAsset($asset);
$size = max($width, $height);
$url = Craft::$app->getAssets()->getThumbUrl($asset, $width, $height);
if ($embeddedAsset) {
$image = $embeddedAsset->getImageToSize($size);
$url = $image['url'] ?? $url;
}
return $this->redirect($url);
}
}
import {hot} from 'react-hot-loader/root';
import React, {useState} from 'react';
import Uppy from '@uppy/core';
import XHRUpload from '@uppy/xhr-upload';
import {DragDrop, StatusBar, ProgressBar} from '@uppy/react';
import '@uppy/core/dist/style.css';
import '@uppy/status-bar/dist/style.css';
import '@uppy/progress-bar/dist/style.css';
import '@uppy/drag-drop/dist/style.css';
import {ReactSortable} from 'react-sortablejs';
import {axios, catchError} from '@js/axios.js';
import classNames from 'classnames';
import {createURL} from '@js/utils';
const getAssetThumbUrl = ({id, width = 50, height = width}) => {
return createURL(`actions/appmodule/assets/thumb?id=${id}&width=${width}&height=${height}`,);
};
const Row = ({
block,
index,
removeBlockByIndex,
blockId = `new${index + 1}`,
}) => {
const baseFieldName = 'fields[galleryMatrix]';
const baseBlockName = `${baseFieldName}[blocks][${blockId}]`;
const isEmbed = block.asset.embedUrl || false;
const icon = isEmbed ? 'play-circle' : 'image';
return (
<div className={classNames('uk-padding-small', index % 2 ? false : 'uk-background-muted')}>
<input type='hidden' name={`${baseFieldName}[sortOrder][]`} value={blockId} />
<input type='hidden' name={`${baseBlockName}[type]`} value='item' />
<input type='hidden' name={`${baseBlockName}[enabled]`} value='1' />
<input type='hidden' name={`${baseBlockName}[fields][asset][]`} value={block.asset.id} />
<div className='uk-grid uk-grid-small' data-uk-grid>
<div className='uk-width-auto'>
<div className='uk-text-center' style={{width: 50}}>
<img src={getAssetThumbUrl({id: block.asset.id})} alt=''/>
</div>
</div>
<div className='uk-width-expand'>
{!isEmbed && (
<input
type='text'
className='uk-input uk-form-small'
placeholder='Optional caption'
name={`${baseBlockName}[fields][caption]`}
defaultValue={block.caption}
/>
)}
<div className='uk-text-small uk-text-muted' style={{marginTop: '5px'}}>{isEmbed ? block.asset.embedUrl : block.asset.filename}</div>
</div>
<div className='uk-width-auto'>
<ul className='uk-iconnav uk-padding-remove'>
<li><button type='button' data-uk-icon='icon: trash' onClick={() => removeBlockByIndex(index)}></button></li>
<li><span data-uk-icon='icon: menu' className='uk-sortable-handle'></span></li>
</ul>
</div>
</div>
</div>
);
};
const UgcUpload = ({
folderId = '479',
folderUid = '30dc4f14-de1c-424b-bbb6-75988c1ee14a',
csrfTokenName,
csrfTokenValue,
initialBlocks = [],
}) => {
const [blocks, setBlocks] = useState(initialBlocks);
const [embedUrl, setEmbedUrl] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
const addBlock = (block) => {
return setBlocks((blocks) => [...blocks, {
id: `new${blocks.length + 1}`,
...block,
}]);
};
const addEmbed = () => {
setIsSubmitting(true);
axios.post('/actions/embeddedassets/actions/save', {
[csrfTokenName]: csrfTokenValue,
url: embedUrl,
// For whatever reason, embeddedassets wants the uid
folderId: folderUid,
})
.then(({data}) => {
const {payload} = data;
const {assetId} = payload;
const asset = {
id: assetId,
embedUrl,
};
// Clear URL for another submission
setEmbedUrl('');
return addBlock({asset});
})
.catch(catchError)
.then(() => setIsSubmitting(false));
};
const uppy = new Uppy({
meta: {
folderId,
[csrfTokenName]: csrfTokenValue,
},
allowMultipleUploads: true,
autoProceed: true,
});
uppy.use(XHRUpload, {
endpoint: '/actions/assets/upload',
formData: true,
fieldName: 'assets-upload',
headers: {
Accept: 'application/json; charset=utf-8',
},
});
uppy.on('upload-error', (file, error, response) => {
// TODO: improve error handling, use `error.isNetworkError`
alert(`${error}: ${file.name}`);
});
uppy.on('complete', (result) => {
result.successful.forEach((result) => {
const {assetId, suggestedFilename, filename} = result.response.body;
return addBlock({
asset: {
id: assetId,
filename: suggestedFilename || filename,
},
});
});
});
const removeBlockByIndex = (index) => {
// TODO: delete the asset?
setBlocks(blocks.filter((block, blockIndex) => blockIndex !== index));
};
return (
<div>
<div className='uk-margin'>
<div className='uk-margin-small'>
<DragDrop
uppy={uppy}
/>
</div>
<div className='uk-margin-small'>
<StatusBar
uppy={uppy}
/>
</div>
<div className='uk-margin-small'>
<div className='uk-grid uk-grid-small' data-uk-grid>
<div className='uk-width-3-4'>
<input
type='text'
className='uk-input'
placeholder='YouTube/Vimeo Video URL'
onChange={(event) => setEmbedUrl(event.target.value)}
value={embedUrl}
disabled={isSubmitting}
/>
</div>
<div className='uk-width-1-4'>
<button
type='button'
className='uk-button uk-button-primary uk-border-pill uk-display-block uk-width-1-1'
disabled={!embedUrl}
onClick={() => addEmbed()}
>+<span className='uk-visible@s'> Add Video URL</span></button>
</div>
</div>
</div>
</div>
<div className='uk-margin'>
<ReactSortable list={blocks} setList={setBlocks}>
{blocks.map((block, index) => (
<Row
key={block.id}
{...{block, index, removeBlockByIndex}}
/>
))}
</ReactSortable>
</div>
</div>
);
};
export default hot(UgcUpload);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment