Skip to content

Commit

Permalink
Move Discussion List State into its own class (#2150)
Browse files Browse the repository at this point in the history
Extract discussion list state
  • Loading branch information
askvortsov1 authored Jun 18, 2020
1 parent c5d3b05 commit 5c1663d
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 204 deletions.
14 changes: 14 additions & 0 deletions js/src/forum/ForumApplication.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Application from '../common/Application';
import Navigation from '../common/components/Navigation';
import NotificationListState from './states/NotificationListState';
import GlobalSearchState from './states/GlobalSearchState';
import DiscussionListState from './state/DiscussionListState';

export default class ForumApplication extends Application {
/**
Expand Down Expand Up @@ -76,6 +77,19 @@ export default class ForumApplication extends Application {
super();

routes(this);

/**
* An object which controls the state of the cached discussion list, which
* is used in the index page and the slideout pane.
*
* @type {DiscussionListState}
*/
this.discussions = new DiscussionListState({ forumApp: this });

/**
* @deprecated beta 14, remove in beta 15.
*/
this.cache.discussionList = this.discussions;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion js/src/forum/components/DiscussionComposer.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export default class DiscussionComposer extends ComposerBody {
.save(data)
.then((discussion) => {
app.composer.hide();
app.cache.discussionList.refresh();
app.discussions.refresh();
m.route(app.route.discussion(discussion));
}, this.loaded.bind(this));
}
Expand Down
176 changes: 11 additions & 165 deletions js/src/forum/components/DiscussionList.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,56 +11,38 @@ import Placeholder from '../../common/components/Placeholder';
*
* - `params` A map of parameters used to construct a refined parameter object
* to send along in the API request to get discussion results.
* - `state` A DiscussionListState object that represents the discussion lists's state.
*/
export default class DiscussionList extends Component {
init() {
/**
* Whether or not discussion results are loading.
*
* @type {Boolean}
*/
this.loading = true;

/**
* Whether or not there are more results that can be loaded.
*
* @type {Boolean}
*/
this.moreResults = false;

/**
* The discussions in the discussion list.
*
* @type {Discussion[]}
*/
this.discussions = [];

this.refresh();
this.state = this.props.state;
}

view() {
const params = this.props.params;
const state = this.state;

const params = state.getParams();
let loading;

if (this.loading) {
if (state.isLoading()) {
loading = LoadingIndicator.component();
} else if (this.moreResults) {
} else if (state.moreResults) {
loading = Button.component({
children: app.translator.trans('core.forum.discussion_list.load_more_button'),
className: 'Button',
onclick: this.loadMore.bind(this),
onclick: state.loadMore.bind(state),
});
}

if (this.discussions.length === 0 && !this.loading) {
if (state.empty()) {
const text = app.translator.trans('core.forum.discussion_list.empty_text');
return <div className="DiscussionList">{Placeholder.component({ text })}</div>;
}

return (
<div className={'DiscussionList' + (this.props.params.q ? ' DiscussionList--searchResults' : '')}>
<div className={'DiscussionList' + (state.isSearchResults() ? ' DiscussionList--searchResults' : '')}>
<ul className="DiscussionList-discussions">
{this.discussions.map((discussion) => {
{state.discussions.map((discussion) => {
return (
<li key={discussion.id()} data-id={discussion.id()}>
{DiscussionListItem.component({ discussion, params })}
Expand All @@ -72,140 +54,4 @@ export default class DiscussionList extends Component {
</div>
);
}

/**
* Get the parameters that should be passed in the API request to get
* discussion results.
*
* @return {Object}
* @api
*/
requestParams() {
const params = { include: ['user', 'lastPostedUser'], filter: {} };

params.sort = this.sortMap()[this.props.params.sort];

if (this.props.params.q) {
params.filter.q = this.props.params.q;

params.include.push('mostRelevantPost', 'mostRelevantPost.user');
}

return params;
}

/**
* Get a map of sort keys (which appear in the URL, and are used for
* translation) to the API sort value that they represent.
*
* @return {Object}
*/
sortMap() {
const map = {};

if (this.props.params.q) {
map.relevance = '';
}
map.latest = '-lastPostedAt';
map.top = '-commentCount';
map.newest = '-createdAt';
map.oldest = 'createdAt';

return map;
}

/**
* Clear and reload the discussion list.
*
* @public
*/
refresh(clear = true) {
if (clear) {
this.loading = true;
this.discussions = [];
}

return this.loadResults().then(
(results) => {
this.discussions = [];
this.parseResults(results);
},
() => {
this.loading = false;
m.redraw();
}
);
}

/**
* Load a new page of discussion results.
*
* @param {Integer} offset The index to start the page at.
* @return {Promise}
*/
loadResults(offset) {
const preloadedDiscussions = app.preloadedApiDocument();

if (preloadedDiscussions) {
return m.deferred().resolve(preloadedDiscussions).promise;
}

const params = this.requestParams();
params.page = { offset };
params.include = params.include.join(',');

return app.store.find('discussions', params);
}

/**
* Load the next page of discussion results.
*
* @public
*/
loadMore() {
this.loading = true;

this.loadResults(this.discussions.length).then(this.parseResults.bind(this));
}

/**
* Parse results and append them to the discussion list.
*
* @param {Discussion[]} results
* @return {Discussion[]}
*/
parseResults(results) {
[].push.apply(this.discussions, results);

this.loading = false;
this.moreResults = !!results.payload.links.next;

m.lazyRedraw();

return results;
}

/**
* Remove a discussion from the list if it is present.
*
* @param {Discussion} discussion
* @public
*/
removeDiscussion(discussion) {
const index = this.discussions.indexOf(discussion);

if (index !== -1) {
this.discussions.splice(index, 1);
}
}

/**
* Add a discussion to the top of the list.
*
* @param {Discussion} discussion
* @public
*/
addDiscussion(discussion) {
this.discussions.unshift(discussion);
}
}
10 changes: 6 additions & 4 deletions js/src/forum/components/DiscussionPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import LoadingIndicator from '../../common/components/LoadingIndicator';
import SplitDropdown from '../../common/components/SplitDropdown';
import listItems from '../../common/helpers/listItems';
import DiscussionControls from '../utils/DiscussionControls';
import DiscussionList from './DiscussionList';

/**
* The `DiscussionPage` component displays a whole discussion page, including
Expand Down Expand Up @@ -35,9 +36,9 @@ export default class DiscussionPage extends Page {
// If the discussion list has been loaded, then we'll enable the pane (and
// hide it by default). Also, if we've just come from another discussion
// page, then we don't want Mithril to redraw the whole page – if it did,
// then the pane would which would be slow and would cause problems with
// then the pane would redraw which would be slow and would cause problems with
// event handlers.
if (app.cache.discussionList) {
if (app.discussions.hasDiscussions()) {
app.pane.enable();
app.pane.hide();

Expand All @@ -49,6 +50,7 @@ export default class DiscussionPage extends Page {
app.history.push('discussion');

this.bodyClass = 'App--discussion';
this.discussionListClass = DiscussionList;
}

onunload(e) {
Expand Down Expand Up @@ -90,9 +92,9 @@ export default class DiscussionPage extends Page {

return (
<div className="DiscussionPage">
{app.cache.discussionList ? (
{app.discussions.hasDiscussions() ? (
<div className="DiscussionPage-list" config={this.configPane.bind(this)}>
{!$('.App-navigation').is(':visible') ? app.cache.discussionList.render() : ''}
{!$('.App-navigation').is(':visible') && <DiscussionList state={app.discussions} />}
</div>
) : (
''
Expand Down
27 changes: 5 additions & 22 deletions js/src/forum/components/IndexPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,10 @@ export default class IndexPage extends Page {
// probably want to refresh the results. We will clear the discussion list
// cache so that results are reloaded.
if (app.previous instanceof IndexPage) {
app.cache.discussionList = null;
app.discussions.clear();
}

const params = app.search.params();

if (app.cache.discussionList) {
// Compare the requested parameters (sort, search query) to the ones that
// are currently present in the cached discussion list. If they differ, we
// will clear the cache and set up a new discussion list component with
// the new parameters.
Object.keys(params).some((key) => {
if (app.cache.discussionList.props.params[key] !== params[key]) {
app.cache.discussionList = null;
return true;
}
});
}

if (!app.cache.discussionList) {
app.cache.discussionList = new DiscussionList({ params });
}
app.discussions.refreshParams(app.search.params());

app.history.push('index', app.translator.trans('core.forum.header.back_to_index_tooltip'));

Expand All @@ -81,7 +64,7 @@ export default class IndexPage extends Page {
<ul className="IndexPage-toolbar-view">{listItems(this.viewItems().toArray())}</ul>
<ul className="IndexPage-toolbar-action">{listItems(this.actionItems().toArray())}</ul>
</div>
{app.cache.discussionList.render()}
<DiscussionList state={app.discussions} />
</div>
</div>
</div>
Expand Down Expand Up @@ -212,7 +195,7 @@ export default class IndexPage extends Page {
*/
viewItems() {
const items = new ItemList();
const sortMap = app.cache.discussionList.sortMap();
const sortMap = app.discussions.sortMap();

const sortOptions = {};
for (const i in sortMap) {
Expand Down Expand Up @@ -257,7 +240,7 @@ export default class IndexPage extends Page {
icon: 'fas fa-sync',
className: 'Button Button--icon',
onclick: () => {
app.cache.discussionList.refresh();
app.discussions.refresh();
if (app.session.user) {
app.store.find('users', app.session.user.id());
m.redraw();
Expand Down
Loading

0 comments on commit 5c1663d

Please sign in to comment.