1
0
mirror of https://github.com/mgerb/classic-wow-forums synced 2026-01-09 00:42:47 +00:00

client - order by functionality done

This commit is contained in:
2018-01-22 19:33:46 -06:00
parent 259c270c51
commit a8e82cdcf5
5 changed files with 92 additions and 59 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 B

View File

@@ -10,6 +10,8 @@ import { Oauth, pagination } from '../../util';
import './forum.scss';
const stickyImage = require('../../assets/sticky.gif');
const upArrow = require('../../assets/arrow-up.gif');
const downArrow = require('../../assets/arrow-down.gif');
interface Props extends RouteComponentProps<any> {
userStore: UserStore;
@@ -26,7 +28,17 @@ interface RouteParams {
categoryId: number;
page: number;
threadsPerPage: number;
sortBy: string;
sortBy: ColumnHeader;
sortOrder: 'asc' | 'desc';
}
// TODO: refactor this on back end to match UI
enum ColumnHeader {
subject = 'Subject',
author = 'Author',
replies = 'Replies',
views = 'Views',
lastPost= 'Last Post',
}
@inject('userStore')
@@ -43,17 +55,18 @@ export class Forum extends React.Component<Props, State> {
}
// easier way to get route params - will provide default values if null
private get routeParams(): RouteParams {
private routeParams(props: Props = this.props): RouteParams {
return {
categoryId: parseInt(this.props.match.params['id'], 10),
page: parseInt(this.props.match.params['page'], 10) || 1,
threadsPerPage: parseInt(this.props.match.params['threadsPerPage'], 10) || 25,
sortBy: this.props.match.params['sortBy'] || 'Latest Reply',
categoryId: parseInt(props.match.params['id'], 10),
page: parseInt(props.match.params['page'], 10) || 1,
threadsPerPage: parseInt(props.match.params['threadsPerPage'], 10) || 25,
sortBy: props.match.params['sortBy'] || ColumnHeader.lastPost,
sortOrder: props.match.params['sortOrder'] || 'desc',
};
}
componentDidMount() {
this.getThreads(this.routeParams.categoryId);
this.getThreads(this.routeParams().categoryId);
}
// update the page if the route params change
@@ -61,22 +74,20 @@ export class Forum extends React.Component<Props, State> {
if (this.props.match.params['id'] !== nextProps.match.params['id']) {
this.getThreads(nextProps.match.params['id']);
} else {
// have to grab params from next props
const page = parseInt(nextProps.match.params['page'], 10) || 1;
const threadsPerPage = parseInt(nextProps.match.params['threadsPerPage'], 10) || 25;
this.processThreads(this.state.threads, page, threadsPerPage);
this.processThreads(this.state.threads, nextProps);
}
}
// fetch threads from server
private async getThreads(categoryId: number) {
const threads = await ThreadService.getCategoryThreads(categoryId);
this.processThreads(threads, this.routeParams.page, this.routeParams.threadsPerPage);
this.processThreads(threads);
}
// process threads and set state
private processThreads(unorderedThreads: ThreadModel[], page: number, threadsPerPage: number): void {
const threads = this.orderBy(unorderedThreads);
private processThreads(unorderedThreads: ThreadModel[], props: Props = this.props): void {
const { threadsPerPage, page } = this.routeParams(props);
const threads = this.orderBy(unorderedThreads, props);
const numPages = Math.ceil(threads.length / threadsPerPage);
const threadIndex = (page - 1) * threadsPerPage;
@@ -87,13 +98,24 @@ export class Forum extends React.Component<Props, State> {
);
}
// TODO:
private orderBy(threads: ThreadModel[]) {
return orderBy(threads, ['sticky', 'updated_at'], ['desc', 'desc']);
private orderBy(threads: ThreadModel[], props: Props) {
const { sortBy, sortOrder } = this.routeParams(props);
const titleMap: any = {
[ColumnHeader.subject]: (t: any) => t.title.toLowerCase(),
[ColumnHeader.author]: (t: any) => {
return t.user.character_name ? t.user.character_name.toLowerCase() : t.user.battletag.toLowerCase();
},
[ColumnHeader.replies]: 'reply_count',
[ColumnHeader.views]: 'view_count',
[ColumnHeader.lastPost]: 'updated_at',
};
// always sort sticky to top
return orderBy(threads, ['sticky', titleMap[sortBy]], ['desc', sortOrder]);
}
private navigateHere(categoryId: number, page: number, threadsPerPage: number, sortBy: string) {
const url = `/f/${categoryId}/${page}/${threadsPerPage}/${sortBy}`;
private navigateHere(categoryId: number, page: number, threadsPerPage: number, sortBy: ColumnHeader, sortOrder: 'asc' | 'desc') {
const url = `/f/${categoryId}/${page}/${threadsPerPage}/${sortBy}/${sortOrder}`;
this.props.history.push(url);
}
@@ -108,14 +130,14 @@ export class Forum extends React.Component<Props, State> {
private onNewTopicClose(cancel: boolean) {
this.setState({ showEditor: false });
if (!cancel) {
this.getThreads(this.routeParams.categoryId);
this.getThreads(this.routeParams().categoryId);
}
}
renderHeader() {
return (
<div className="forum-header">
<ForumNav categoryId={this.routeParams.categoryId} {...this.props}/>
<ForumNav categoryId={this.routeParams().categoryId} {...this.props}/>
<div style={{ height: '100%' }}>
<LoginButton onNavigate={dest => this.props.history.push(dest)}/>
</div>
@@ -145,15 +167,14 @@ export class Forum extends React.Component<Props, State> {
);
}
renderCell(content: JSX.Element | string, style: any, center?: boolean, header?: boolean) {
renderCell(content: JSX.Element | string, style: any, center?: boolean) {
let classNames: string = '';
classNames += center ? ' forum-cell--center' : '';
classNames += header ? ' forum-cell--header' : ' forum-cell--body';
return <div className={`forum-cell flex-1 ${classNames}`} style={style}>{content}</div>;
return <div className={`forum-cell flex-1 forum-cell--body ${classNames}`} style={style}>{content}</div>;
}
renderThreadRows() {
const { categoryId } = this.routeParams;
const { categoryId } = this.routeParams();
return this.state.pageThreads.map((thread, index) => {
const authorBluePost = thread.user.permissions === 'admin' ? 'blue' : '';
const lastReplyBluePost = thread.last_reply.permissions === 'admin' ? 'blue' : '';
@@ -180,11 +201,11 @@ export class Forum extends React.Component<Props, State> {
}
renderThreadsPerPageDropdown() {
const { categoryId, sortBy } = this.routeParams;
const { categoryId, sortBy, sortOrder } = this.routeParams();
return (
<select style={{ margin: '0 5px' }}
value={this.routeParams.threadsPerPage}
onChange={e => this.navigateHere(categoryId, 1, parseInt(e.target.value, 10), sortBy)}
value={this.routeParams().threadsPerPage}
onChange={e => this.navigateHere(categoryId, 1, parseInt(e.target.value, 10), sortBy, sortOrder)}
>
<option value={25}>25</option>
<option value={50}>50</option>
@@ -193,44 +214,48 @@ export class Forum extends React.Component<Props, State> {
);
}
// TOOD:
renderSortByDropdown() {
return (
<select style={{ margin: '0 5px' }}>
<option>Latest Reply</option>
<option>Subject</option>
<option>Author</option>
<option># of Replies</option>
<option># of Views</option>
<option>Creation Date</option>
</select>
);
}
renderHeaderFooter() {
const { categoryId, sortBy, threadsPerPage } = this.routeParams;
const { categoryId, sortBy, threadsPerPage, sortOrder } = this.routeParams();
return (
<div className="forum-row forum-row--header">
<div className="forum-cell forum-cell--header forum-cell--header-footer flex-1">
<div>
<span style={{ marginRight: '10px' }}>Page:</span>
<PaginationLinks
activePage={this.routeParams.page}
activePage={this.routeParams().page}
pageLinks={this.state.pageLinks}
onPageSelect={page => this.navigateHere(categoryId, page, threadsPerPage, sortBy)}
onPageSelect={page => this.navigateHere(categoryId, page, threadsPerPage, sortBy, sortOrder)}
/>
</div>
<div>
<b>Threads/Page:</b>
{this.renderThreadsPerPageDropdown()}
<b>Sort by:</b>
{this.renderSortByDropdown()}
</div>
</div>
</div>
);
}
renderSortingArrow(show: boolean, sortOrder: string) {
const imgSrc = sortOrder === 'asc' ? upArrow : downArrow;
return show ? <img src={imgSrc}/> : null;
}
renderHeaderCell(columnHeader: ColumnHeader, style: any, center: boolean) {
const { categoryId, page, threadsPerPage, sortBy, sortOrder } = this.routeParams();
const newSortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
const centerClass = center ? 'forum-cell--center' : '';
return (
<div className={`forum-cell forum-cell--header flex-1 ${centerClass}`} style={style}>
<a onClick={() => this.navigateHere(categoryId, page, threadsPerPage, columnHeader, newSortOrder)}>
<span>{columnHeader}</span>
{this.renderSortingArrow(sortBy === columnHeader, sortOrder)}
</a>
</div>
);
}
renderTable() {
return (
<div style={{ padding: '0 3px' }}>
@@ -241,12 +266,14 @@ export class Forum extends React.Component<Props, State> {
{/* column headers */}
<div className="forum-row forum-row--header">
{this.renderCell(<img src={require('../../assets/flag.gif')}/>, { maxWidth: '50px' }, true, true)}
{this.renderCell(<a>Subject</a>, { minWidth: '200px' }, false, true)}
{this.renderCell(<a>Author</a>, { maxWidth: '150px' }, true, true)}
{this.renderCell(<a>Replies</a>, { maxWidth: '150px' }, true, true)}
{this.renderCell(<a>Views</a>, { maxWidth: '150px' }, true, true)}
{this.renderCell(<a>Last Post</a>, { maxWidth: '200px' }, true, true)}
<div className={`forum-cell forum-cell--header flex-1 forum-cell--center`} style={{ maxWidth: '50px' }}>
<img src={require('../../assets/flag.gif')}/>
</div>
{this.renderHeaderCell(ColumnHeader.subject, { minWidth: '200px' }, false)}
{this.renderHeaderCell(ColumnHeader.author, { maxWidth: '150px' }, true)}
{this.renderHeaderCell(ColumnHeader.replies, { maxWidth: '150px' }, true)}
{this.renderHeaderCell(ColumnHeader.views, { maxWidth: '150px' }, true)}
{this.renderHeaderCell(ColumnHeader.lastPost, { maxWidth: '200px' }, true)}
</div>
{/* table body */}
@@ -265,7 +292,7 @@ export class Forum extends React.Component<Props, State> {
render() {
return (
<ScrollToTop>
{this.state.showEditor && <Editor categoryId={this.routeParams.categoryId}
{this.state.showEditor && <Editor categoryId={this.routeParams().categoryId}
onClose={cancel => this.onNewTopicClose(cancel)}/>}
{this.renderHeader()}
{this.renderBody()}

View File

@@ -91,12 +91,18 @@ export class Home extends React.Component<Props, State> {
<ContentContainer>
<img src={header_forms} />
<div>
<b>Welcome to the World of Warcraft community forums!</b>
<b>Welcome to the unofficial Classic WoW Forums!</b>
</div>
<p>
Blizzard provides the World of Warcraft community forums for its player to chat, exchange ideas, and submit feedback. Posting on
the World of Warcraft community forums requires a World of Warcraft account. Only customers are allowed to post on these forums,
but anyone can read them. Please note that you must adhere to the Forum Guidelines if you wish to post on the forums.
This site is made with the intention of providing a web forum
that replicates the World of Warcraft forums as they were
back in 2005.
</p>
<p>
<b>DISCLAIMER:</b> This site is in no way affiliated with Blizzard Entertainment, Inc. All rights reserved.
World of Warcraft, Warcraft and Blizzard Entertainment are trademarks or registered trademarks of Blizzard Entertainment,
Inc. in the U.S. and/or other countries.
</p>
<div className="topic-container">

View File

@@ -44,7 +44,7 @@ export class Routes extends React.Component<Props, State> {
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/realms" component={Realms} />
<Route exact path="/f/:id/:page?/:threadsPerPage?/:sortBy?" component={Forum} />
<Route exact path="/f/:id/:page?/:threadsPerPage?/:sortBy?/:sortOrder?" component={Forum} />
<Route exact path="/t/:categoryId/:threadId/:page?" component={Thread} />
<Route exact path="/oauth" component={Oauth} />
<Route exact path="/user-account" component={UserAccount} />