From 4013aed07882fc17e3429dfbd1cf86ce293ff115 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 18 Jun 2020 18:47:01 -0400 Subject: [PATCH] Remove app.search instance, cache app.cache.searched (#2151) * Moved search state logic into search state --- .../core/js/src/forum/ForumApplication.js | 17 ++-- .../src/forum/components/HeaderSecondary.js | 3 +- .../core/js/src/forum/components/IndexPage.js | 79 ++------------- .../core/js/src/forum/components/Search.js | 67 ++++--------- .../js/src/forum/components/SearchSource.js | 4 +- .../js/src/forum/states/GlobalSearchState.js | 95 +++++++++++++++++++ .../core/js/src/forum/states/SearchState.js | 35 +++++++ 7 files changed, 169 insertions(+), 131 deletions(-) create mode 100644 framework/core/js/src/forum/states/GlobalSearchState.js create mode 100644 framework/core/js/src/forum/states/SearchState.js diff --git a/framework/core/js/src/forum/ForumApplication.js b/framework/core/js/src/forum/ForumApplication.js index 8173dd73d..c1ab8a5ee 100644 --- a/framework/core/js/src/forum/ForumApplication.js +++ b/framework/core/js/src/forum/ForumApplication.js @@ -1,6 +1,5 @@ import History from './utils/History'; import Pane from './utils/Pane'; -import Search from './components/Search'; import ReplyComposer from './components/ReplyComposer'; import DiscussionPage from './components/DiscussionPage'; import SignUpModal from './components/SignUpModal'; @@ -15,6 +14,7 @@ import alertEmailConfirmation from './utils/alertEmailConfirmation'; import Application from '../common/Application'; import Navigation from '../common/components/Navigation'; import NotificationListState from './states/NotificationListState'; +import GlobalSearchState from './states/GlobalSearchState'; export default class ForumApplication extends Application { /** @@ -35,13 +35,6 @@ export default class ForumApplication extends Application { discussionRenamed: DiscussionRenamedPost, }; - /** - * The page's search component instance. - * - * @type {Search} - */ - search = new Search(); - /** * An object which controls the state of the page's side pane. * @@ -71,6 +64,14 @@ export default class ForumApplication extends Application { */ notifications = new NotificationListState(this); + /* + * An object which stores previously searched queries and provides convenient + * tools for retrieving and managing search values. + * + * @type {GlobalSearchState} + */ + search = new GlobalSearchState(); + constructor() { super(); diff --git a/framework/core/js/src/forum/components/HeaderSecondary.js b/framework/core/js/src/forum/components/HeaderSecondary.js index cc6db8bb2..932408fde 100644 --- a/framework/core/js/src/forum/components/HeaderSecondary.js +++ b/framework/core/js/src/forum/components/HeaderSecondary.js @@ -7,6 +7,7 @@ import SelectDropdown from '../../common/components/SelectDropdown'; import NotificationsDropdown from './NotificationsDropdown'; import ItemList from '../../common/utils/ItemList'; import listItems from '../../common/helpers/listItems'; +import Search from '../components/Search'; /** * The `HeaderSecondary` component displays secondary header controls, such as @@ -33,7 +34,7 @@ export default class HeaderSecondary extends Component { items() { const items = new ItemList(); - items.add('search', app.search.render(), 30); + items.add('search', Search.component({ state: app.search }), 30); if (app.forum.attribute('showLanguageSelector') && Object.keys(app.data.locales).length > 1) { const locales = []; diff --git a/framework/core/js/src/forum/components/IndexPage.js b/framework/core/js/src/forum/components/IndexPage.js index 9e399a8c8..76a2e83c8 100644 --- a/framework/core/js/src/forum/components/IndexPage.js +++ b/framework/core/js/src/forum/components/IndexPage.js @@ -2,7 +2,6 @@ import { extend } from '../../common/extend'; import Page from './Page'; import ItemList from '../../common/utils/ItemList'; import listItems from '../../common/helpers/listItems'; -import icon from '../../common/helpers/icon'; import DiscussionList from './DiscussionList'; import WelcomeHero from './WelcomeHero'; import DiscussionComposer from './DiscussionComposer'; @@ -18,6 +17,8 @@ import SelectDropdown from '../../common/components/SelectDropdown'; * hero, the sidebar, and the discussion list. */ export default class IndexPage extends Page { + static providesInitialSearch = true; + init() { super.init(); @@ -36,7 +37,7 @@ export default class IndexPage extends Page { app.cache.discussionList = null; } - const params = this.params(); + const params = app.search.params(); if (app.cache.discussionList) { // Compare the requested parameters (sort, search query) to the ones that @@ -187,7 +188,7 @@ export default class IndexPage extends Page { */ navItems() { const items = new ItemList(); - const params = this.stickyParams(); + const params = app.search.stickyParams(); items.add( 'allDiscussions', @@ -222,15 +223,15 @@ export default class IndexPage extends Page { 'sort', Dropdown.component({ buttonClassName: 'Button', - label: sortOptions[this.params().sort] || Object.keys(sortMap).map((key) => sortOptions[key])[0], + label: sortOptions[app.search.params().sort] || Object.keys(sortMap).map((key) => sortOptions[key])[0], children: Object.keys(sortOptions).map((value) => { const label = sortOptions[value]; - const active = (this.params().sort || Object.keys(sortMap)[0]) === value; + const active = (app.search.params().sort || Object.keys(sortMap)[0]) === value; return Button.component({ children: label, icon: active ? 'fas fa-check' : true, - onclick: this.changeSort.bind(this, value), + onclick: app.search.changeSort.bind(app.search, value), active: active, }); }), @@ -280,72 +281,6 @@ export default class IndexPage extends Page { return items; } - /** - * Return the current search query, if any. This is implemented to activate - * the search box in the header. - * - * @see Search - * @return {String} - */ - searching() { - return this.params().q; - } - - /** - * Redirect to the index page without a search filter. This is called when the - * 'x' is clicked in the search box in the header. - * - * @see Search - */ - clearSearch() { - const params = this.params(); - delete params.q; - - m.route(app.route(this.props.routeName, params)); - } - - /** - * Redirect to the index page using the given sort parameter. - * - * @param {String} sort - */ - changeSort(sort) { - const params = this.params(); - - if (sort === Object.keys(app.cache.discussionList.sortMap())[0]) { - delete params.sort; - } else { - params.sort = sort; - } - - m.route(app.route(this.props.routeName, params)); - } - - /** - * Get URL parameters that stick between filter changes. - * - * @return {Object} - */ - stickyParams() { - return { - sort: m.route.param('sort'), - q: m.route.param('q'), - }; - } - - /** - * Get parameters to pass to the DiscussionList component. - * - * @return {Object} - */ - params() { - const params = this.stickyParams(); - - params.filter = m.route.param('filter'); - - return params; - } - /** * Open the composer for a new discussion or prompt the user to login. * diff --git a/framework/core/js/src/forum/components/Search.js b/framework/core/js/src/forum/components/Search.js index b744abdcc..5302d6c6f 100644 --- a/framework/core/js/src/forum/components/Search.js +++ b/framework/core/js/src/forum/components/Search.js @@ -12,19 +12,17 @@ import UsersSearchSource from './UsersSearchSource'; * The `Search` component displays a menu of as-you-type results from a variety * of sources. * - * The search box will be 'activated' if the app's current controller implements - * a `searching` method that returns a truthy value. If this is the case, an 'x' - * button will be shown next to the search field, and clicking it will call the - * `clearSearch` method on the controller. + * The search box will be 'activated' if the app's seach state's + * getInitialSearch() value is a truthy value. If this is the case, an 'x' + * button will be shown next to the search field, and clicking it will clear the search. + * + * PROPS: + * + * - state: AlertState instance. */ export default class Search extends Component { init() { - /** - * The value of the search input. - * - * @type {Function} - */ - this.value = m.prop(''); + this.state = this.props.state; /** * Whether or not the search input has focus. @@ -47,13 +45,6 @@ export default class Search extends Component { */ this.loadingSources = 0; - /** - * A list of queries that have been searched for. - * - * @type {Array} - */ - this.searched = []; - /** * The index of the currently-selected