diff options
author | enahum <nahumhbl@gmail.com> | 2016-09-15 09:53:21 -0300 |
---|---|---|
committer | Joram Wilander <jwawilander@gmail.com> | 2016-09-15 08:53:21 -0400 |
commit | 2659d19d05c033d84a22d916e2b8eb709dcf23dc (patch) | |
tree | e91bb2058801f2507fea8c1dc645a94bf858ae58 /webapp/components | |
parent | b180bb46e3034d0ce75c9961a8ccea3eefbc855c (diff) | |
download | chat-2659d19d05c033d84a22d916e2b8eb709dcf23dc.tar.gz chat-2659d19d05c033d84a22d916e2b8eb709dcf23dc.tar.bz2 chat-2659d19d05c033d84a22d916e2b8eb709dcf23dc.zip |
PLT-3455 More channels filter (#4022)
Diffstat (limited to 'webapp/components')
-rw-r--r-- | webapp/components/filtered_channel_list.jsx | 180 | ||||
-rw-r--r-- | webapp/components/more_channels.jsx | 102 |
2 files changed, 216 insertions, 66 deletions
diff --git a/webapp/components/filtered_channel_list.jsx b/webapp/components/filtered_channel_list.jsx new file mode 100644 index 000000000..259c5cbf2 --- /dev/null +++ b/webapp/components/filtered_channel_list.jsx @@ -0,0 +1,180 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import $ from 'jquery'; +import ReactDOM from 'react-dom'; +import * as UserAgent from 'utils/user_agent.jsx'; + +import {localizeMessage} from 'utils/utils.jsx'; +import {FormattedMessage} from 'react-intl'; + +import React from 'react'; +import loadingGif from 'images/load.gif'; + +export default class FilteredChannelList extends React.Component { + constructor(props) { + super(props); + + this.handleFilterChange = this.handleFilterChange.bind(this); + this.createChannelRow = this.createChannelRow.bind(this); + this.filterChannels = this.filterChannels.bind(this); + + this.state = { + filter: '', + joiningChannel: '', + channels: this.filterChannels(props.channels) + }; + } + + componentWillReceiveProps(nextProps) { + // assume the channel list is immutable + if (this.props.channels !== nextProps.channels) { + this.setState({ + channels: this.filterChannels(nextProps.channels) + }); + } + } + + componentDidMount() { + // only focus the search box on desktop so that we don't cause the keyboard to open on mobile + if (!UserAgent.isMobileApp()) { + ReactDOM.findDOMNode(this.refs.filter).focus(); + } + } + + componentDidUpdate(prevProps, prevState) { + if (prevState.filter !== this.state.filter) { + $(ReactDOM.findDOMNode(this.refs.channelList)).scrollTop(0); + } + } + + handleJoin(channel) { + this.setState({joiningChannel: channel.id}); + this.props.handleJoin( + channel, + () => { + this.setState({joiningChannel: ''}); + }); + } + + createChannelRow(channel) { + let joinButton; + if (this.state.joiningChannel === channel.id) { + joinButton = ( + <img + className='join-channel-loading-gif' + src={loadingGif} + /> + ); + } else { + joinButton = ( + <button + onClick={this.handleJoin.bind(this, channel)} + className='btn btn-primary' + > + <FormattedMessage + id='more_channels.join' + defaultMessage='Join' + /> + </button> + ); + } + + return ( + <div + className='more-modal__row' + key={channel.id} + > + <div className='more-modal__details'> + <p className='more-modal__name'>{channel.display_name}</p> + <p className='more-modal__description'>{channel.purpose}</p> + </div> + <div className='more-modal__actions'> + {joinButton} + </div> + </div> + ); + } + + filterChannels(channels) { + if (!this.state || !this.state.filter) { + return channels; + } + + return channels.filter((chan) => { + const filter = this.state.filter.toLowerCase(); + return !!((chan.name.toLowerCase().indexOf(filter) !== -1 || chan.display_name.toLowerCase().indexOf(filter) !== -1) && chan.delete_at === 0); + }); + } + + handleFilterChange(e) { + this.setState({ + filter: e.target.value + }); + } + + render() { + let channels = this.state.channels; + + if (this.state.filter && this.state.filter.length > 0) { + channels = this.filterChannels(channels); + } + + let count; + if (channels.length === this.props.channels.length) { + count = ( + <FormattedMessage + id='filtered_channels_list.count' + defaultMessage='{count} {count, plural, =0 {0 channels} one {channel} other {channels}}' + values={{ + count: channels.length + }} + /> + ); + } else { + count = ( + <FormattedMessage + id='filtered_channels_list.countTotal' + defaultMessage='{count} {count, plural, =0 {0 channels} one {channel} other {channels}} of {total} Total' + values={{ + count: channels.length, + total: this.props.channels.length + }} + /> + ); + } + + return ( + <div className='filtered-user-list'> + <div className='filter-row'> + <div className='col-sm-6'> + <input + ref='filter' + className='form-control filter-textbox' + placeholder={localizeMessage('filtered_channels_list.search', 'Search channels')} + onInput={this.handleFilterChange} + /> + </div> + <div className='col-sm-12'> + <span className='channel-count pull-left'>{count}</span> + </div> + </div> + <div + ref='channelList' + className='more-modal__list' + > + {channels.map(this.createChannelRow)} + </div> + </div> + ); + } +} + +FilteredChannelList.defaultProps = { + channels: [] +}; + +FilteredChannelList.propTypes = { + channels: React.PropTypes.arrayOf(React.PropTypes.object), + handleJoin: React.PropTypes.func.isRequired +}; diff --git a/webapp/components/more_channels.jsx b/webapp/components/more_channels.jsx index 724cd2f60..00e7ecf78 100644 --- a/webapp/components/more_channels.jsx +++ b/webapp/components/more_channels.jsx @@ -4,6 +4,7 @@ import $ from 'jquery'; import LoadingScreen from './loading_screen.jsx'; import NewChannelFlow from './new_channel_flow.jsx'; +import FilteredChannelList from './filtered_channel_list.jsx'; import ChannelStore from 'stores/channel_store.jsx'; import UserStore from 'stores/user_store.jsx'; @@ -18,18 +19,8 @@ import {FormattedMessage} from 'react-intl'; import {browserHistory} from 'react-router/es6'; import React from 'react'; -import ReactDOM from 'react-dom'; import PureRenderMixin from 'react-addons-pure-render-mixin'; -import loadingGif from 'images/load.gif'; - -function getStateFromStores() { - return { - channels: ChannelStore.getMoreAll(), - serverError: null - }; -} - export default class MoreChannels extends React.Component { constructor(props) { super(props); @@ -37,100 +28,78 @@ export default class MoreChannels extends React.Component { this.onListenerChange = this.onListenerChange.bind(this); this.handleJoin = this.handleJoin.bind(this); this.handleNewChannel = this.handleNewChannel.bind(this); - this.createChannelRow = this.createChannelRow.bind(this); this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); - var initState = getStateFromStores(); + const initState = this.getStateFromStores(); initState.channelType = ''; - initState.joiningChannel = ''; initState.showNewChannelModal = false; this.state = initState; } + componentDidMount() { + const self = this; ChannelStore.addMoreChangeListener(this.onListenerChange); - $(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', () => { + + $(this.refs.modal).on('shown.bs.modal', () => { AsyncClient.getMoreChannels(true); }); - var self = this; - $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', (e) => { - var button = e.relatedTarget; + $(this.refs.modal).on('show.bs.modal', (e) => { + const button = e.relatedTarget; self.setState({channelType: $(button).attr('data-channeltype')}); }); } + componentWillUnmount() { ChannelStore.removeMoreChangeListener(this.onListenerChange); } + + getStateFromStores() { + return { + channels: ChannelStore.getMoreAll(), + serverError: null + }; + } + onListenerChange() { - var newState = getStateFromStores(); + const newState = this.getStateFromStores(); if (!Utils.areObjectsEqual(newState.channels, this.state.channels)) { this.setState(newState); } } - handleJoin(channel) { - this.setState({joiningChannel: channel.id}); + + handleJoin(channel, done) { GlobalActions.emitJoinChannelEvent( channel, () => { - $(ReactDOM.findDOMNode(this.refs.modal)).modal('hide'); + $(this.refs.modal).modal('hide'); browserHistory.push(TeamStore.getCurrentTeamRelativeUrl() + '/channels/' + channel.name); - this.setState({joiningChannel: ''}); + if (done) { + done(); + } }, (err) => { - this.setState({joiningChannel: '', serverError: err.message}); + this.setState({serverError: err.message}); + if (done) { + done(); + } } ); } + handleNewChannel() { - $(ReactDOM.findDOMNode(this.refs.modal)).modal('hide'); + $(this.refs.modal).modal('hide'); this.setState({showNewChannelModal: true}); } - createChannelRow(channel) { - let joinButton; - if (this.state.joiningChannel === channel.id) { - joinButton = ( - <img - className='join-channel-loading-gif' - src={loadingGif} - /> - ); - } else { - joinButton = ( - <button - onClick={this.handleJoin.bind(self, channel)} - className='btn btn-primary' - > - <FormattedMessage - id='more_channels.join' - defaultMessage='Join' - /> - </button> - ); - } - return ( - <div - className='more-modal__row' - key={channel.id} - > - <div className='more-modal__details'> - <p className='more-modal__name'>{channel.display_name}</p> - <p className='more-modal__description'>{channel.purpose}</p> - </div> - <div className='more-modal__actions'> - {joinButton} - </div> - </div> - ); - } render() { let maxHeight = 1000; if (Utils.windowHeight() <= 1200) { maxHeight = Utils.windowHeight() - 300; } - var serverError; + let serverError; if (this.state.serverError) { serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>; } @@ -170,7 +139,7 @@ export default class MoreChannels extends React.Component { } } - var moreChannels; + let moreChannels; if (this.state.channels != null) { var channels = this.state.channels; @@ -178,9 +147,10 @@ export default class MoreChannels extends React.Component { moreChannels = <LoadingScreen/>; } else if (channels.length) { moreChannels = ( - <div className='more-modal__list'> - {channels.map(this.createChannelRow)} - </div> + <FilteredChannelList + channels={channels} + handleJoin={this.handleJoin} + /> ); } else { moreChannels = ( |