summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--webapp/components/search_bar.jsx13
-rw-r--r--webapp/components/search_results.jsx29
-rw-r--r--webapp/components/search_results_header.jsx3
-rw-r--r--webapp/components/sidebar_right/sidebar_right.jsx2
-rwxr-xr-xwebapp/i18n/en.json1
-rw-r--r--webapp/sass/components/_search.scss17
-rw-r--r--webapp/sass/layout/_sidebar-right.scss9
-rw-r--r--webapp/sass/responsive/_mobile.scss5
-rw-r--r--webapp/stores/search_store.jsx16
9 files changed, 83 insertions, 12 deletions
diff --git a/webapp/components/search_bar.jsx b/webapp/components/search_bar.jsx
index c635c5eb1..f2fed25ca 100644
--- a/webapp/components/search_bar.jsx
+++ b/webapp/components/search_bar.jsx
@@ -171,7 +171,16 @@ export default class SearchBar extends React.Component {
handleSubmit(e) {
e.preventDefault();
- this.handleSearch(this.state.searchTerm.trim());
+ const terms = this.state.searchTerm.trim();
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_SEARCH_TERM,
+ term: terms,
+ do_search: true,
+ is_mention_search: false
+ });
+
+ this.handleSearch(terms);
this.search.blur();
}
@@ -221,7 +230,7 @@ export default class SearchBar extends React.Component {
var isSearching = null;
if (this.state.isSearching) {
- isSearching = <span className={'fa fa-refresh fa-refresh-animate icon--refresh icon--rotate'}/>;
+ isSearching = <span className={'fa fa-spin fa-spinner'}/>;
}
let helpClass = 'search-help-popover';
diff --git a/webapp/components/search_results.jsx b/webapp/components/search_results.jsx
index 1d1627950..1499c8abb 100644
--- a/webapp/components/search_results.jsx
+++ b/webapp/components/search_results.jsx
@@ -39,7 +39,8 @@ function getStateFromStores() {
results,
channels,
searchTerm: SearchStore.getSearchTerm(),
- flaggedPosts: PreferenceStore.getCategory(Constants.Preferences.CATEGORY_FLAGGED_POST)
+ flaggedPosts: PreferenceStore.getCategory(Constants.Preferences.CATEGORY_FLAGGED_POST),
+ loading: SearchStore.isLoading()
};
}
@@ -71,6 +72,7 @@ export default class SearchResults extends React.Component {
componentDidMount() {
this.mounted = true;
+ SearchStore.addSearchTermChangeListener(this.onSearchTermChange);
SearchStore.addSearchChangeListener(this.onChange);
ChannelStore.addChangeListener(this.onChange);
PreferenceStore.addChangeListener(this.onPreferenceChange);
@@ -113,6 +115,7 @@ export default class SearchResults extends React.Component {
componentWillUnmount() {
this.mounted = false;
+ SearchStore.removeSearchTermChangeListener(this.onSearchTermChange);
SearchStore.removeSearchChangeListener(this.onChange);
ChannelStore.removeChangeListener(this.onChange);
PreferenceStore.removeChangeListener(this.onPreferenceChange);
@@ -144,6 +147,14 @@ export default class SearchResults extends React.Component {
});
}
+ onSearchTermChange(doSearch) {
+ if (this.mounted && doSearch) {
+ this.setState({
+ loading: true
+ });
+ }
+ }
+
onChange() {
if (this.mounted) {
this.setState(getStateFromStores());
@@ -175,7 +186,20 @@ export default class SearchResults extends React.Component {
var ctls = null;
- if (this.props.isFlaggedPosts && noResults) {
+ if (this.state.loading) {
+ ctls =
+ (
+ <div className='sidebar--right__subheader'>
+ <div className='sidebar--right__loading'>
+ <i className='fa fa-spinner fa-spin'/>
+ <FormattedMessage
+ id='search_header.loading'
+ defaultMessage='Searching...'
+ />
+ </div>
+ </div>
+ );
+ } else if (this.props.isFlaggedPosts && noResults) {
ctls = (
<div className='sidebar--right__subheader'>
<ul>
@@ -319,6 +343,7 @@ export default class SearchResults extends React.Component {
isFlaggedPosts={this.props.isFlaggedPosts}
isPinnedPosts={this.props.isPinnedPosts}
channelDisplayName={this.props.channelDisplayName}
+ isLoading={this.state.loading}
/>
<div
id='search-items-container'
diff --git a/webapp/components/search_results_header.jsx b/webapp/components/search_results_header.jsx
index 467b77e27..b3f77c413 100644
--- a/webapp/components/search_results_header.jsx
+++ b/webapp/components/search_results_header.jsx
@@ -148,5 +148,6 @@ SearchResultsHeader.propTypes = {
shrink: PropTypes.func,
isFlaggedPosts: PropTypes.bool,
isPinnedPosts: PropTypes.bool,
- channelDisplayName: PropTypes.string.isRequired
+ channelDisplayName: PropTypes.string.isRequired,
+ isLoading: PropTypes.bool.isRequired
};
diff --git a/webapp/components/sidebar_right/sidebar_right.jsx b/webapp/components/sidebar_right/sidebar_right.jsx
index 737254682..f7c0a6400 100644
--- a/webapp/components/sidebar_right/sidebar_right.jsx
+++ b/webapp/components/sidebar_right/sidebar_right.jsx
@@ -157,7 +157,7 @@ export default class SidebarRight extends React.Component {
onSearchChange() {
this.setState({
- searchVisible: SearchStore.getSearchResults() !== null,
+ searchVisible: SearchStore.getSearchResults() !== null || SearchStore.isLoading(),
isMentionSearch: SearchStore.getIsMentionSearch(),
isFlaggedPosts: SearchStore.getIsFlaggedPosts(),
isPinnedPosts: SearchStore.getIsPinnedPosts()
diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json
index 77206c161..992af160f 100755
--- a/webapp/i18n/en.json
+++ b/webapp/i18n/en.json
@@ -2061,6 +2061,7 @@
"search_item.jump": "Jump",
"search_results.because": "<ul><li>If you're searching a partial phrase (ex. searching \"rea\", looking for \"reach\" or \"reaction\"), append a * to your search term.</li><li>Two letter searches and common words like \"this\", \"a\" and \"is\" won't appear in search results due to excessive results returned.</li></ul>",
"search_results.noResults": "No results found. Try again?",
+ "search_results.searching": "Searching...",
"search_results.usage": "<ul><li>Use <b>\"quotation marks\"</b> to search for phrases</li><li>Use <b>from:</b> to find posts from specific users and <b>in:</b> to find posts in specific channels</li></ul>",
"search_results.usageFlag1": "You haven't flagged any messages yet.",
"search_results.usageFlag2": "You can add a flag to messages and comments by clicking the ",
diff --git a/webapp/sass/components/_search.scss b/webapp/sass/components/_search.scss
index c9b8d4c02..4461b1da1 100644
--- a/webapp/sass/components/_search.scss
+++ b/webapp/sass/components/_search.scss
@@ -42,10 +42,6 @@
width: 40px;
}
-.search-form__container {
-
-}
-
.search__form {
position: relative;
@@ -71,15 +67,16 @@
width: 100%;
}
- .icon--refresh {
+ .fa-spin {
@include opacity(0.5);
+ font-size: 1.2em;
position: absolute;
right: 27px;
top: 27px;
.search-bar__container & {
right: 12px;
- top: 11px;
+ top: 10px;
}
}
}
@@ -93,6 +90,14 @@
position: relative;
}
+.search-items-container div.loading {
+ text-align: center;
+}
+
+.search-items-container img {
+ display: inline-block;
+}
+
.search-results-header {
border-bottom: $border-gray;
color: #999999;
diff --git a/webapp/sass/layout/_sidebar-right.scss b/webapp/sass/layout/_sidebar-right.scss
index 6d31c1606..d7a18b587 100644
--- a/webapp/sass/layout/_sidebar-right.scss
+++ b/webapp/sass/layout/_sidebar-right.scss
@@ -224,6 +224,15 @@
text-transform: uppercase;
}
+ .sidebar--right__loading {
+ @include opacity(.7);
+ text-align: center;
+
+ .fa {
+ margin-right: 5px;
+ }
+ }
+
.sidebar--right__subheader {
font-size: 1em;
padding: .5em 1em 0;
diff --git a/webapp/sass/responsive/_mobile.scss b/webapp/sass/responsive/_mobile.scss
index 8f44a883f..d39797efb 100644
--- a/webapp/sass/responsive/_mobile.scss
+++ b/webapp/sass/responsive/_mobile.scss
@@ -1008,6 +1008,11 @@
margin-top: 9px;
width: 100%;
+ .fa-spin {
+ font-size: 1.1em;
+ top: 9px;
+ }
+
.search-bar {
font-size: 14px;
height: 32px;
diff --git a/webapp/stores/search_store.jsx b/webapp/stores/search_store.jsx
index d57c630cb..dd9b6dbdf 100644
--- a/webapp/stores/search_store.jsx
+++ b/webapp/stores/search_store.jsx
@@ -24,6 +24,7 @@ class SearchStoreClass extends EventEmitter {
this.isPinnedPosts = false;
this.isVisible = false;
this.searchTerm = '';
+ this.loading = false;
}
emitChange() {
@@ -157,6 +158,14 @@ class SearchStoreClass extends EventEmitter {
results.order.splice(index, 1);
}
}
+
+ setLoading(loading) {
+ this.loading = loading;
+ }
+
+ isLoading() {
+ return this.loading;
+ }
}
var SearchStore = new SearchStoreClass();
@@ -173,10 +182,17 @@ SearchStore.dispatchToken = AppDispatcher.register((payload) => {
// ignore pin posts update after switch to a new channel
return;
}
+ SearchStore.setLoading(false);
SearchStore.storeSearchResults(action.results, action.is_mention_search, action.is_flagged_posts, action.is_pinned_posts);
SearchStore.emitSearchChange();
break;
case ActionTypes.RECEIVED_SEARCH_TERM:
+ if (action.do_search) {
+ // while a search is in progress, hide results from previous search
+ SearchStore.setLoading(true);
+ SearchStore.storeSearchResults(null, false, false, false);
+ SearchStore.emitSearchChange();
+ }
SearchStore.storeSearchTerm(action.term);
SearchStore.emitSearchTermChange(action.do_search, action.is_mention_search);
break;