From a8e82cdcf54e8c58021baa68ba434f84f5853c5a Mon Sep 17 00:00:00 2001 From: Mitchell Gerber Date: Mon, 22 Jan 2018 19:33:46 -0600 Subject: [PATCH] client - order by functionality done --- client/app/assets/arrow-down.gif | Bin 0 -> 67 bytes client/app/assets/arrow-up.gif | Bin 0 -> 67 bytes client/app/pages/forum/forum.tsx | 135 ++++++++++++++++++------------- client/app/pages/home/home.tsx | 14 +++- client/app/routes.tsx | 2 +- 5 files changed, 92 insertions(+), 59 deletions(-) create mode 100644 client/app/assets/arrow-down.gif create mode 100644 client/app/assets/arrow-up.gif diff --git a/client/app/assets/arrow-down.gif b/client/app/assets/arrow-down.gif new file mode 100644 index 0000000000000000000000000000000000000000..e9ac9b9e701b734654889c5ed65976e926bc998e GIT binary patch literal 67 zcmZ?wbhEHbCMz|5cn5(UXKFp11zU75|!sGPxj R)F87qB6~{Zlm(&;)&K*>5LW;I literal 0 HcmV?d00001 diff --git a/client/app/assets/arrow-up.gif b/client/app/assets/arrow-up.gif new file mode 100644 index 0000000000000000000000000000000000000000..207f253a30b223881c1b81386b6457efed92b2e3 GIT binary patch literal 67 zcmZ?wbhEHbCMz|5cn5(UXKFp12`Z>VNGB_+UQ R { 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 { } // 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 { 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 { ); } - // 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 { private onNewTopicClose(cancel: boolean) { this.setState({ showEditor: false }); if (!cancel) { - this.getThreads(this.routeParams.categoryId); + this.getThreads(this.routeParams().categoryId); } } renderHeader() { return (
- +
this.props.history.push(dest)}/>
@@ -145,15 +167,14 @@ export class Forum extends React.Component { ); } - 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
{content}
; + return
{content}
; } 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 { } renderThreadsPerPageDropdown() { - const { categoryId, sortBy } = this.routeParams; + const { categoryId, sortBy, sortOrder } = this.routeParams(); return ( - - - - - - - - ); - } - renderHeaderFooter() { - const { categoryId, sortBy, threadsPerPage } = this.routeParams; + const { categoryId, sortBy, threadsPerPage, sortOrder } = this.routeParams(); return (
Page: this.navigateHere(categoryId, page, threadsPerPage, sortBy)} + onPageSelect={page => this.navigateHere(categoryId, page, threadsPerPage, sortBy, sortOrder)} />
Threads/Page: {this.renderThreadsPerPageDropdown()} - Sort by: - {this.renderSortByDropdown()}
); } + renderSortingArrow(show: boolean, sortOrder: string) { + const imgSrc = sortOrder === 'asc' ? upArrow : downArrow; + return show ? : 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 ( + + ); + } + renderTable() { return (
@@ -241,12 +266,14 @@ export class Forum extends React.Component { {/* column headers */}
- {this.renderCell(, { maxWidth: '50px' }, true, true)} - {this.renderCell(Subject, { minWidth: '200px' }, false, true)} - {this.renderCell(Author, { maxWidth: '150px' }, true, true)} - {this.renderCell(Replies, { maxWidth: '150px' }, true, true)} - {this.renderCell(Views, { maxWidth: '150px' }, true, true)} - {this.renderCell(Last Post, { maxWidth: '200px' }, true, true)} +
+ +
+ {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)}
{/* table body */} @@ -265,7 +292,7 @@ export class Forum extends React.Component { render() { return ( - {this.state.showEditor && this.onNewTopicClose(cancel)}/>} {this.renderHeader()} {this.renderBody()} diff --git a/client/app/pages/home/home.tsx b/client/app/pages/home/home.tsx index dd1262a..130c853 100644 --- a/client/app/pages/home/home.tsx +++ b/client/app/pages/home/home.tsx @@ -91,12 +91,18 @@ export class Home extends React.Component {
- Welcome to the World of Warcraft community forums! + Welcome to the unofficial Classic WoW Forums!

- 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. +

+ +

+ DISCLAIMER: 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.

diff --git a/client/app/routes.tsx b/client/app/routes.tsx index b280837..f010f68 100644 --- a/client/app/routes.tsx +++ b/client/app/routes.tsx @@ -44,7 +44,7 @@ export class Routes extends React.Component { - +