move.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import React from 'react'; // jshint ignore:line
  2. import Form from 'misago/components/form';
  3. import FormGroup from 'misago/components/form-group'; // jshint ignore:line
  4. import CategorySelect from 'misago/components/category-select'; // jshint ignore:line
  5. import * as select from 'misago/reducers/selection'; // jshint ignore:line
  6. import { filterThreads } from 'misago/reducers/threads'; // jshint ignore:line
  7. import modal from 'misago/services/modal'; // jshint ignore:line
  8. import store from 'misago/services/store'; // jshint ignore:line
  9. export default class extends Form {
  10. constructor(props) {
  11. super(props);
  12. this.state = {
  13. category: null,
  14. };
  15. const acls = {};
  16. for (const i in props.user.acl.categories) {
  17. if (!props.user.acl.categories.hasOwnProperty(i)) {
  18. continue;
  19. }
  20. const acl = props.user.acl.categories[i];
  21. acls[acl.id] = acl;
  22. }
  23. this.categoryChoices = [];
  24. props.categories.forEach((category) => {
  25. if (category.level > 0) {
  26. const acl = acls[category.id];
  27. const disabled = !acl.can_start_threads || (category.is_closed && !acl.can_close_threads);
  28. this.categoryChoices.push({
  29. value: category.id,
  30. disabled: disabled,
  31. level: category.level - 1,
  32. label: category.name
  33. });
  34. if (!disabled && !this.state.category) {
  35. this.state.category = category.id;
  36. }
  37. }
  38. });
  39. }
  40. /* jshint ignore:start */
  41. handleSubmit = (event) => {
  42. // we don't reload page on submissions
  43. event.preventDefault();
  44. modal.hide();
  45. const onSuccess = () => {
  46. store.dispatch(
  47. filterThreads(this.props.route.category, this.props.categoriesMap));
  48. // deselect threads moved outside of visible scope
  49. const storeState = store.getState();
  50. const leftThreads = storeState.threads.map((thread) => (thread.id));
  51. store.dispatch(select.all(storeState.selection.filter((thread) => {
  52. return leftThreads.indexOf(thread) !== -1;
  53. })));
  54. };
  55. this.props.callApi([
  56. {op: 'replace', path: 'category', value: this.state.category},
  57. {op: 'replace', path: 'flatten-categories', value: null},
  58. {op: 'add', path: 'acl', value: true}
  59. ], gettext("Selected threads were moved."), onSuccess);
  60. };
  61. /* jshint ignore:end */
  62. getClassName() {
  63. if (!this.state.category) {
  64. return 'modal-dialog modal-message';
  65. } else {
  66. return 'modal-dialog';
  67. }
  68. }
  69. renderForm() {
  70. /* jshint ignore:start */
  71. return <form onSubmit={this.handleSubmit}>
  72. <div className="modal-body">
  73. <FormGroup label={gettext("New category")}
  74. for="id_new_category">
  75. <CategorySelect id="id_new_category"
  76. onChange={this.bindInput('category')}
  77. value={this.state.category}
  78. choices={this.categoryChoices} />
  79. </FormGroup>
  80. </div>
  81. <div className="modal-footer">
  82. <button
  83. className="btn btn-default"
  84. data-dismiss="modal"
  85. disabled={this.state.isLoading}
  86. type="button"
  87. >
  88. {gettext("Cancel")}
  89. </button>
  90. <button className="btn btn-primary">
  91. {gettext("Move threads")}
  92. </button>
  93. </div>
  94. </form>;
  95. /* jshint ignore:end */
  96. }
  97. renderCantMoveMessage() {
  98. /* jshint ignore:start */
  99. return <div className="modal-body">
  100. <div className="message-icon">
  101. <span className="material-icon">
  102. info_outline
  103. </span>
  104. </div>
  105. <div className="message-body">
  106. <p className="lead">
  107. {gettext("You can't move threads because there are no categories you are allowed to move them to.")}
  108. </p>
  109. <p>
  110. {gettext("You need permission to start threads in category to be able to move threads to it.")}
  111. </p>
  112. <button
  113. className="btn btn-default"
  114. data-dismiss="modal"
  115. type="button"
  116. >
  117. {gettext("Ok")}
  118. </button>
  119. </div>
  120. </div>;
  121. /* jshint ignore:end */
  122. }
  123. render() {
  124. /* jshint ignore:start */
  125. return <div className={this.getClassName()} role="document">
  126. <div className="modal-content">
  127. <div className="modal-header">
  128. <button type="button" className="close" data-dismiss="modal"
  129. aria-label={gettext("Close")}>
  130. <span aria-hidden="true">&times;</span>
  131. </button>
  132. <h4 className="modal-title">{gettext("Move threads")}</h4>
  133. </div>
  134. {this.state.category ? this.renderForm() : this.renderCantMoveMessage()}
  135. </div>
  136. </div>;
  137. /* jshint ignore:end */
  138. }
  139. }