123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- import React from 'react'; // jshint ignore:line
- import Button from 'misago/components/button'; // jshint ignore:line
- import { compareGlobalWeight, compareWeight } from 'misago/components/threads/compare'; // jshint ignore:line
- import Container from 'misago/components/threads/container'; // jshint ignore:line
- import Header from 'misago/components/threads/header'; // jshint ignore:line
- import { diffThreads, getModerationActions, getPageTitle, getTitle } from 'misago/components/threads/utils'; // jshint ignore:line
- import ThreadsList from 'misago/components/threads-list'; // jshint ignore:line
- import ThreadsListEmpty from 'misago/components/threads/list-empty'; // jshint ignore:line
- import WithDropdown from 'misago/components/with-dropdown'; // jshint ignore:line
- import misago from 'misago/index';
- import * as select from 'misago/reducers/selection'; // jshint ignore:line
- import { append, deleteThread, hydrate, patch } from 'misago/reducers/threads'; // jshint ignore:line
- import ajax from 'misago/services/ajax';
- import polls from 'misago/services/polls';
- import snackbar from 'misago/services/snackbar';
- import store from 'misago/services/store';
- import title from 'misago/services/page-title';
- import * as sets from 'misago/utils/sets'; // jshint ignore:line
- export default class extends WithDropdown {
- constructor(props) {
- super(props);
- this.state = {
- isMounted: true,
- isLoaded: false,
- isBusy: false,
- diff: {
- results: []
- },
- moderation: [],
- busyThreads: [],
- dropdown: false,
- subcategories: [],
- count: 0,
- more: 0,
- page: 1,
- pages: 1
- };
- let category = this.getCategory();
- if (misago.has('THREADS')) {
- this.initWithPreloadedData(category, misago.get('THREADS'));
- } else {
- this.initWithoutPreloadedData(category);
- }
- }
- getCategory() {
- if (!this.props.route.category.special_role) {
- return this.props.route.category.id;
- } else {
- return null;
- }
- }
- initWithPreloadedData(category, data) {
- this.state = Object.assign(this.state, {
- moderation: getModerationActions(data.results),
- subcategories: data.subcategories,
- count: data.count,
- more: data.more,
- page: data.page,
- pages: data.pages
- });
- this.startPolling(category);
- }
- initWithoutPreloadedData(category) {
- this.loadThreads(category);
- }
- loadThreads(category, page=1) {
- ajax.get(this.props.options.api, {
- category: category,
- list: this.props.route.list.type,
- page: page || 1
- }, 'threads').then((data) => {
- if (!this.state.isMounted) {
- // user changed route before loading completion
- return;
- }
- if (page === 1) {
- store.dispatch(hydrate(data.results));
- } else {
- store.dispatch(append(data.results, this.getSorting()));
- }
- this.setState({
- isLoaded: true,
- isBusy: false,
- moderation: getModerationActions(store.getState().threads),
- subcategories: data.subcategories,
- count: data.count,
- more: data.more,
- page: data.page,
- pages: data.pages
- });
- this.startPolling(category);
- }, (rejection) => {
- snackbar.apiError(rejection);
- });
- }
- startPolling(category) {
- polls.start({
- poll: 'threads',
- url: this.props.options.api,
- data: {
- category: category,
- list: this.props.route.list.type
- },
- frequency: 120 * 1000,
- update: this.pollResponse
- });
- }
- componentDidMount() {
- this.setPageTitle();
- if (misago.has('THREADS')) {
- // unlike in other components, routes are root components for threads
- // so we can't dispatch store action from constructor
- store.dispatch(hydrate(misago.pop('THREADS').results));
- this.setState({
- isLoaded: true
- });
- }
- store.dispatch(select.none());
- }
- componentWillUnmount() {
- this.state.isMounted = false;
- polls.stop('threads');
- }
- getTitle() {
- if (this.props.options.title) {
- return this.props.options.title;
- }
- return getTitle(this.props.route);
- }
- setPageTitle() {
- if (this.props.route.category.level || !misago.get('THREADS_ON_INDEX')) {
- title.set(getPageTitle(this.props.route));
- } else if (this.props.options.title) {
- title.set(this.props.options.title);
- } else {
- if (misago.get('SETTINGS').forum_index_title) {
- document.title = misago.get('SETTINGS').forum_index_title;
- } else {
- document.title = misago.get('SETTINGS').forum_name;
- }
- }
- }
- getSorting() {
- if (this.props.route.category.level) {
- return compareWeight;
- } else {
- return compareGlobalWeight;
- }
- }
- /* jshint ignore:start */
- // AJAX
- loadMore = () => {
- this.setState({
- isBusy: true
- });
- this.loadThreads(this.getCategory(), this.state.page + 1);
- };
- pollResponse = (data) => {
- this.setState({
- diff: Object.assign({}, data, {
- results: diffThreads(this.props.threads, data.results)
- })
- });
- };
- addThreads = (threads) => {
- store.dispatch(append(threads, this.getSorting()));
- };
- applyDiff = () => {
- this.addThreads(this.state.diff.results);
- this.setState(Object.assign({}, this.state.diff, {
- moderation: getModerationActions(store.getState().threads),
- diff: {
- results: []
- }
- }));
- };
- // Thread state utils
- freezeThread = (thread) => {
- this.setState(function(currentState) {
- return {
- busyThreads: sets.toggle(currentState.busyThreads, thread)
- };
- });
- };
- updateThread = (thread) => {
- store.dispatch(patch(thread, thread, this.getSorting()));
- };
- deleteThread = (thread) => {
- store.dispatch(deleteThread(thread));
- };
- /* jshint ignore:end */
- getMoreButton() {
- if (this.state.more) {
- /* jshint ignore:start */
- return (
- <div className="pager-more">
- <Button
- className="btn btn-default btn-outline"
- loading={this.state.isBusy || this.state.busyThreads.length}
- onClick={this.loadMore}
- >
- {gettext("Show more")}
- </Button>
- </div>
- );
- /* jshint ignore:end */
- } else {
- return null;
- }
- }
- getClassName() {
- let className = 'page page-threads';
- className += ' page-threads-' + this.props.route.list.type;
- if (this.props.route.category.css_class) {
- className += ' page-' + this.props.route.category.css_class;
- }
- return className;
- }
- render() {
- /* jshint ignore:start */
- return (
- <div className={this.getClassName()}>
- <Header
- categories={this.props.route.categoriesMap}
- disabled={!this.state.isLoaded}
- startThread={this.props.options.startThread}
- threads={this.props.threads}
- title={this.getTitle()}
- toggleNav={this.toggleNav}
- route={this.props.route}
- user={this.props.user}
- />
- <Container
- route={this.props.route}
- subcategories={this.state.subcategories}
- user={this.props.user}
- pageLead={this.props.options.pageLead}
- threads={this.props.threads}
- threadsCount={this.state.count}
- moderation={this.state.moderation}
- selection={this.props.selection}
- busyThreads={this.state.busyThreads}
- addThreads={this.addThreads}
- freezeThread={this.freezeThread}
- deleteThread={this.deleteThread}
- updateThread={this.updateThread}
- isLoaded={this.state.isLoaded}
- isBusy={this.state.isBusy}
- >
- <ThreadsList
- category={this.props.route.category}
- categories={this.props.route.categoriesMap}
- list={this.props.route.list}
- selection={this.props.selection}
- threads={this.props.threads}
- diffSize={this.state.diff.results.length}
- applyDiff={this.applyDiff}
- showOptions={!!this.props.user.id}
- isLoaded={this.state.isLoaded}
- busyThreads={this.state.busyThreads}
- >
- <ThreadsListEmpty
- category={this.props.route.category}
- emptyMessage={this.props.options.emptyMessage}
- list={this.props.route.list}
- />
- </ThreadsList>
- {this.getMoreButton()}
- </Container>
- </div>
- );
- /* jshint ignore:end */
- }
- }
|