fix #14 create the all topics page with the filter.
This commit is contained in:
parent
3d20857fc1
commit
ed1fffca88
|
@ -5,7 +5,7 @@ import './App.css';
|
|||
import 'font-awesome/css/font-awesome.min.css';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import Home from './containers/Home';
|
||||
import TopicsList from './containers/TopicsList';
|
||||
import TopicsListContainer from './containers/TopicsListContainer';
|
||||
import store from './store/configureStore';
|
||||
import history from './history';
|
||||
import Header from "./components/Header";
|
||||
|
@ -25,7 +25,7 @@ class App extends Component {
|
|||
<Search/>
|
||||
<Switch>
|
||||
<Route exact={true} path="/" component={Home}/>
|
||||
<Route path="/topics" component={TopicsList}/>
|
||||
<Route path="/topics" component={TopicsListContainer}/>
|
||||
<Route path="/videos/:videoId" component={Video}/>
|
||||
</Switch>
|
||||
<Nav/>
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
export function getAllTopics() {
|
||||
return dispatch => {
|
||||
dispatch(startLoadingAllTopics());
|
||||
// this calls the API
|
||||
fetchAllTopics()
|
||||
.then(topics => {
|
||||
dispatch(loadedTopics(topics));
|
||||
|
@ -29,6 +30,7 @@ function startLoadingAllTopics() {
|
|||
};
|
||||
|
||||
function loadedTopics(topics) {
|
||||
// cache
|
||||
setItem('allTopics', JSON.stringify(topics));
|
||||
setItem('topicsUpdatedAt', new Date());
|
||||
return {
|
||||
|
@ -36,7 +38,9 @@ function loadedTopics(topics) {
|
|||
payload: topics
|
||||
};
|
||||
};
|
||||
|
||||
// actions :
|
||||
// - async actions
|
||||
// - simple: return action type and payload.
|
||||
export function getRandomTopicVideos(allTopics, numVideos=4) {
|
||||
return dispatch => {
|
||||
const randomTopic = getRandomTopic(allTopics);
|
||||
|
|
|
@ -3,7 +3,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import VideoItem from "./VideoItem";
|
||||
import RandomItemTitle from "./SectionHeading";
|
||||
|
||||
import {Link} from 'react-router-dom';
|
||||
class RandomTopic extends React.Component {
|
||||
|
||||
shuffleTopic() {
|
||||
|
@ -22,13 +22,14 @@ class RandomTopic extends React.Component {
|
|||
width={450}
|
||||
>
|
||||
{/* View all topics button*/}
|
||||
<button className="view-all-topics" onClick="">
|
||||
<span className="english-text">All Topics - </span>
|
||||
<span className="arabic-text"> جميع المواضيع </span>
|
||||
|
||||
</button>
|
||||
<Link to="/topics">
|
||||
<button className="view-all-topics">
|
||||
<span className="english-text">All Topics - </span>
|
||||
<span className="arabic-text"> جميع المواضيع </span>
|
||||
</button>
|
||||
</Link>
|
||||
{/* Shuffle button */}
|
||||
<button className="shuffle-button" onClick={ this.shuffleTopic.bind(this) }>
|
||||
<button className="shuffle-button" onClick={this.shuffleTopic.bind(this)}>
|
||||
<span className="english-text">Shuffle Topics - </span>
|
||||
<span className="arabic-text"> موضوع عشوائي </span>
|
||||
<i className="fa fa-random"></i>
|
||||
|
@ -41,12 +42,6 @@ class RandomTopic extends React.Component {
|
|||
{this.props.videos.map(video =>
|
||||
<VideoItem id={video.id} key={video.id} title={video.title}/>
|
||||
)}
|
||||
{/*<Link to="/topics">*/}
|
||||
{/*<span className="view-all-parents">See All Topics</span>*/}
|
||||
{/*</Link>*/}
|
||||
{/*<Link to="/topics">*/}
|
||||
{/*<span className="view-all-items"> See All videos of {this.props.topicName} ({this.props.topicCount})</span>*/}
|
||||
{/*</Link>*/}
|
||||
</Grid>
|
||||
</section>
|
||||
|
||||
|
|
57
src/components/TopicsList.js
Normal file
57
src/components/TopicsList.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
import React from 'react';
|
||||
import {Link} from 'react-router-dom';
|
||||
|
||||
class TopicsList extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
currentFilter: "",
|
||||
filteredTopics: [],
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({
|
||||
filteredTopics: this.props.allTopics
|
||||
})
|
||||
}
|
||||
|
||||
filterTopics(event) {
|
||||
// find the topics that contain that string
|
||||
// return filtered topics.
|
||||
// this.
|
||||
const input = event.target.value.toLowerCase();
|
||||
this.setState({
|
||||
filteredTopics: this.props.allTopics.filter(topic => topic.name.toLowerCase().indexOf(input) !== -1)
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<main className="topics-list">
|
||||
<section className="filter-box">
|
||||
<label className="label" htmlFor="search-topics">
|
||||
<i className="fa fa-filter"> </i>
|
||||
Filter Topics :
|
||||
<input type="text" id="search-topics" ref="filter-element" onChange={event => this.filterTopics(event)}/>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
{this.state.filteredTopics.map(topic => {
|
||||
return (
|
||||
|
||||
<section className="topic-item" key={topic.name}>
|
||||
<Link to={"/topic/" + topic.name}>
|
||||
<h3>{topic.name} ({topic.items} videos)</h3>
|
||||
</Link>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</main>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default TopicsList;
|
|
@ -1,24 +0,0 @@
|
|||
import React from 'react';
|
||||
import VideoItem from "../components/VideoItem";
|
||||
import Grid from "react-css-grid";
|
||||
import SectionHeading from "../components/SectionHeading";
|
||||
|
||||
class TopicsList extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<section>
|
||||
<SectionHeading title="Random Topic"/>
|
||||
<Grid
|
||||
width={450}
|
||||
gap={24}>
|
||||
<VideoItem/>
|
||||
<VideoItem/>
|
||||
<VideoItem/>
|
||||
<VideoItem/>
|
||||
</Grid>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TopicsList;
|
46
src/containers/TopicsListContainer.js
Normal file
46
src/containers/TopicsListContainer.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
import React from 'react';
|
||||
import SectionHeading from "../components/SectionHeading";
|
||||
import TopicsList from "../components/TopicsList";
|
||||
import {getAllTopics} from "../actions/topics";
|
||||
import {connect} from 'react-redux'
|
||||
class TopicsListContainer extends React.Component {
|
||||
componentDidMount(){
|
||||
// if the data isn't there call the action.
|
||||
if (!this.props.allTopics){
|
||||
this.props.getAllTopics();
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<section>
|
||||
<SectionHeading title="All Topics"/>
|
||||
{/* TODO: get the topics objects*/}
|
||||
<TopicsList allTopics={this.props.allTopics}/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// function ->
|
||||
// - recieves the global state.
|
||||
// - maps the global state to the local props of the component.
|
||||
// - maps the global actions to the local props of the component.
|
||||
/**
|
||||
*
|
||||
* @param state
|
||||
* @returns object
|
||||
*/
|
||||
const mapStateToProps = state => ({
|
||||
allTopics: state.topics.allTopics,
|
||||
loadingAllTopics: state.topics.loadingAllTopics,
|
||||
});
|
||||
/**
|
||||
*
|
||||
* @param dispatch: Redux's action dispatcher.
|
||||
* @returns {{}}
|
||||
*/
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
getAllTopics: () => dispatch(getAllTopics()),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TopicsListContainer);
|
|
@ -80,4 +80,4 @@ a {
|
|||
.normal-link{
|
||||
text-decoration: underline;
|
||||
color: $normal-link-color;
|
||||
}
|
||||
}
|
||||
|
|
24
src/stylesheets/items-list.scss
Normal file
24
src/stylesheets/items-list.scss
Normal file
|
@ -0,0 +1,24 @@
|
|||
@import "variables";
|
||||
@import "globals";
|
||||
|
||||
#search-topics {
|
||||
padding: 1em;
|
||||
border: .5px solid gray;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.topic-item {
|
||||
h3 {
|
||||
text-align: left;
|
||||
padding: 1em;
|
||||
font-size: 30px;
|
||||
}
|
||||
border-left: 2px solid black;
|
||||
border-bottom: 6px solid black;
|
||||
max-width: 1000px;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
|
@ -37,4 +37,12 @@
|
|||
.search-box::placeholder {
|
||||
font-size: .75em;
|
||||
}
|
||||
}
|
||||
|
||||
// filter
|
||||
.filter-box{
|
||||
background: $light-gray-color;
|
||||
padding: 1em;
|
||||
margin-bottom: 2em;
|
||||
text-align: left;
|
||||
}
|
|
@ -7,4 +7,5 @@
|
|||
@import "video-item";
|
||||
@import "video-player";
|
||||
@import "footer";
|
||||
@import "items-list";
|
||||
@import "~video-react/styles/scss/video-react";
|
|
@ -30,7 +30,7 @@ export function callApi(action, data) {
|
|||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => response.json()) // convert the response to json (instead of response object).
|
||||
.then(data => {
|
||||
if (data.status.code === 200) {
|
||||
return data.data;
|
||||
|
@ -50,6 +50,7 @@ export function callApi(action, data) {
|
|||
@returns {Promise<Array>} array of topic objects like: {'name': '<topicName>', 'items': <no_of_items>}
|
||||
*/
|
||||
export function fetchAllTopics(startRange=0, endRange=1000) {
|
||||
// the object that we need to send to the api
|
||||
const data = {
|
||||
"query": {
|
||||
"conditions": [],
|
||||
|
@ -209,7 +210,7 @@ export function fetchRandomPlace() {
|
|||
"operator": "-"
|
||||
}]
|
||||
};
|
||||
return callApi('findPlaces', data).then(data => data.items[0]);
|
||||
return callApi('findPlaces', data).then(data => data.items[0]);
|
||||
}
|
||||
|
||||
export function fetchVideosByPlace(placeId, startRange=0, endRange=100, sortKey='random') {
|
||||
|
|
Loading…
Reference in New Issue
Block a user