Merge branch 'feature/video-player' of 858/frontend into develop
This commit is contained in:
commit
7d8778b51c
|
@ -19,7 +19,7 @@
|
|||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
<title>858</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
|
||||
// Topics Action Types
|
||||
export const START_LOADING_ALL_TOPICS = 'START_LOADING_ALL_TOPICS';
|
||||
export const LOADED_ALL_TOPICS = 'LOADED_ALL_TOPICS';
|
||||
export const START_LOADING_RANDOM_TOPIC = 'START_LOADING_RANDOM_TOPIC';
|
||||
export const LOADED_RANDOM_TOPIC = 'LOADED_RANDOM_TOPIC';
|
||||
|
||||
// Videos Action Types
|
||||
export const ADD_VIDEOS_TO_STATE = 'ADD_VIDEOS_TO_STATE';
|
||||
export const START_LOADING_VIDEO = 'START_LOADING_VIDEO';
|
||||
export const LOADED_VIDEO = 'LOADED_VIDEO';
|
|
@ -2,6 +2,7 @@ import { fetchAllTopics, fetchVideosByTopic } from '../utils/api';
|
|||
import { APIError } from './errors';
|
||||
import { getItem, setItem } from '../utils/safe-storage';
|
||||
import getRandomTopic from '../utils/get-random-topic';
|
||||
import { addVideosToState } from './videos';
|
||||
import {
|
||||
START_LOADING_ALL_TOPICS,
|
||||
LOADED_ALL_TOPICS,
|
||||
|
@ -42,6 +43,7 @@ export function getRandomTopicVideos(allTopics, numVideos=4) {
|
|||
dispatch(loadingRandomTopicVideos());
|
||||
fetchVideosByTopic(randomTopic.name, 0, 4)
|
||||
.then(videos => {
|
||||
dispatch(addVideosToState(videos));
|
||||
dispatch(loadedRandomTopicVideos(randomTopic, videos));
|
||||
});
|
||||
};
|
||||
|
|
8
src/actions/videos.js
Normal file
8
src/actions/videos.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { ADD_VIDEOS_TO_STATE } from './action_types';
|
||||
|
||||
export function addVideosToState(videos) {
|
||||
return {
|
||||
type: ADD_VIDEOS_TO_STATE,
|
||||
payload: videos
|
||||
};
|
||||
}
|
|
@ -3,7 +3,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import VideoItem from "./VideoItem";
|
||||
import SectionHeading from "./SectionHeading";
|
||||
import {Link} from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
class RandomTopic extends React.Component {
|
||||
|
||||
|
@ -15,7 +15,7 @@ class RandomTopic extends React.Component {
|
|||
width={450}
|
||||
gap={16}>
|
||||
{this.props.videos.map(video =>
|
||||
<VideoItem id={video.id} title={video.title}/>
|
||||
<VideoItem id={video.id} key={video.id} title={video.title}/>
|
||||
)}
|
||||
<Link to="/topics">
|
||||
See All Topics
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getThumbnail } from '../utils/video';
|
||||
class VideoItem extends React.Component {
|
||||
|
||||
render() {
|
||||
const videoLink = `/videos/${this.props.id}`;
|
||||
return (
|
||||
<section className="video-item">
|
||||
<section className="video-thumbnail-container">
|
||||
<img className="video-thumbnail" src={ getThumbnail(this.props.id) } alt=""/>
|
||||
<Link to={ videoLink }>
|
||||
<section className="video-item">
|
||||
<section className="video-thumbnail-container">
|
||||
<img className="video-thumbnail" src={ getThumbnail(this.props.id) } alt=""/>
|
||||
</section>
|
||||
<h3 className="video-title">{ this.props.title }</h3>
|
||||
</section>
|
||||
<h3 className="video-title">{ this.props.title }</h3>
|
||||
</section>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import React from 'react';
|
||||
import Grid from 'react-css-grid';
|
||||
import PropTypes from 'prop-types';
|
||||
import { getVideo } from '../utils/video';
|
||||
|
||||
class VideoPlayer extends React.Component {
|
||||
render() {
|
||||
|
@ -8,19 +10,13 @@ class VideoPlayer extends React.Component {
|
|||
<Grid width={450}
|
||||
gap={16}>
|
||||
<section>
|
||||
<h2>Title</h2>
|
||||
<video src="https://video22.858.ma/BUR/240p3.mp4" controls>
|
||||
<h2>{ this.props.title }</h2>
|
||||
<video src={ getVideo(this.props.id) } controls>
|
||||
</video>
|
||||
<h3>date mm/dd/yy </h3>
|
||||
<h3>date { this.props.date } </h3>
|
||||
</section>
|
||||
<section className="text-left">
|
||||
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the
|
||||
industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and
|
||||
scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into
|
||||
electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of
|
||||
Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like
|
||||
Aldus
|
||||
PageMaker including versions of Lorem Ipsum.</p>
|
||||
<p>{ this.props.description }</p>
|
||||
</section>
|
||||
|
||||
</Grid>
|
||||
|
@ -28,4 +24,10 @@ class VideoPlayer extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default (VideoPlayer);
|
||||
VideoPlayer.propTypes = {
|
||||
title: PropTypes.string,
|
||||
date: PropTypes.string,
|
||||
description: PropTypes.string
|
||||
};
|
||||
|
||||
export default VideoPlayer;
|
|
@ -1,14 +1,56 @@
|
|||
import React from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import VideoPlayer from "../components/VideoPlayer";
|
||||
import { fetchVideoById } from '../utils/api';
|
||||
|
||||
class Video extends React.Component {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
currentVideo: null
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// get videoId from the URL
|
||||
const videoId = this.props.match.params.videoId;
|
||||
|
||||
// if data for videoId is already part of allVideos, setState to currentVideo
|
||||
if (this.props.allVideos && this.props.allVideos.hasOwnProperty(videoId)) {
|
||||
const currentVideo = this.props.allVideos[videoId];
|
||||
this.setState({currentVideo: currentVideo});
|
||||
} else {
|
||||
|
||||
// video is not part of allVideos, fetch it from the API
|
||||
fetchVideoById(videoId)
|
||||
.then(video => {
|
||||
this.setState({
|
||||
currentVideo: video
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const video = this.state.currentVideo;
|
||||
return (
|
||||
<section>
|
||||
<VideoPlayer/>
|
||||
{ video &&
|
||||
<VideoPlayer
|
||||
id={ video.id }
|
||||
title={ video.title }
|
||||
date = { video.date }
|
||||
description={ video.description }
|
||||
/>
|
||||
}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default (Video);
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
allVideos: state.videos.allVideos
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(Video);
|
|
@ -1,9 +1,11 @@
|
|||
import { combineReducers } from 'redux';
|
||||
|
||||
import TopicsReducer from './topics'
|
||||
import TopicsReducer from './topics';
|
||||
import VideosReducer from './videos';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
topics: TopicsReducer
|
||||
topics: TopicsReducer,
|
||||
videos: VideosReducer
|
||||
});
|
||||
|
||||
export default rootReducer;
|
22
src/reducers/videos.js
Normal file
22
src/reducers/videos.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import {
|
||||
ADD_VIDEOS_TO_STATE
|
||||
} from '../actions/action_types';
|
||||
|
||||
export default function(state={}, action) {
|
||||
switch (action.type) {
|
||||
case ADD_VIDEOS_TO_STATE:
|
||||
let clonedState = Object.assign({}, state);
|
||||
if (!clonedState.allVideos) {
|
||||
clonedState.allVideos = {};
|
||||
}
|
||||
const videosToAdd = action.payload;
|
||||
videosToAdd.forEach(video => {
|
||||
if (!clonedState.allVideos.hasOwnProperty(video.id)) {
|
||||
clonedState.allVideos[video.id] = video;
|
||||
}
|
||||
});
|
||||
return clonedState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,16 @@
|
|||
import config from '../config';
|
||||
import 'whatwg-fetch';
|
||||
|
||||
const VIDEO_KEYS = [
|
||||
"title",
|
||||
"source",
|
||||
"project",
|
||||
"topic",
|
||||
"language",
|
||||
"duration",
|
||||
"id",
|
||||
"date"
|
||||
];
|
||||
|
||||
/*
|
||||
Base function to make API calls
|
||||
|
@ -98,7 +108,7 @@ export function fetchAllDates(startRange=0, endRange=1000) {
|
|||
*/
|
||||
export function fetchVideosByX(key, value, startRange, endRange, sortKey='random') {
|
||||
const data = {
|
||||
"keys": ["editable", "modified", "title", "source", "project", "topic", "language", "duration", "id"],
|
||||
"keys": VIDEO_KEYS,
|
||||
"query": {
|
||||
"conditions": [{
|
||||
"key": key,
|
||||
|
@ -205,3 +215,7 @@ export function fetchRandomPlace() {
|
|||
export function fetchVideosByPlace(placeId, startRange=0, endRange=100, sortKey='random') {
|
||||
return fetchVideosByX('place', placeId, startRange, endRange, sortKey);
|
||||
}
|
||||
|
||||
export function fetchVideoById(id) {
|
||||
return callApi('get', {'id': id, keys: VIDEO_KEYS});
|
||||
}
|
||||
|
|
|
@ -5,5 +5,5 @@ export function getThumbnail(id, size='256') {
|
|||
}
|
||||
|
||||
export function getVideo(id, size='480') {
|
||||
return `${config.videoBase}/${id}/${size}.mp4`;
|
||||
return `${config.videoBase}/${id}/${size}p.mp4`;
|
||||
}
|
Loading…
Reference in New Issue
Block a user