diff options
author | Joram Wilander <jwawilander@gmail.com> | 2017-04-25 11:46:02 -0400 |
---|---|---|
committer | Christopher Speller <crspeller@gmail.com> | 2017-04-25 11:46:02 -0400 |
commit | 6c4c706313eb765eb00c639f381646be74f27b69 (patch) | |
tree | 6068feaa9668dcd74601730ac1a5abfb366402b1 /webapp/components/channel_invite_modal | |
parent | cc07c005074348de87854f1c953a549e8772fa03 (diff) | |
download | chat-6c4c706313eb765eb00c639f381646be74f27b69.tar.gz chat-6c4c706313eb765eb00c639f381646be74f27b69.tar.bz2 chat-6c4c706313eb765eb00c639f381646be74f27b69.zip |
Start moving webapp to Redux (#6140)
* Start moving webapp to Redux
* Fix localforage import
* Updates per feedback
* Feedback udpates and a few fixes
* Minor updates
* Fix statuses, config not loading properly, getMe sanitizing too much
* Fix preferences
* Fix user autocomplete
* Fix sessions and audits
* Fix error handling for all redux actions
* Use new directory structure for components and containers
* Refresh immediately on logout instead of after timeout
* Add fetch polyfill
Diffstat (limited to 'webapp/components/channel_invite_modal')
-rw-r--r-- | webapp/components/channel_invite_modal/channel_invite_modal.jsx | 188 | ||||
-rw-r--r-- | webapp/components/channel_invite_modal/index.js | 24 |
2 files changed, 212 insertions, 0 deletions
diff --git a/webapp/components/channel_invite_modal/channel_invite_modal.jsx b/webapp/components/channel_invite_modal/channel_invite_modal.jsx new file mode 100644 index 000000000..847af16f6 --- /dev/null +++ b/webapp/components/channel_invite_modal/channel_invite_modal.jsx @@ -0,0 +1,188 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import ChannelInviteButton from 'components/channel_invite_button.jsx'; +import SearchableUserList from 'components/searchable_user_list/searchable_user_list_container.jsx'; +import LoadingScreen from 'components/loading_screen.jsx'; + +import ChannelStore from 'stores/channel_store.jsx'; +import UserStore from 'stores/user_store.jsx'; +import TeamStore from 'stores/team_store.jsx'; + +import {searchUsers} from 'actions/user_actions.jsx'; + +import * as AsyncClient from 'utils/async_client.jsx'; +import * as UserAgent from 'utils/user_agent.jsx'; +import Constants from 'utils/constants.jsx'; + +import React from 'react'; +import {Modal} from 'react-bootstrap'; +import {FormattedMessage} from 'react-intl'; + +import store from 'stores/redux_store.jsx'; +import {searchProfilesNotInCurrentChannel} from 'mattermost-redux/selectors/entities/users'; + +const USERS_PER_PAGE = 50; + +export default class ChannelInviteModal extends React.Component { + static propTypes = { + onHide: React.PropTypes.func.isRequired, + channel: React.PropTypes.object.isRequired, + actions: React.PropTypes.shape({ + getProfilesNotInChannel: React.PropTypes.func.isRequired + }).isRequired + } + + constructor(props) { + super(props); + + this.onChange = this.onChange.bind(this); + this.onStatusChange = this.onStatusChange.bind(this); + this.onHide = this.onHide.bind(this); + this.handleInviteError = this.handleInviteError.bind(this); + this.nextPage = this.nextPage.bind(this); + this.search = this.search.bind(this); + + this.term = ''; + this.searchTimeoutId = 0; + + const channelStats = ChannelStore.getStats(props.channel.id); + const teamStats = TeamStore.getCurrentStats(); + + this.state = { + users: UserStore.getProfileListNotInChannel(props.channel.id, true), + total: teamStats.active_member_count - channelStats.member_count, + show: true, + statusChange: false + }; + } + + componentDidMount() { + TeamStore.addStatsChangeListener(this.onChange); + ChannelStore.addStatsChangeListener(this.onChange); + UserStore.addNotInChannelChangeListener(this.onChange); + UserStore.addStatusesChangeListener(this.onStatusChange); + + this.props.actions.getProfilesNotInChannel(TeamStore.getCurrentId(), this.props.channel.id, 0); + AsyncClient.getTeamStats(TeamStore.getCurrentId()); + } + + componentWillUnmount() { + TeamStore.removeStatsChangeListener(this.onChange); + ChannelStore.removeStatsChangeListener(this.onChange); + UserStore.removeNotInChannelChangeListener(this.onChange); + UserStore.removeStatusesChangeListener(this.onStatusChange); + } + + onChange() { + let users; + if (this.term) { + users = searchProfilesNotInCurrentChannel(store.getState(), this.term, true); + } else { + users = UserStore.getProfileListNotInChannel(this.props.channel.id, true); + } + + const channelStats = ChannelStore.getStats(this.props.channel.id); + const teamStats = TeamStore.getCurrentStats(); + + this.setState({ + users, + total: teamStats.active_member_count - channelStats.member_count + }); + } + + onStatusChange() { + // Initiate a render to pick up on new statuses + this.setState({ + statusChange: !this.state.statusChange + }); + } + + onHide() { + this.setState({show: false}); + } + + handleInviteError(err) { + if (err) { + this.setState({ + inviteError: err.message + }); + } else { + this.setState({ + inviteError: null + }); + } + } + + nextPage(page) { + this.props.actions.getProfilesNotInChannel(TeamStore.getCurrentId(), this.props.channel.id, (page + 1) * USERS_PER_PAGE, USERS_PER_PAGE); + } + + search(term) { + clearTimeout(this.searchTimeoutId); + this.term = term; + + if (term === '') { + this.onChange(); + return; + } + + this.searchTimeoutId = setTimeout( + () => { + searchUsers(term, TeamStore.getCurrentId(), {not_in_channel_id: this.props.channel.id}); + }, + Constants.SEARCH_TIMEOUT_MILLISECONDS + ); + } + + render() { + let inviteError = null; + if (this.state.inviteError) { + inviteError = (<label className='has-error control-label'>{this.state.inviteError}</label>); + } + + let content; + if (this.state.loading) { + content = (<LoadingScreen/>); + } else { + content = ( + <SearchableUserList + users={this.state.users} + usersPerPage={USERS_PER_PAGE} + total={this.state.total} + nextPage={this.nextPage} + search={this.search} + actions={[ChannelInviteButton]} + focusOnMount={!UserAgent.isMobile()} + actionProps={{ + channel: this.props.channel, + onInviteError: this.handleInviteError + }} + /> + ); + } + + return ( + <Modal + dialogClassName='more-modal' + show={this.state.show} + onHide={this.onHide} + onExited={this.props.onHide} + > + <Modal.Header closeButton={true}> + <Modal.Title> + <FormattedMessage + id='channel_invite.addNewMembers' + defaultMessage='Add New Members to ' + /> + <span className='name'>{this.props.channel.display_name}</span> + </Modal.Title> + </Modal.Header> + <Modal.Body> + {inviteError} + {content} + </Modal.Body> + </Modal> + ); + } +} diff --git a/webapp/components/channel_invite_modal/index.js b/webapp/components/channel_invite_modal/index.js new file mode 100644 index 000000000..c8bdb54f5 --- /dev/null +++ b/webapp/components/channel_invite_modal/index.js @@ -0,0 +1,24 @@ +// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import {getProfilesNotInChannel} from 'mattermost-redux/actions/users'; + +import ChannelInviteModal from './channel_invite_modal.jsx'; + +function mapStateToProps(state, ownProps) { + return { + ...ownProps + }; +} + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + getProfilesNotInChannel + }, dispatch) + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(ChannelInviteModal); |