-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Maham Akif
authored and
Maham Akif
committed
Jul 12, 2024
1 parent
516a21b
commit 0f99e70
Showing
14 changed files
with
568 additions
and
8 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import React, { useEffect } from 'react'; | ||
import { useParams, Link, useLocation } from 'react-router-dom'; | ||
import { | ||
Container, Breadcrumb, Row, MediaQuery, breakpoints, Badge, Skeleton, | ||
} from '@openedx/paragon'; | ||
import loadable from '@loadable/component'; | ||
import { FormattedMessage } from '@edx/frontend-platform/i18n'; | ||
import { useEnterpriseCustomer } from '../app/data'; | ||
import { Sidebar } from '../layout'; | ||
import './styles/VideoDetailPage.scss'; | ||
import DelayedFallbackContainer from '../DelayedFallback/DelayedFallbackContainer'; | ||
import { useVideoData } from './data/hooks'; | ||
import { features } from '../../config'; | ||
|
||
const VideoPlayer = loadable(() => import(/* webpackChunkName: "videojs" */ '../video/VideoPlayer'), { | ||
fallback: ( | ||
<DelayedFallbackContainer> | ||
<Skeleton height={200} /> | ||
</DelayedFallbackContainer> | ||
), | ||
}); | ||
|
||
const VideoDetailPage = () => { | ||
const { edxVideoID } = useParams(); | ||
const { data: enterpriseCustomer } = useEnterpriseCustomer(); | ||
const location = useLocation(); | ||
|
||
const [videoData, isLoading, error] = useVideoData(edxVideoID); | ||
|
||
const customOptions = { | ||
showPlaybackMenu: true, | ||
showTranscripts: true, | ||
transcriptUrls: videoData?.transcriptUrls, | ||
}; | ||
|
||
useEffect(() => { | ||
if (videoData?.videoURL) { | ||
VideoPlayer.preload(); | ||
} | ||
}, [videoData?.videoURL]); | ||
|
||
const routeLinks = [ | ||
{ | ||
label: 'Explore Videos', | ||
to: `/${enterpriseCustomer.slug}/videos`, | ||
}, | ||
]; | ||
if (location.state?.parentRoute) { | ||
routeLinks.push(location.state.parentRoute); | ||
} | ||
|
||
if (isLoading) { | ||
return ( | ||
<DelayedFallbackContainer> | ||
<Skeleton height={400} /> | ||
</DelayedFallbackContainer> | ||
); | ||
} | ||
|
||
// Comprehensive error handling will be implemented upon receiving specific error use cases from the UX team | ||
// and corresponding Figma designs. | ||
if (error || !features.FEATURE_ENABLE_VIDEO_CATALOG) { | ||
return ( | ||
<div className="text-center py-5"> | ||
<h1>404</h1> | ||
<p>{error?.message}</p> | ||
</div> | ||
); | ||
} | ||
|
||
return ( | ||
<Container size="lg" className="pt-3"> | ||
<div className="small"> | ||
<Breadcrumb | ||
links={routeLinks} | ||
activeLabel={videoData.courseTitle} | ||
linkAs={Link} | ||
/> | ||
</div> | ||
<Row> | ||
<article className="col-12 col-lg-9"> | ||
<div className="d-flex flex-column align-items-start flex-grow-1 video-container"> | ||
<div className="d-flex flex-row align-items-center justify-content-between"> | ||
<h2 data-testid="video-title" className="mb-0"> | ||
{videoData.courseTitle} | ||
</h2> | ||
<span className="small ml-2 mt-2"> | ||
{videoData.videoDuration && `(${videoData.videoDuration} minutes)`} | ||
</span> | ||
</div> | ||
<p className="small align-self-stretch text-justify mb-2"> | ||
{videoData.videoSummary} | ||
</p> | ||
<div className="d-flex flex-row align-items-center"> | ||
<h4> | ||
<FormattedMessage | ||
id="videoDetailPage.skills.label" | ||
defaultMessage="Skills:" | ||
description="Label for skills on video detail page" | ||
/> | ||
</h4> | ||
<div className="ml-2 mb-2.5"> | ||
{videoData.videoSkills && ( | ||
videoData.videoSkills.map((skill) => ( | ||
<Badge | ||
key={skill.skill_id} | ||
className="video-skills" | ||
variant="light" | ||
> | ||
{skill.name} | ||
</Badge> | ||
)) | ||
)} | ||
</div> | ||
</div> | ||
</div> | ||
<div className="video-wrapper mt-2"> | ||
<VideoPlayer videoURL={videoData.videoUrl} customOptions={customOptions} /> | ||
</div> | ||
</article> | ||
<MediaQuery minWidth={breakpoints.large.minWidth}> | ||
{matches => matches && ( | ||
<Sidebar> | ||
{/* Course Sidebar will be inserted here */} | ||
</Sidebar> | ||
)} | ||
</MediaQuery> | ||
</Row> | ||
</Container> | ||
); | ||
}; | ||
|
||
export default VideoDetailPage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { useState, useEffect } from 'react'; | ||
import { logError } from '@edx/frontend-platform/logging'; | ||
import { fetchVideoData } from './service'; | ||
|
||
export function useVideoData(edxVideoID) { | ||
const [videoData, setVideoData] = useState({}); | ||
const [error, setError] = useState(null); | ||
const [isLoading, setIsLoading] = useState(true); | ||
|
||
useEffect(() => { | ||
const fetchData = async () => { | ||
try { | ||
const data = await fetchVideoData(edxVideoID); | ||
setVideoData(data); | ||
} catch (err) { | ||
logError(err); | ||
setError(err); | ||
} finally { | ||
setIsLoading(false); | ||
} | ||
}; | ||
|
||
fetchData(); | ||
}, [edxVideoID]); | ||
|
||
return [videoData, isLoading, error]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; | ||
import { camelCaseObject } from '@edx/frontend-platform/utils'; | ||
import { getConfig } from '@edx/frontend-platform/config'; | ||
import { transformVideoData } from './utils'; | ||
|
||
export const fetchVideoData = async (edxVideoID) => { | ||
const config = getConfig(); | ||
const url = `${config.ENTERPRISE_CATALOG_API_BASE_URL}/api/v1/videos/${edxVideoID}`; | ||
const result = await getAuthenticatedHttpClient().get(url); | ||
return camelCaseObject(transformVideoData(result?.data || {})); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
export const transformVideoData = (data) => { | ||
const skillNames = Object.values(data.skills).map(skill => ({ | ||
skill_id: skill.skill_id, | ||
name: skill.name, | ||
})); | ||
|
||
const minutes = Math.floor(data.json_metadata.duration / 60); | ||
const seconds = Math.round(data.json_metadata.duration % 60); | ||
const duration = `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`; | ||
|
||
const transformedData = { | ||
videoUrl: data.json_metadata.download_link, | ||
courseTitle: data.parent_content_metadata.title, | ||
videoSummary: data.summary_transcripts['list-item'], | ||
transcriptUrls: data.json_metadata.transcript_urls, | ||
videoSkills: skillNames, | ||
videoDuration: duration, | ||
}; | ||
|
||
return transformedData; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default as VideoDetailPage } from './VideoDetailPage'; |
Oops, something went wrong.