dyno-bot/Dyno-web-premium-before-reset/react/serverListing/List.jsx

312 lines
10 KiB
React
Raw Normal View History

2020-09-12 20:08:48 +02:00
/* globals window document */
/* eslint-disable no-invalid-this */
import React from 'react';
import ListItem from './ListItem.jsx';
import ListItemSkeleton from './ListItemSkeleton.jsx';
export default class List extends React.Component {
constructor() {
super();
this.state = {
servers: [],
pageCount: 1,
pageLimit: 10,
activePage: 0,
isLoading: true,
hasMoreContent: true,
};
this.changePage = this.changePage.bind(this);
this.buildPages = this.buildPages.bind(this);
}
async componentWillReceiveProps(nextProps) {
if (this.props.isShowcase) {
this.setState({
servers: nextProps.servers,
});
}
if ((this.props.category !== nextProps.category) ||
(this.props.sort !== nextProps.sort) ||
(nextProps.search !== undefined) ||
(nextProps.searchQuery !== undefined)) {
await this.reloadServers();
}
}
async reloadServers() {
this.setState({ isLoading: true });
const serversInfo = await this.props.getPage(0, this.type);
this.setState({
servers: serversInfo.servers || [],
pageCount: serversInfo.pageCount,
activePage: 0,
isLoading: false,
hasMoreContent: true,
});
}
async componentWillMount() {
if (this.props.isShowcase) {
this.setState({
servers: this.props.servers,
isLoading: false,
});
return;
}
if (this.props.featured) {
this.type = 'featured';
} else if (this.props.premium) {
this.type = 'premium';
} else if (this.props.search) {
this.type = 'search';
window.onscroll = this.handleScroll;
} else {
this.type = 'regular';
}
try {
const serversInfo = await this.props.getPage(0, this.type);
this.setState({
servers: serversInfo.servers || [],
pageCount: serversInfo.pageCount,
activePage: 0,
isLoading: false,
// We return at most 20 entires from the backend. Less than that means end of the list
hasMoreContent: serversInfo.servers.length === 12,
});
} catch (e) {
this.setState({ error: 'Failed to load servers, try again later' });
}
}
handleScroll = () => {
if (this.state.isLoading ||
!this.state.hasMoreContent ||
!this.props.pagination ||
!this.props.paginationInfiniteScroll)
return;
// Checks that the page has scrolled to the bottom
if (window.innerHeight + document.documentElement.scrollTop >= document.documentElement.offsetHeight - 1) {
this.changePage(this.state.activePage + 1);
}
};
async changePage(page) {
this.setState({ isLoading: true, activePage: page });
if (this.type !== 'search' && !this.props.premium && !this.props.featured) {
const elem = document.getElementsByClassName('server-list-wrapper premium')[0];
if (elem) {
window.scrollBy({ top: elem.getBoundingClientRect().top + 50, behavior: 'smooth' });
}
}
const serversInfo = await this.props.getPage(page, this.type);
let servers;
serversInfo.servers = serversInfo.servers || [];
if (this.type === 'search') {
servers = this.state.servers.slice(0);
servers.push(...serversInfo.servers);
} else {
servers = serversInfo.servers;
}
this.setState({
servers: servers,
isLoading: false,
// We return at most 20 entires from the backend. Less than that means end of the list
hasMoreContent: serversInfo.servers.length === 12,
});
}
buildHeader() {
return false;
if (!this.props.search || this.props.isMainPage) return false;
const premiumList = <List premium={true} getPage={(page, type) => this.props.getPage(page, type, new Date().getTime())} pagination paginationCircles />;
return (
<div>
{premiumList}
</div>
);
}
buildFooter() {
if (!this.props.search) return false;
if (this.state.hasMoreContent) return false;
return (
<div>
<div className="search-footer">
<div className="separator"></div>
</div>
</div>
);
}
buildPages() {
let pages = [...Array(this.state.pageCount).keys()];
if (!this.props.paginationCircles && !this.props.paginationInfiniteScroll) {
let { activePage, pageLimit, pageCount } = this.state;
if (pageCount < pageLimit) {
return <ul className="pagination-list">{pages.map((p, i) => (
<li key={`page-${i}`} className={p === activePage ? 'active' : ''}>
<a className='page' onClick={this.changePage.bind(this, p)}>{p + 1}</a>
</li>
))}
</ul>;
}
// Places the active page around the middle if possible
let startPage = activePage - (pageLimit / 2);
if (startPage < 0) {
startPage = 0;
}
pages = [];
let i = 0;
if (activePage > (pageLimit / 2)) {
pages.push((
<li key={`page-${i++}`}>
<a className='page' onClick={this.changePage.bind(this, 0)}>1</a>
</li>
));
pages.push((<li key="page-dots1"><a>...</a></li>));
}
for (let i = 1; i <= pageLimit; i++) {
let p = startPage + i;
if (p > pageCount) {
break;
}
pages.push((
<li key={`page-${i}`} className={p === activePage + 1 ? 'active' : ''}>
<a className='page' onClick={this.changePage.bind(this, p - 1)}>{p}</a>
</li>
));
}
if (activePage < pageCount - (pageLimit / 2)) {
pages.push((<li key="page-dots2"><a>...</a></li>));
pages.push((
<li key={`page-${pageCount}`}>
<a className='page' onClick={this.changePage.bind(this, pageCount - 1)}>{pageCount}</a>
</li>
));
}
return <ul className="pagination-list">{pages}</ul>;
} else if (this.props.paginationCircles) {
return (
<ul className="pagination-list circles">
{pages.map((p, i) => {
let liClasses = '';
if (p === this.state.activePage) {
liClasses += 'active';
}
return (<li className={liClasses} key={i}>
<span className="page-circle" onClick={() => this.changePage(p)}></span>
</li>);
})}
</ul>
);
} else if (this.props.paginationInfiniteScroll) {
if (this.state.isLoading) {
return (
<div className="lds-ring"><div></div><div></div><div></div><div></div></div>
);
}
return false;
}
}
render() {
if (this.state.error) {
return <p>{this.state.error}</p>;
}
let title;
let subTitle;
let skeletonSize = 12;
let additionalClasses = '';
if (this.props.featured) {
additionalClasses += 'vertical ';
skeletonSize = 4;
title = 'Featured';
subTitle = 'Selected Dyno Server';
} else if (this.props.premium) {
additionalClasses += 'premium ';
skeletonSize = 3;
title = 'Sponsored';
subTitle = 'Our recommended servers';
} else {
additionalClasses += 'regular ';
title = (this.props.isMainPage) ? 'Discord Servers' : 'All Servers';
subTitle = 'List of all discord servers';
}
subTitle = '';
if (this.props.search) {
skeletonSize = 0;
title = 'Search Results';
}
if (this.props.isShowcase) {
title = '';
additionalClasses += 'showcase ';
}
let listNodes;
if (this.state.isLoading && this.type !== 'search') {
listNodes = [];
for (let i = 0; i < skeletonSize; i++) {
listNodes.push((<ListItemSkeleton additionalClasses={additionalClasses} key={i} />));
}
} else {
listNodes = this.state.servers.map((server, index) => <ListItem key={index} server={server} featured={this.props.featured} premium={this.props.premium} />);
}
return (
<div className={`server-list-wrapper ${additionalClasses}`}>
{this.buildHeader()}
{ title &&
<div className="list-title">
<h1 className="is-size-3">{title}</h1>
{ !this.props.isShowcase &&
<h3 className="is-size-5 has-text-grey">{subTitle}</h3>
}
</div>
}
{this.props.sortSelect}
<div className={`server-list ${additionalClasses}`}>
{listNodes}
</div>
{this.props.pagination &&
<nav className="pagination" role="navigation" aria-label="pagination">
{this.buildPages()}
</nav>
}
{this.buildFooter()}
</div>
);
}
}