added audio preview in content builder

This commit is contained in:
Michiel Sioen 2022-02-06 11:49:57 +01:00
parent ba558ca530
commit d32e31d488
19 changed files with 193 additions and 14 deletions

View File

@ -32,7 +32,8 @@
"allowedTypes": [
"files",
"images",
"videos"
"videos",
"audios"
],
"pluginOptions": {}
},

View File

@ -11,7 +11,8 @@ const options = [
children: [
{ label: 'images (JPEG, PNG, GIF, SVG, TIFF, ICO, DVU)', value: 'images' },
{ label: 'videos (MPEG, MP4, Quicktime, WMV, AVI, FLV)', value: 'videos' },
{ label: 'files (CSV, ZIP, MP3, PDF, Excel, JSON, ...)', value: 'files' },
{ label: 'audios (MP3, WAV, OGG)', value: 'audios' },
{ label: 'files (CSV, ZIP, PDF, Excel, JSON, ...)', value: 'files' },
],
},
];

View File

@ -215,7 +215,7 @@ const types = {
required: validators.required(),
allowedTypes: yup
.array()
.of(yup.string().oneOf(['images', 'videos', 'files']))
.of(yup.string().oneOf(['images', 'videos', 'files', 'audios']))
.min(1)
.nullable(),
};

View File

