224 lines
5.9 KiB
JavaScript
224 lines
5.9 KiB
JavaScript
import React, { useState, useEffect, useCallback } from "react"
|
|
import PropTypes from "prop-types"
|
|
import { OrderedMap, Map } from "immutable"
|
|
import fuzzy from "fuzzy";
|
|
import InfiniteScroll from "react-infinite-scroll-component";
|
|
|
|
import ListFilter from "./filter"
|
|
import ExplorerOptions from "./explorerOptions"
|
|
import Poster from "./poster"
|
|
|
|
import Loader from "../loader/loader"
|
|
|
|
const ListPosters = (props) => {
|
|
if (props.loading) {
|
|
return (<Loader />);
|
|
}
|
|
|
|
let elmts = props.data;
|
|
const listSize = elmts !== undefined ? elmts.size : 0;
|
|
|
|
// Filter the list of elements
|
|
if (props.filter !== "") {
|
|
elmts = elmts.filter((v) => fuzzy.test(props.filter, v.get("title")));
|
|
} else {
|
|
elmts = elmts.slice(0, listSize.items);
|
|
}
|
|
|
|
// Chose when to display filter / explore options
|
|
let displayFilter = true;
|
|
if ((props.params
|
|
&& props.params.category
|
|
&& props.params.category !== ""
|
|
&& props.params.source
|
|
&& props.params.source !== "")
|
|
|| (listSize === 0)) {
|
|
displayFilter = false;
|
|
}
|
|
|
|
|
|
let displayExplorerOptions = false;
|
|
if (listSize !== 0) {
|
|
displayExplorerOptions = !displayFilter;
|
|
}
|
|
|
|
return (
|
|
<div className="col-4 col-md-8 px-1">
|
|
{displayFilter &&
|
|
<ListFilter
|
|
updateFilter={props.updateFilter}
|
|
placeHolder={props.placeHolder}
|
|
/>
|
|
}
|
|
<ExplorerOptions
|
|
type={props.type}
|
|
display={displayExplorerOptions}
|
|
params={props.params}
|
|
options={props.exploreOptions}
|
|
/>
|
|
<Posters
|
|
elmts={elmts}
|
|
loading={props.loading}
|
|
selectedImdbId={props.selectedImdbId}
|
|
selectPoster={props.onClick}
|
|
onDoubleClick={props.onDoubleClick}
|
|
onKeyEnter={props.onKeyEnter}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
ListPosters.propTypes = {
|
|
data: PropTypes.instanceOf(OrderedMap),
|
|
onClick: PropTypes.func,
|
|
onDoubleClick: PropTypes.func,
|
|
onKeyEnter: PropTypes.func,
|
|
selectedImdbId: PropTypes.string,
|
|
loading: PropTypes.bool.isRequired,
|
|
params: PropTypes.object.isRequired,
|
|
exploreOptions: PropTypes.instanceOf(Map),
|
|
type: PropTypes.string.isRequired,
|
|
placeHolder: PropTypes.string.isRequired,
|
|
updateFilter: PropTypes.func.isRequired,
|
|
filter: PropTypes.string,
|
|
};
|
|
|
|
export default ListPosters;
|
|
|
|
const Posters = (props) => {
|
|
const addMoreCount = 20;
|
|
const [size, setSize] = useState(0);
|
|
const [postersPerRow, setPostersPerRow] = useState(0);
|
|
const [posterHeight, setPosterHeight] = useState(0);
|
|
|
|
const loadMore = () => {
|
|
if ((size === props.elmts.size)) { return }
|
|
|
|
const newSize = (((size + addMoreCount) >= props.elmts.size)
|
|
? props.elmts.size
|
|
: size + addMoreCount);
|
|
|
|
setSize(newSize);
|
|
}
|
|
|
|
useEffect(() => {
|
|
loadMore()
|
|
}, [props.elmts.size]);
|
|
|
|
const move = (event) => {
|
|
// Only run the function if nothing else if actively focused
|
|
if (document.activeElement.tagName.toLowerCase() !== "body") { return }
|
|
|
|
let diff = 0;
|
|
let moveFocus = 0;
|
|
switch (event.key) {
|
|
case "Enter":
|
|
props.onKeyEnter(props.selectedImdbId);
|
|
return;
|
|
case "l":
|
|
diff = 1;
|
|
break;
|
|
case "h":
|
|
diff = -1;
|
|
break;
|
|
case "k":
|
|
diff = -1*postersPerRow;
|
|
moveFocus = -1*posterHeight;
|
|
break;
|
|
case "j":
|
|
diff = postersPerRow;
|
|
moveFocus = posterHeight;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
// Get the index of the currently selected item
|
|
const idx = props.elmts.keySeq().findIndex(k => k === props.selectedImdbId);
|
|
|
|
var newIdx = idx + diff;
|
|
|
|
// Handle edge cases
|
|
if (newIdx > props.elmts.size -1) {
|
|
newIdx = props.elmts.size -1;
|
|
} else if (newIdx < 0) {
|
|
newIdx = 0;
|
|
}
|
|
|
|
// Get the imdbID of the newly selected item
|
|
var selectedImdb = Object.keys(props.elmts.toJS())[newIdx];
|
|
|
|
// Select the movie
|
|
props.selectPoster(selectedImdb);
|
|
if (moveFocus !== 0) {
|
|
window.scrollBy(0, moveFocus);
|
|
}
|
|
}
|
|
|
|
const posterCount = useCallback(node => {
|
|
if (node === null) { return }
|
|
const parentWidth = node.getBoundingClientRect().width;
|
|
const childContainer = node.getElementsByClassName("img-thumbnail");
|
|
let childWidth = 0;
|
|
let posterHeight = 0;
|
|
if ((childContainer !== null) && (childContainer.item(0) !== null)) {
|
|
const child = childContainer.item(0);
|
|
childWidth = child.getBoundingClientRect().width + child.getBoundingClientRect().left;
|
|
posterHeight = child.getBoundingClientRect().height;
|
|
}
|
|
|
|
let numberPerRow = (childWidth >= parentWidth) ? 1 : Math.floor(parentWidth/childWidth);
|
|
setPostersPerRow(numberPerRow);
|
|
setPosterHeight(posterHeight);
|
|
})
|
|
|
|
useEffect(() => {
|
|
document.onkeypress = move;
|
|
return () => {
|
|
document.onkeypress = null;
|
|
}
|
|
}, [move])
|
|
|
|
if (props.elmts.size === 0) {
|
|
return (
|
|
<div className="jumbotron">
|
|
<h2>No result</h2>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div ref={posterCount}>
|
|
<InfiniteScroll
|
|
className="poster-list d-flex flex-column flex-sm-row flex-sm-wrap justify-content-around"
|
|
dataLength={size}
|
|
next={loadMore}
|
|
hasMore={(size !== props.elmts.size)}
|
|
loader={<Loader />}
|
|
>
|
|
{props.elmts.slice(0, size).toIndexedSeq().map(function(el, index) {
|
|
const imdbId = el.get("imdb_id");
|
|
const selected = (imdbId === props.selectedImdbId) ? true : false;
|
|
|
|
return (
|
|
<Poster
|
|
url={el.get("poster_url")}
|
|
key={index}
|
|
selected={selected}
|
|
onClick={() => props.selectPoster(imdbId)}
|
|
onDoubleClick={() => props.onDoubleClick(imdbId)}
|
|
/>
|
|
)
|
|
} ,this)}
|
|
</InfiniteScroll>
|
|
</div>
|
|
);
|
|
}
|
|
Posters.propTypes = {
|
|
elmts: PropTypes.instanceOf(OrderedMap),
|
|
selectedImdbId: PropTypes.string,
|
|
loading: PropTypes.bool.isRequired,
|
|
onDoubleClick: PropTypes.func,
|
|
onKeyEnter: PropTypes.func,
|
|
selectPoster: PropTypes.func,
|
|
};
|