252 lines
6.2 KiB
JavaScript
252 lines
6.2 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 = ({
|
|
elmts,
|
|
onKeyEnter,
|
|
selectedImdbId,
|
|
selectPoster,
|
|
onDoubleClick
|
|
}) => {
|
|
const addMoreCount = 20;
|
|
const [size, setSize] = useState(0);
|
|
const [postersPerRow, setPostersPerRow] = useState(0);
|
|
const [posterHeight, setPosterHeight] = useState(0);
|
|
|
|
const loadMore = useCallback(() => {
|
|
if (size === elmts.size) {
|
|
return;
|
|
}
|
|
|
|
const newSize =
|
|
size + addMoreCount >= elmts.size ? elmts.size : size + addMoreCount;
|
|
|
|
setSize(newSize);
|
|
}, [size, elmts.size]);
|
|
|
|
useEffect(() => {
|
|
loadMore();
|
|
}, [elmts.size, loadMore]);
|
|
|
|
const move = useCallback(
|
|
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":
|
|
onKeyEnter(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 = elmts.keySeq().findIndex(k => k === selectedImdbId);
|
|
|
|
var newIdx = idx + diff;
|
|
|
|
// Handle edge cases
|
|
if (newIdx > elmts.size - 1) {
|
|
newIdx = elmts.size - 1;
|
|
} else if (newIdx < 0) {
|
|
newIdx = 0;
|
|
}
|
|
|
|
// Get the imdbID of the newly selected item
|
|
var selectedImdb = Object.keys(elmts.toJS())[newIdx];
|
|
|
|
// Select the movie
|
|
selectPoster(selectedImdb);
|
|
if (moveFocus !== 0) {
|
|
window.scrollBy(0, moveFocus);
|
|
}
|
|
},
|
|
[
|
|
elmts,
|
|
onKeyEnter,
|
|
posterHeight,
|
|
postersPerRow,
|
|
selectPoster,
|
|
selectedImdbId
|
|
]
|
|
);
|
|
|
|
const posterCount = 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 (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 !== elmts.size}
|
|
loader={<Loader />}
|
|
>
|
|
{elmts
|
|
.slice(0, size)
|
|
.toIndexedSeq()
|
|
.map(function(el, index) {
|
|
const imdbId = el.get("imdb_id");
|
|
const selected = imdbId === selectedImdbId ? true : false;
|
|
|
|
return (
|
|
<Poster
|
|
url={el.get("poster_url")}
|
|
key={index}
|
|
selected={selected}
|
|
onClick={() => selectPoster(imdbId)}
|
|
onDoubleClick={() => 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
|
|
};
|