@ -266,7 +266,7 @@ const reducer = (state = initialState, action) =>
dataToSet = options;
} else if (attributeType === 'media') {
dataToSet = {
allowedTypes: ['images', 'files', 'videos'],
allowedTypes: ['images', 'files', 'videos', 'audios'],
type: 'media',
multiple: true,
...options,

View File

@ -582,7 +582,7 @@ describe('CTB | components | FormModal | reducer | actions', () => {
modifiedData: {
type: 'media',
multiple: true,
allowedTypes: ['images', 'files', 'videos'],
allowedTypes: ['images', 'files', 'videos', 'audios'],
},
};

View File

@ -36,7 +36,7 @@ export default {
type: 'media',
multiple: false,
required: false,
allowedTypes: ['files', 'images', 'videos'],
allowedTypes: ['files', 'images', 'videos', 'audios'],
pluginOptions: {
i18n: {
localized: true,

View File

@ -53,7 +53,7 @@ const getTypeShape = (attribute, { modelType, attributes } = {}) => {
required: validators.required,
allowedTypes: yup
.array()
.of(yup.string().oneOf(['images', 'videos', 'files']))
.of(yup.string().oneOf(['images', 'videos', 'files', 'audios']))
.min(1),
};
}

View File

@ -4,6 +4,7 @@ import { prefixFileUrlWithBackendUrl, getFileExtension } from '@strapi/helper-pl
import { ImageAssetCard } from './ImageAssetCard';
import { VideoAssetCard } from './VideoAssetCard';
import { DocAssetCard } from './DocAssetCard';
import { AudioAssetCard } from './AudioAssetCard';
import { AssetType, AssetDefinition } from '../../constants';
import { createAssetUrl } from '../../utils/createAssetUrl';
import toSingularTypes from '../../utils/toSingularTypes';
@ -64,7 +65,31 @@ export const AssetCard = ({
);
}
const canSelectAsset = singularTypes.includes('file') && !['video', 'image'].includes(fileType);
if (asset.mime.includes(AssetType.Audio)) {
const canSelectAsset = singularTypes.includes(fileType);
if (!canSelectAsset && !isSelected) {
handleSelect = undefined;
}
return (
<AudioAssetCard
id={asset.id}
key={asset.id}
name={asset.name}
extension={getFileExtension(asset.ext)}
url={local ? asset.url : createAssetUrl(asset, true)}
mime={asset.mime}
onEdit={onEdit ? () => onEdit(asset) : undefined}
onSelect={handleSelect}
selected={isSelected}
size={size}
/>
);
}
const canSelectAsset =
singularTypes.includes('file') && !['video', 'image', 'audio'].includes(fileType);
if (!canSelectAsset && !isSelected) {
handleSelect = undefined;
@ -74,7 +99,7 @@ export const AssetCard = ({
};
AssetCard.defaultProps = {
allowedTypes: ['images', 'files', 'videos'],
allowedTypes: ['images', 'files', 'videos', 'audios'],
isSelected: false,
// Determine if the asset is loaded locally or from a remote resource
local: false,

View File

@ -0,0 +1,100 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import {
Card,
CardAction,
CardAsset,
CardBadge,
CardBody,
CardCheckbox,
CardContent,
CardHeader,
CardTitle,
CardSubtitle,
} from '@strapi/design-system/Card';
import { IconButton } from '@strapi/design-system/IconButton';
import Pencil from '@strapi/icons/Pencil';
import { useIntl } from 'react-intl';
import { Box } from '@strapi/design-system/Box';
import { AudioPreview } from './AudioPreview';
import { getTrad } from '../../utils';
const Extension = styled.span`
text-transform: uppercase;
`;
const AudioPreviewWrapper = styled(Box)`
canvas,
audio {
display: block;
max-width: 100%;
max-height: ${({ size }) => (size === 'M' ? 164 / 16 : 88 / 16)}rem;
}
`;
export const AudioAssetCard = ({
name,
extension,
url,
mime,
selected,
onSelect,
onEdit,
size,
}) => {
const { formatMessage } = useIntl();
return (
<Card>
<CardHeader>
<CardAsset size={size}>
<AudioPreviewWrapper size={size}>
<AudioPreview url={url} mime={mime} alt={name} />
</AudioPreviewWrapper>
</CardAsset>
{onSelect && <CardCheckbox value={selected} onValueChange={onSelect} />}
{onEdit && (
<CardAction position="end">
<IconButton
label={formatMessage({ id: getTrad('control-card.edit'), defaultMessage: 'Edit' })}
icon={<Pencil />}
onClick={onEdit}
/>
</CardAction>
)}
</CardHeader>
<CardBody>
<CardContent>
<Box paddingTop={1}>
<CardTitle as="h2">{name}</CardTitle>
</Box>
<CardSubtitle>
<Extension>{extension}</Extension>
</CardSubtitle>
</CardContent>
<CardBadge>
{formatMessage({ id: getTrad('settings.section.audio.label'), defaultMessage: 'Audio' })}
</CardBadge>
</CardBody>
</Card>
);
};
AudioAssetCard.defaultProps = {
onSelect: undefined,
onEdit: undefined,
selected: false,
size: 'M',
};
AudioAssetCard.propTypes = {
extension: PropTypes.string.isRequired,
mime: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
onSelect: PropTypes.func,
onEdit: PropTypes.func,
url: PropTypes.string.isRequired,
selected: PropTypes.bool,
size: PropTypes.oneOf(['S', 'M']),
};

View File

@ -0,0 +1,24 @@
/* eslint-disable jsx-a11y/media-has-caption */
import React from 'react';
import PropTypes from 'prop-types';
import { Box } from '@strapi/design-system/Box';
import { VisuallyHidden } from '@strapi/design-system/VisuallyHidden';
export const AudioPreview = ({ url, mime, alt }) => {
return (
<Box>
<audio controls>
<source src={url} type={mime} />
</audio>
<VisuallyHidden as="figcaption">{alt}</VisuallyHidden>
</Box>
);
};
AudioPreview.defaultProps = {};
AudioPreview.propTypes = {
alt: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
mime: PropTypes.string.isRequired,
};

View File

@ -44,6 +44,11 @@ export const UploadingAssetCard = ({ asset, onCancel, onStatusChange, addUploade
id: getTrad('settings.section.video.label'),
defaultMessage: 'Video',
});
} else if (asset.type === AssetType.Audio) {
badgeContent = formatMessage({
id: getTrad('settings.section.audio.label'),
defaultMessage: 'Audio',
});
} else {
badgeContent = formatMessage({
id: getTrad('settings.section.doc.label'),

View File

@ -75,7 +75,7 @@ export const AssetList = ({
};
AssetList.defaultProps = {
allowedTypes: ['images', 'files', 'videos'],
allowedTypes: ['images', 'files', 'videos', 'audios'],
onEditAsset: undefined,
size: 'M',
onReorderAsset: undefined,

View File

@ -27,6 +27,14 @@ export const AssetPreview = forwardRef(({ mime, url, name }, ref) => {
);
}
if (mime.includes(AssetType.Audio)) {
return (
<audio controls src={url} ref={ref}>
<track label={name} default kind="captions" srcLang={lang} src="" />
</audio>
);
}
if (mime.includes('pdf')) {
return (
<CardAsset justifyContent="center">

View File

@ -27,7 +27,7 @@ export const MediaLibraryDialog = ({ onClose, onSelectAssets, allowedTypes }) =>
};
MediaLibraryDialog.defaultProps = {
allowedTypes: ['files', 'images', 'videos'],
allowedTypes: ['files', 'images', 'videos', 'audios'],
};
MediaLibraryDialog.propTypes = {

View File

@ -6,6 +6,7 @@ import { Box } from '@strapi/design-system/Box';
import { Flex } from '@strapi/design-system/Flex';
import { AssetType, AssetDefinition } from '../../../constants';
import { VideoPreview } from '../../AssetCard/VideoPreview';
import { AudioPreview } from '../../AssetCard/AudioPreview';
import { createAssetUrl } from '../../../utils/createAssetUrl';
const DocAsset = styled(Flex)`
@ -33,6 +34,16 @@ export const CarouselAsset = ({ asset }) => {
);
}
if (asset.mime.includes(AssetType.Audio)) {
return (
<AudioPreview
url={createAssetUrl(asset, true)}
mime={asset.mime}
alt={asset.alternativeText || asset.name}
/>
);
}
if (asset.mime.includes(AssetType.Image)) {
return (
<Box

View File

@ -24,7 +24,7 @@ export const MediaLibraryInput = ({
value,
required,
}) => {
const fieldAllowedTypes = allowedTypes || ['files', 'images', 'videos'];
const fieldAllowedTypes = allowedTypes || ['files', 'images', 'videos', 'audios'];
const [uploadedFiles, setUploadedFiles] = useState([]);
const [step, setStep] = useState(undefined);
const [selectedIndex, setSelectedIndex] = useState(0);
@ -180,7 +180,7 @@ export const MediaLibraryInput = ({
};
MediaLibraryInput.defaultProps = {
attribute: { allowedTypes: ['videos', 'files', 'images'] },
attribute: { allowedTypes: ['videos', 'files', 'images', 'audios'] },
disabled: false,
description: undefined,
error: undefined,

View File

@ -4,6 +4,7 @@ export const AssetType = {
Video: 'video',
Image: 'image',
Document: 'doc',
Audio: 'audio',
};
export const AssetSource = {

View File

@ -7,6 +7,9 @@ export const typeFromMime = mime => {
if (mime.includes(AssetType.Video)) {
return AssetType.Video;
}
if (mime.includes(AssetType.Audio)) {
return AssetType.Audio;
}
return AssetType.Document;
};

View File

@ -73,7 +73,7 @@ module.exports = plop => {
}
if (answer.attributeType === 'media') {
val.allowedTypes = ['images', 'files', 'videos'];
val.allowedTypes = ['images', 'files', 'videos', 'audios'];
val.multiple = answer.multiple;
}