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
};