Merge branch 'keith/video-preview-widget'

This commit is contained in:
Ken Clary
2023-03-23 14:19:24 -04:00
7 changed files with 144 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,13 @@
import messages from '../messages';
import { parseYoutubeId } from '../../../../../../data/services/cms/api';
function getVideoType(videoSource) {
if (parseYoutubeId(videoSource) !== null) {
return messages.videoTypeYoutube;
}
return messages.videoTypeOther;
}
export default {
getVideoType,
};

View File

@@ -0,0 +1,79 @@
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
Collapsible, Image, Stack, Hyperlink,
} from '@edx/paragon';
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 && (
<Hyperlink
className="text-primary x-small"
destination={videoSource}
target="_blank"
rel="noopener noreferrer"
>
{videoType}
</Hyperlink>
)}
</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 />