Add flagging functionality to search results (#3803)

Этот коммит содержится в:
Joram Wilander
2016-08-18 18:37:09 -04:00
коммит произвёл Corey Hulen
родитель a820b1640b
Коммит 4a2fbcaf98
2 изменённых файлов: 62 добавлений и 31 удалений

Просмотреть файл

@@ -1,18 +1,21 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information. // See License.txt for license information.
import $ from 'jquery'; import SearchResultsHeader from './search_results_header.jsx';
import SearchResultsItem from './search_results_item.jsx';
import SearchBox from './search_bar.jsx';
import ChannelStore from 'stores/channel_store.jsx'; import ChannelStore from 'stores/channel_store.jsx';
import SearchStore from 'stores/search_store.jsx'; import SearchStore from 'stores/search_store.jsx';
import UserStore from 'stores/user_store.jsx'; import UserStore from 'stores/user_store.jsx';
import PreferenceStore from 'stores/preference_store.jsx'; import PreferenceStore from 'stores/preference_store.jsx';
import SearchBox from './search_bar.jsx';
import * as Utils from 'utils/utils.jsx'; import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx'; import Constants from 'utils/constants.jsx';
const Preferences = Constants.Preferences; const Preferences = Constants.Preferences;
import SearchResultsHeader from './search_results_header.jsx';
import SearchResultsItem from './search_results_item.jsx';
import $ from 'jquery';
import React from 'react';
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl'; import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
function getStateFromStores() { function getStateFromStores() {
@@ -34,12 +37,11 @@ function getStateFromStores() {
return { return {
results, results,
channels, channels,
searchTerm: SearchStore.getSearchTerm() searchTerm: SearchStore.getSearchTerm(),
flaggedPosts: PreferenceStore.getCategory(Constants.Preferences.CATEGORY_FLAGGED_POST)
}; };
} }
import React from 'react';
export default class SearchResults extends React.Component { export default class SearchResults extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
@@ -48,6 +50,7 @@ export default class SearchResults extends React.Component {
this.onChange = this.onChange.bind(this); this.onChange = this.onChange.bind(this);
this.onUserChange = this.onUserChange.bind(this); this.onUserChange = this.onUserChange.bind(this);
this.onPreferenceChange = this.onPreferenceChange.bind(this);
this.resize = this.resize.bind(this); this.resize = this.resize.bind(this);
this.onPreferenceChange = this.onPreferenceChange.bind(this); this.onPreferenceChange = this.onPreferenceChange.bind(this);
this.handleResize = this.handleResize.bind(this); this.handleResize = this.handleResize.bind(this);
@@ -62,10 +65,13 @@ export default class SearchResults extends React.Component {
componentDidMount() { componentDidMount() {
this.mounted = true; this.mounted = true;
SearchStore.addSearchChangeListener(this.onChange); SearchStore.addSearchChangeListener(this.onChange);
ChannelStore.addChangeListener(this.onChange); ChannelStore.addChangeListener(this.onChange);
PreferenceStore.addChangeListener(this.onPreferenceChange); PreferenceStore.addChangeListener(this.onPreferenceChange);
UserStore.addChangeListener(this.onUserChange); UserStore.addChangeListener(this.onUserChange);
PreferenceStore.addChangeListener(this.onPreferenceChange);
this.resize(); this.resize();
window.addEventListener('resize', this.handleResize); window.addEventListener('resize', this.handleResize);
if (!Utils.isMobile()) { if (!Utils.isMobile()) {
@@ -94,11 +100,14 @@ export default class SearchResults extends React.Component {
} }
componentWillUnmount() { componentWillUnmount() {
this.mounted = false;
SearchStore.removeSearchChangeListener(this.onChange); SearchStore.removeSearchChangeListener(this.onChange);
ChannelStore.removeChangeListener(this.onChange); ChannelStore.removeChangeListener(this.onChange);
PreferenceStore.removeChangeListener(this.onPreferenceChange); PreferenceStore.removeChangeListener(this.onPreferenceChange);
UserStore.removeChangeListener(this.onUserChange); UserStore.removeChangeListener(this.onUserChange);
this.mounted = false; PreferenceStore.removeChangeListener(this.onPreferenceChange);
window.removeEventListener('resize', this.handleResize); window.removeEventListener('resize', this.handleResize);
} }
@@ -111,7 +120,8 @@ export default class SearchResults extends React.Component {
onPreferenceChange() { onPreferenceChange() {
this.setState({ this.setState({
compactDisplay: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT compactDisplay: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT,
flaggedPosts: PreferenceStore.getCategory(Constants.Preferences.CATEGORY_FLAGGED_POST)
}); });
} }
@@ -213,6 +223,11 @@ export default class SearchResults extends React.Component {
} else { } else {
profile = profiles[post.user_id]; profile = profiles[post.user_id];
} }
let isFlagged = false;
if (this.state.flaggedPosts) {
isFlagged = this.state.flaggedPosts.get(post.id) === 'true';
}
return ( return (
<SearchResultsItem <SearchResultsItem
key={post.id} key={post.id}
@@ -222,9 +237,10 @@ export default class SearchResults extends React.Component {
user={profile} user={profile}
term={searchTerm} term={searchTerm}
isMentionSearch={this.props.isMentionSearch} isMentionSearch={this.props.isMentionSearch}
isFlaggedSearch={this.props.isFlaggedPosts}
useMilitaryTime={this.props.useMilitaryTime} useMilitaryTime={this.props.useMilitaryTime}
shrink={this.props.shrink} shrink={this.props.shrink}
isFlagged={this.props.isFlaggedPosts} isFlagged={isFlagged}
/> />
); );
}, this); }, this);

Просмотреть файл

@@ -9,7 +9,7 @@ import UserStore from 'stores/user_store.jsx';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx'; import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import * as GlobalActions from 'actions/global_actions.jsx'; import * as GlobalActions from 'actions/global_actions.jsx';
import {unflagPost, getFlaggedPosts} from 'actions/post_actions.jsx'; import {flagPost, unflagPost} from 'actions/post_actions.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx'; import * as TextFormatting from 'utils/text_formatting.jsx';
import * as Utils from 'utils/utils.jsx'; import * as Utils from 'utils/utils.jsx';
@@ -30,6 +30,7 @@ export default class SearchResultsItem extends React.Component {
this.handleFocusRHSClick = this.handleFocusRHSClick.bind(this); this.handleFocusRHSClick = this.handleFocusRHSClick.bind(this);
this.shrinkSidebar = this.shrinkSidebar.bind(this); this.shrinkSidebar = this.shrinkSidebar.bind(this);
this.unflagPost = this.unflagPost.bind(this); this.unflagPost = this.unflagPost.bind(this);
this.flagPost = this.flagPost.bind(this);
} }
hideSidebar() { hideSidebar() {
@@ -47,11 +48,14 @@ export default class SearchResultsItem extends React.Component {
GlobalActions.emitPostFocusRightHandSideFromSearch(this.props.post, this.props.isMentionSearch); GlobalActions.emitPostFocusRightHandSideFromSearch(this.props.post, this.props.isMentionSearch);
} }
flagPost(e) {
e.preventDefault();
flagPost(this.props.post.id);
}
unflagPost(e) { unflagPost(e) {
e.preventDefault(); e.preventDefault();
unflagPost(this.props.post.id, unflagPost(this.props.post.id);
() => getFlaggedPosts()
);
} }
render() { render() {
@@ -110,6 +114,7 @@ export default class SearchResultsItem extends React.Component {
} }
let flag; let flag;
let flagFunc;
let flagVisible = ''; let flagVisible = '';
let flagTooltip = ( let flagTooltip = (
<Tooltip id='flagTooltip'> <Tooltip id='flagTooltip'>
@@ -129,24 +134,21 @@ export default class SearchResultsItem extends React.Component {
/> />
</Tooltip> </Tooltip>
); );
flagFunc = this.unflagPost;
flag = ( flag = (
<OverlayTrigger
delayShow={Constants.OVERLAY_TIME_DELAY}
placement='top'
overlay={flagTooltip}
>
<a
href='#'
className={'flag-icon__container ' + flagVisible}
onClick={this.unflagPost}
>
<span <span
className='icon' className='icon'
dangerouslySetInnerHTML={{__html: flagIcon}} dangerouslySetInnerHTML={{__html: flagIcon}}
/> />
</a>
</OverlayTrigger>
); );
} else {
flag = (
<span
className='icon'
dangerouslySetInnerHTML={{__html: flagIcon}}
/>
);
flagFunc = this.flagPost;
} }
return ( return (
@@ -187,7 +189,19 @@ export default class SearchResultsItem extends React.Component {
minute='2-digit' minute='2-digit'
/> />
</time> </time>
<OverlayTrigger
delayShow={Constants.OVERLAY_TIME_DELAY}
placement='top'
overlay={flagTooltip}
>
<a
href='#'
className={'flag-icon__container ' + flagVisible}
onClick={flagFunc}
>
{flag} {flag}
</a>
</OverlayTrigger>
</li> </li>
<li className='col__controls'> <li className='col__controls'>
<a <a
@@ -256,6 +270,7 @@ SearchResultsItem.propTypes = {
channel: React.PropTypes.object, channel: React.PropTypes.object,
compactDisplay: React.PropTypes.bool, compactDisplay: React.PropTypes.bool,
isMentionSearch: React.PropTypes.bool, isMentionSearch: React.PropTypes.bool,
isFlaggedSearch: React.PropTypes.bool,
term: React.PropTypes.string, term: React.PropTypes.string,
useMilitaryTime: React.PropTypes.bool.isRequired, useMilitaryTime: React.PropTypes.bool.isRequired,
shrink: React.PropTypes.function, shrink: React.PropTypes.function,