feat: add video preview widget to settings modal

This commit is contained in:
Keith Grootboom
2023-03-14 14:19:41 +02:00
committed by Ken Clary
parent c7c0d5e100
commit b4fb88c73c
7 changed files with 154 additions and 1 deletions

View File

@@ -105,9 +105,11 @@ export const fileInput = ({ setThumbnailSrc, imgRef, fileSizeError }) => {
const [resampledUrl, resampledFile] = module.resampleImage({ image, filename: file.name });
setThumbnailSrc(resampledUrl);
dispatch(thunkActions.video.uploadThumbnail({ thumbnail: resampledFile }));
dispatch(actions.video.updateField({ thumbnail: resampledUrl }));
return;
}
dispatch(thunkActions.video.uploadThumbnail({ thumbnail: file }));
dispatch(actions.video.updateField({ thumbnail: reader.result }));
};
};
dispatch(actions.video.updateField({ thumbnail: ' ' }));

View File

@@ -59,7 +59,6 @@ export const LanguageSelector = ({
const onLanguageChange = module.hooks.onSelectLanguage({
dispatch: useDispatch(), languageBeforeChange: localLang, setLocalLang, triggerupload: input.click,
});
console.log({ localLang, language, openLanguages });
const getTitle = () => {
if (Object.prototype.hasOwnProperty.call(videoTranscriptLanguages, language)) {

View File

@@ -0,0 +1,34 @@
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Icon } from '@edx/paragon';
import { ClosedCaptionOff, ClosedCaption } from '@edx/paragon/icons';
import PropTypes from 'prop-types';
import React from 'react';
import messages from '../messages';
import { hooks as transcriptHooks } from '../TranscriptWidget';
export const LanguageNamesWidget = ({ transcripts, intl }) => {
let icon = ClosedCaptionOff;
const hasTranscripts = transcriptHooks.hasTranscripts(transcripts);
let message = intl.formatMessage(messages.noTranscriptsAdded);
let fontClass = 'text-gray';
if (hasTranscripts) {
message = transcriptHooks.transcriptLanguages(transcripts, intl);
fontClass = 'text-primary';
icon = ClosedCaption;
}
return (
<div className="d-flex flex-row align-items-center x-small">
<Icon className="mr-1" src={icon} />
<span className={fontClass}>{message}</span>
</div>
);
};
LanguageNamesWidget.propTypes = {
intl: intlShape.isRequired,
transcripts: PropTypes.arrayOf(PropTypes.string).isRequired,
};
export default injectIntl(LanguageNamesWidget);

View File

@@ -0,0 +1,18 @@
import messages from '../messages';
// https://stackoverflow.com/a/28735961/479084
const youtubeRegex = /^(?:https?:\/\/)?(?:m\.|www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;
function isYoutubeUrl(url) {
return url.match(youtubeRegex) !== null;
}
function getVideoType(videoSource) {
if (isYoutubeUrl(videoSource)) {
return messages.videoTypeYoutube;
}
return messages.videoTypeOther;
}
export default {
getVideoType,
};

View File

@@ -0,0 +1,84 @@
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
Collapsible, Icon, Image, Stack,
} from '@edx/paragon';
import { Launch } from '@edx/paragon/icons';
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import { selectors } from '../../../../../../data/redux';
import thumbnailMessages from '../ThumbnailWidget/messages';
import hooks from './hooks';
import LanguageNamesWidget from './LanguageNamesWidget';
export const VideoPreviewWidget = ({
thumbnail,
videoSource,
transcripts,
blockTitle,
intl,
}) => {
const imgRef = React.useRef();
const videoType = intl.formatMessage(hooks.getVideoType(videoSource));
return (
<Collapsible.Advanced
className="collapsible-card rounded mx-4 my-3 px-4"
defaultOpen
open
>
<Collapsible.Body className="collapsible-body rounded px-0 py-4">
<div className="d-flex flex-row">
<Image
thumbnail
className="mr-3"
ref={imgRef}
src={thumbnail}
alt={intl.formatMessage(thumbnailMessages.thumbnailAltText)}
style={{
maxWidth: '200px',
minWidth: '200px',
minHeight: '112px',
maxHeight: '112px',
}}
/>
<Stack gap={1} className="justify-content-center">
<h4 className="text-primary mb-0">{blockTitle}</h4>
<LanguageNamesWidget transcripts={transcripts} />
{videoType && (
<a
className="text-primary x-small"
href={videoSource}
target="_blank"
rel="noopener noreferrer"
>
{videoType}
<Icon
className="d-inline-block align-text-bottom pgn__icon__sm"
src={Launch}
/>
</a>
)}
</Stack>
</div>
</Collapsible.Body>
</Collapsible.Advanced>
);
};
VideoPreviewWidget.propTypes = {
intl: intlShape.isRequired,
videoSource: PropTypes.string.isRequired,
thumbnail: PropTypes.string.isRequired,
transcripts: PropTypes.arrayOf(PropTypes.string).isRequired,
blockTitle: PropTypes.string.isRequired,
};
export const mapStateToProps = (state) => ({
transcripts: selectors.video.transcripts(state),
videoSource: selectors.video.videoSource(state),
thumbnail: selectors.video.thumbnail(state),
blockTitle: selectors.app.blockTitle(state),
});
export default injectIntl(connect(mapStateToProps)(VideoPreviewWidget));

View File

@@ -61,6 +61,20 @@ export const messages = {
id: 'authoring.videoeditor.duration.custom',
defaultMessage: 'Custom: {total}',
description: 'Text describing a video with custom start time and custom stop time, or just a custom stop time for a collapsed widget',
noTranscriptsAdded: {
id: 'authoring.videoeditor.transcripts.empty',
defaultMessage: 'No transcripts added',
description: 'Message shown when the user has not selected any transcripts for the video.',
},
videoTypeYoutube: {
id: 'authoring.videoeditor.videotype.youtube',
defaultMessage: 'Youtube video',
description: 'Shown on the preview card if the video is from youtube.com.',
},
videoTypeOther: {
id: 'authoring.videoeditor.videotype.other',
defaultMessage: 'Other video',
description: 'Shown on the preview card if the video source could not be identified.',
},
};

View File

@@ -8,11 +8,13 @@ import LicenseWidget from './components/LicenseWidget';
import ThumbnailWidget from './components/ThumbnailWidget';
import TranscriptWidget from './components/TranscriptWidget';
import VideoSourceWidget from './components/VideoSourceWidget';
import VideoPreviewWidget from './components/VideoPreviewWidget';
import './index.scss';
export const VideoSettingsModal = () => (
<>
<ErrorSummary />
<VideoPreviewWidget />
<VideoSourceWidget />
<ThumbnailWidget />
<TranscriptWidget />