route.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import React from 'react'; // jshint ignore:line
  2. import Button from 'misago/components/button'; // jshint ignore:line
  3. import CategoryPicker from 'misago/components/threads/category-picker'; // jshint ignore:line
  4. import Header from 'misago/components/threads/header'; // jshint ignore:line
  5. import ThreadsListEmpty from 'misago/components/threads/list-empty'; // jshint ignore:line
  6. import { CompactNav } from 'misago/components/threads/navs'; // jshint ignore:line
  7. import { getPageTitle, getTitle } from 'misago/components/threads/utils';
  8. import ThreadsList from 'misago/components/threads-list/root'; // jshint ignore:line
  9. import WithDropdown from 'misago/components/with-dropdown';
  10. import misago from 'misago/index';
  11. import { append, hydrate } from 'misago/reducers/threads'; // jshint ignore:line
  12. import ajax from 'misago/services/ajax';
  13. import snackbar from 'misago/services/snackbar';
  14. import store from 'misago/services/store';
  15. import title from 'misago/services/page-title';
  16. export default class extends WithDropdown {
  17. constructor(props) {
  18. super(props);
  19. if (misago.has('THREADS')) {
  20. this.initWithPreloadedData(misago.get('THREADS'));
  21. } else {
  22. this.initWithoutPreloadedData();
  23. }
  24. }
  25. initWithPreloadedData(data) {
  26. this.state = {
  27. isLoaded: false,
  28. isBusy: false,
  29. dropdown: false,
  30. subcategories: data.subcategories,
  31. count: data.count,
  32. more: data.more,
  33. page: data.page,
  34. pages: data.pages
  35. };
  36. }
  37. initWithoutPreloadedData() {
  38. this.state = {
  39. isLoaded: false,
  40. isBusy: false,
  41. dropdown: false,
  42. subcategories: [],
  43. count: 0,
  44. more: 0,
  45. page: 1,
  46. pages: 1
  47. };
  48. this.loadThreads();
  49. }
  50. loadThreads(page=1) {
  51. let category = null;
  52. if (!this.props.route.category.special_role) {
  53. category = this.props.route.category.id;
  54. }
  55. ajax.get(misago.get('THREADS_API'), {
  56. category: category,
  57. list: this.props.route.list.type,
  58. page: page || 1
  59. }, 'threads').then((data) => {
  60. if (page === 1) {
  61. store.dispatch(hydrate(data.results));
  62. } else {
  63. store.dispatch(append(data.results));
  64. }
  65. this.setState({
  66. isLoaded: true,
  67. isBusy: false,
  68. subcategories: data.subcategories,
  69. count: data.count,
  70. more: data.more,
  71. page: data.page,
  72. pages: data.pages
  73. });
  74. }, (rejection) => {
  75. snackbar.apiError(rejection);
  76. });
  77. }
  78. componentDidMount() {
  79. title.set(getPageTitle(this.props.route));
  80. if (misago.has('THREADS')) {
  81. // unlike in other components, routes are root components for threads
  82. // so we can't dispatch store action from constructor
  83. store.dispatch(hydrate(misago.pop('THREADS').results));
  84. this.setState({
  85. isLoaded: true
  86. });
  87. }
  88. }
  89. getTitle() {
  90. return getTitle(this.props.route);
  91. }
  92. /* jshint ignore:start */
  93. loadMore = () => {
  94. this.setState({
  95. isBusy: true
  96. });
  97. this.loadThreads(this.state.page + 1);
  98. };
  99. /* jshint ignore:end */
  100. getClassName() {
  101. let className = 'page page-threads';
  102. className += ' page-threads-' + this.props.route.list;
  103. if (this.props.route.category.css_class) {
  104. className += ' page-' + this.props.route.category.css_class;
  105. }
  106. return className;
  107. }
  108. getCompactNav() {
  109. if (this.props.route.lists.length > 1) {
  110. /* jshint ignore:start */
  111. return <CompactNav baseUrl={this.props.route.category.absolute_url}
  112. list={this.props.route.list}
  113. lists={this.props.route.lists}
  114. hideNav={this.hideNav} />;
  115. /* jshint ignore:end */
  116. } else {
  117. return null;
  118. }
  119. }
  120. getCategoryDescription() {
  121. if (this.props.route.category.description) {
  122. /* jshint ignore:start */
  123. return <div className="category-description">
  124. <div className="lead" dangerouslySetInnerHTML={{
  125. __html: this.props.route.category.description.html
  126. }} />
  127. </div>;
  128. /* jshint ignore:end */
  129. } else {
  130. return null;
  131. }
  132. }
  133. getToolbar() {
  134. if (this.state.subcategories.length) {
  135. /* jshint ignore:start */
  136. return <div className="toolbar">
  137. <CategoryPicker choices={this.state.subcategories}
  138. categories={this.props.route.categoriesMap}
  139. list={this.props.route.list} />
  140. </div>;
  141. /* jshint ignore:end */
  142. } else {
  143. return null;
  144. }
  145. }
  146. getMoreButton() {
  147. if (this.state.more) {
  148. /* jshint ignore:start */
  149. return <div className="pager-more">
  150. <Button loading={this.state.isBusy}
  151. onClick={this.loadMore}>
  152. {gettext("Show more")}
  153. </Button>
  154. </div>;
  155. /* jshint ignore:end */
  156. } else {
  157. return null;
  158. }
  159. }
  160. render() {
  161. /* jshint ignore:start */
  162. return <div className={this.getClassName()}>
  163. <Header title={this.getTitle()}
  164. route={this.props.route}
  165. dropdown={this.state.dropdown}
  166. toggleNav={this.toggleNav}
  167. hideNav={this.hideNav} />
  168. <div className={this.getCompactNavClassName()}>
  169. {this.getCompactNav()}
  170. </div>
  171. <div className="container">
  172. {this.getCategoryDescription()}
  173. {this.getToolbar()}
  174. <ThreadsList threads={this.props.threads}
  175. categories={this.props.route.categoriesMap}
  176. isLoaded={this.state.isLoaded}
  177. isBusy={this.state.isBusy}>
  178. <ThreadsListEmpty category={this.props.route.category}
  179. list={this.props.route.list} />
  180. </ThreadsList>
  181. {this.getMoreButton()}
  182. </div>
  183. </div>;
  184. /* jshint ignore:end */
  185. }
  186. }