feat: localize videos (#42869)
* refactor: separate out VideoPlayer component * feat: support bilibili videos * feat(client): allow localized videos to be shown * fix: remove add subtitles CTA * feat: add locale ids for Why Program?
This commit is contained in:
committed by
GitHub
parent
48f2c02c5d
commit
2b6bef08ae
@ -183,6 +183,17 @@ type Required = {
|
||||
src: string;
|
||||
crossDomain?: boolean;
|
||||
};
|
||||
export interface BilibiliIds {
|
||||
aid: string;
|
||||
bvid: string;
|
||||
cid: string;
|
||||
}
|
||||
|
||||
export interface VideoLocaleIds {
|
||||
espanol?: string;
|
||||
italian?: string;
|
||||
portuguese?: string;
|
||||
}
|
||||
|
||||
export type ChallengeNodeType = {
|
||||
block: string;
|
||||
@ -213,6 +224,8 @@ export type ChallengeNodeType = {
|
||||
translationPending: boolean;
|
||||
url: string;
|
||||
videoId: string;
|
||||
videoLocaleIds?: VideoLocaleIds;
|
||||
bilibiliIds?: BilibiliIds;
|
||||
videoUrl: string;
|
||||
};
|
||||
|
||||
|
78
client/src/templates/Challenges/components/VideoPlayer.tsx
Normal file
78
client/src/templates/Challenges/components/VideoPlayer.tsx
Normal file
@ -0,0 +1,78 @@
|
||||
import React from 'react';
|
||||
import YouTube from 'react-youtube';
|
||||
import envData from '../../../../../config/env.json';
|
||||
import type { BilibiliIds, VideoLocaleIds } from '../../../redux/prop-types';
|
||||
|
||||
// TODO: pull these types from all-langs
|
||||
const { clientLocale } = envData as {
|
||||
clientLocale:
|
||||
| 'english'
|
||||
| 'chinese'
|
||||
| 'chinese-traditional'
|
||||
| 'espanol'
|
||||
| 'italian'
|
||||
| 'portuguese';
|
||||
};
|
||||
|
||||
interface VideoPlayerProps {
|
||||
videoId: string;
|
||||
videoLocaleIds?: VideoLocaleIds;
|
||||
onVideoLoad: () => void;
|
||||
videoIsLoaded: boolean;
|
||||
bilibiliIds?: BilibiliIds;
|
||||
title: string;
|
||||
}
|
||||
|
||||
function VideoPlayer({
|
||||
videoId,
|
||||
videoLocaleIds,
|
||||
onVideoLoad,
|
||||
videoIsLoaded,
|
||||
bilibiliIds,
|
||||
title
|
||||
}: VideoPlayerProps): JSX.Element {
|
||||
let bilibiliSrc = null;
|
||||
|
||||
if (
|
||||
bilibiliIds &&
|
||||
['chinese', 'chinese-traditional'].includes(clientLocale)
|
||||
) {
|
||||
const { aid, bvid, cid } = bilibiliIds;
|
||||
bilibiliSrc = `//player.bilibili.com/player.html?aid=${aid}&bvid=${bvid}&cid=${cid}`;
|
||||
}
|
||||
|
||||
if (videoLocaleIds) {
|
||||
const localeId = videoLocaleIds[clientLocale as keyof VideoLocaleIds];
|
||||
videoId = localeId || videoId;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{bilibiliSrc ? (
|
||||
<iframe
|
||||
frameBorder='no'
|
||||
scrolling='no'
|
||||
src={bilibiliSrc}
|
||||
title={title}
|
||||
/>
|
||||
) : (
|
||||
<YouTube
|
||||
className={
|
||||
videoIsLoaded ? 'display-youtube-video' : 'hide-youtube-video'
|
||||
}
|
||||
onReady={onVideoLoad}
|
||||
opts={{
|
||||
playerVars: {
|
||||
rel: 0
|
||||
},
|
||||
width: 'auto',
|
||||
height: 'auto'
|
||||
}}
|
||||
videoId={videoId}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default VideoPlayer;
|
@ -6,7 +6,6 @@ import Helmet from 'react-helmet';
|
||||
import { ObserveKeys } from 'react-hotkeys';
|
||||
import { TFunction, withTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import YouTube from 'react-youtube';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
import { createSelector } from 'reselect';
|
||||
@ -21,6 +20,7 @@ import {
|
||||
} from '../../../redux/prop-types';
|
||||
import ChallengeDescription from '../components/Challenge-Description';
|
||||
import Hotkeys from '../components/Hotkeys';
|
||||
import VideoPlayer from '../components/VideoPlayer';
|
||||
import ChallengeTitle from '../components/challenge-title';
|
||||
import CompletionModal from '../components/completion-modal';
|
||||
import PrismFormatted from '../components/prism-formatted';
|
||||
@ -162,7 +162,7 @@ class ShowVideo extends Component<ShowVideoProps, ShowVideoState> {
|
||||
});
|
||||
};
|
||||
|
||||
videoIsReady = () => {
|
||||
onVideoLoad = () => {
|
||||
this.setState({
|
||||
videoIsLoaded: true
|
||||
});
|
||||
@ -179,6 +179,8 @@ class ShowVideo extends Component<ShowVideoProps, ShowVideoState> {
|
||||
block,
|
||||
translationPending,
|
||||
videoId,
|
||||
videoLocaleIds,
|
||||
bilibiliIds,
|
||||
question: { text, answers, solution }
|
||||
}
|
||||
},
|
||||
@ -223,22 +225,13 @@ class ShowVideo extends Component<ShowVideoProps, ShowVideoState> {
|
||||
<Loader />
|
||||
</div>
|
||||
) : null}
|
||||
<YouTube
|
||||
className={
|
||||
this.state.videoIsLoaded
|
||||
? 'display-youtube-video'
|
||||
: 'hide-youtube-video'
|
||||
}
|
||||
onReady={this.videoIsReady}
|
||||
opts={{
|
||||
playerVars: {
|
||||
rel: 0
|
||||
},
|
||||
width: 'auto',
|
||||
height: 'auto'
|
||||
}}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
<VideoPlayer
|
||||
bilibiliIds={bilibiliIds}
|
||||
onVideoLoad={this.onVideoLoad}
|
||||
title={title}
|
||||
videoId={videoId}
|
||||
videoIsLoaded={this.state.videoIsLoaded}
|
||||
videoLocaleIds={videoLocaleIds}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
@ -323,6 +316,16 @@ export const query = graphql`
|
||||
query VideoChallenge($slug: String!) {
|
||||
challengeNode(fields: { slug: { eq: $slug } }) {
|
||||
videoId
|
||||
videoLocaleIds {
|
||||
espanol
|
||||
italian
|
||||
portuguese
|
||||
}
|
||||
bilibiliIds {
|
||||
aid
|
||||
bvid
|
||||
cid
|
||||
}
|
||||
title
|
||||
description
|
||||
challengeType
|
||||
|
@ -3,6 +3,14 @@ id: 5e6a54a558d3af90110a60a0
|
||||
title: 'Introduction: Why Program?'
|
||||
challengeType: 11
|
||||
videoId: 3muQV-Im3Z0
|
||||
bilibiliIds:
|
||||
aid: 206882253
|
||||
bvid: BV1Fh411z7tr
|
||||
cid: 376314257
|
||||
videoLocaleIds:
|
||||
espanol: 3muQV-Im3Z0
|
||||
italian: 3muQV-Im3Z0
|
||||
portuguese: 3muQV-Im3Z0
|
||||
dashedName: introduction-why-program
|
||||
---
|
||||
|
||||
|
@ -58,6 +58,22 @@ const schema = Joi.object()
|
||||
is: challengeTypes.video,
|
||||
then: Joi.string().required()
|
||||
}),
|
||||
videoLocaleIds: Joi.when('challengeType', {
|
||||
is: challengeTypes.video,
|
||||
then: Joi.object().keys({
|
||||
espanol: Joi.string(),
|
||||
italian: Joi.string(),
|
||||
portuguese: Joi.string()
|
||||
})
|
||||
}),
|
||||
bilibiliIds: Joi.when('challengeType', {
|
||||
is: challengeTypes.video,
|
||||
then: Joi.object().keys({
|
||||
aid: Joi.number().required(),
|
||||
bvid: Joi.string().required(),
|
||||
cid: Joi.number().required()
|
||||
})
|
||||
}),
|
||||
question: Joi.object().keys({
|
||||
text: Joi.string().required(),
|
||||
answers: Joi.array().items(Joi.string()).required(),
|
||||
|
Reference in New Issue
Block a user