From 7307156c49b194c4afd946cd9e57715d45b5b21d Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Wed, 26 Apr 2017 15:49:15 -0400 Subject: PLT-6213 Move team store and actions over to use redux (#6222) * Move team store and actions over to user redux * Fix JS error when inviting by email --- .../components/admin_console/system_users/index.js | 27 ++ .../admin_console/system_users/system_users.jsx | 16 +- webapp/components/analytics/team_analytics.jsx | 360 -------------------- .../components/analytics/team_analytics/index.js | 24 ++ .../analytics/team_analytics/team_analytics.jsx | 366 +++++++++++++++++++++ .../channel_invite_modal/channel_invite_modal.jsx | 6 +- webapp/components/channel_invite_modal/index.js | 4 +- webapp/components/member_list_team.jsx | 169 ---------- webapp/components/member_list_team/index.js | 24 ++ .../member_list_team/member_list_team.jsx | 166 ++++++++++ webapp/components/select_team/index.js | 24 ++ webapp/components/select_team/select_team.jsx | 8 +- webapp/components/team_general_tab.jsx | 8 +- webapp/components/team_members_dropdown/index.js | 4 +- .../team_members_dropdown.jsx | 9 +- webapp/components/team_members_modal.jsx | 2 +- webapp/components/team_sidebar/index.js | 24 ++ .../team_sidebar/team_sidebar_controller.jsx | 9 +- 18 files changed, 699 insertions(+), 551 deletions(-) create mode 100644 webapp/components/admin_console/system_users/index.js delete mode 100644 webapp/components/analytics/team_analytics.jsx create mode 100644 webapp/components/analytics/team_analytics/index.js create mode 100644 webapp/components/analytics/team_analytics/team_analytics.jsx delete mode 100644 webapp/components/member_list_team.jsx create mode 100644 webapp/components/member_list_team/index.js create mode 100644 webapp/components/member_list_team/member_list_team.jsx create mode 100644 webapp/components/select_team/index.js create mode 100644 webapp/components/team_sidebar/index.js (limited to 'webapp/components') diff --git a/webapp/components/admin_console/system_users/index.js b/webapp/components/admin_console/system_users/index.js new file mode 100644 index 000000000..24144d701 --- /dev/null +++ b/webapp/components/admin_console/system_users/index.js @@ -0,0 +1,27 @@ +// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import {getTeams, getTeamStats} from 'mattermost-redux/actions/teams'; +import {getUser} from 'mattermost-redux/actions/users'; + +import SystemUsers from './system_users.jsx'; + +function mapStateToProps(state, ownProps) { + return { + ...ownProps + }; +} + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + getTeams, + getTeamStats, + getUser + }, dispatch) + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(SystemUsers); diff --git a/webapp/components/admin_console/system_users/system_users.jsx b/webapp/components/admin_console/system_users/system_users.jsx index 7bc4b81ed..29fcd634b 100644 --- a/webapp/components/admin_console/system_users/system_users.jsx +++ b/webapp/components/admin_console/system_users/system_users.jsx @@ -16,7 +16,7 @@ import AnalyticsStore from 'stores/analytics_store.jsx'; import TeamStore from 'stores/team_store.jsx'; import UserStore from 'stores/user_store.jsx'; -import {getAllTeams, getStandardAnalytics, getTeamStats, getUser} from 'utils/async_client.jsx'; +import {getStandardAnalytics} from 'utils/async_client.jsx'; import {Constants, StatTypes, UserSearchOptions} from 'utils/constants.jsx'; import {convertTeamMapToList} from 'utils/team_utils.jsx'; import * as Utils from 'utils/utils.jsx'; @@ -33,6 +33,14 @@ const USER_ID_LENGTH = 26; const USERS_PER_PAGE = 50; export default class SystemUsers extends React.Component { + static propTypes = { + actions: React.PropTypes.shape({ + getTeams: React.PropTypes.func.isRequired, + getTeamStats: React.PropTypes.func.isRequired, + getUser: React.PropTypes.func.isRequired + }).isRequired + } + constructor(props) { super(props); @@ -76,7 +84,7 @@ export default class SystemUsers extends React.Component { UserStore.addWithoutTeamChangeListener(this.updateUsersFromStore); this.loadDataForTeam(this.state.teamId); - getAllTeams(); + this.props.actions.getTeams(0, 1000); } componentWillUpdate(nextProps, nextState) { @@ -155,7 +163,7 @@ export default class SystemUsers extends React.Component { loadProfilesWithoutTeam(0, Constants.PROFILE_CHUNK_SIZE, this.loadComplete); } else { loadProfilesAndTeamMembers(0, Constants.PROFILE_CHUNK_SIZE, teamId, this.loadComplete); - getTeamStats(teamId); + this.props.actions.getTeamStats(teamId); } } @@ -240,7 +248,7 @@ export default class SystemUsers extends React.Component { return; } - getUser( + this.props.actions.getUser( id, () => { this.setState({ diff --git a/webapp/components/analytics/team_analytics.jsx b/webapp/components/analytics/team_analytics.jsx deleted file mode 100644 index 700dc5a10..000000000 --- a/webapp/components/analytics/team_analytics.jsx +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import React from 'react'; -import {FormattedDate, FormattedMessage, FormattedHTMLMessage} from 'react-intl'; - -import Banner from 'components/admin_console/banner.jsx'; -import LoadingScreen from 'components/loading_screen.jsx'; - -import AdminStore from 'stores/admin_store.jsx'; -import AnalyticsStore from 'stores/analytics_store.jsx'; -import BrowserStore from 'stores/browser_store.jsx'; - -import * as AsyncClient from 'utils/async_client.jsx'; -import {StatTypes} from 'utils/constants.jsx'; -import {convertTeamMapToList} from 'utils/team_utils.jsx'; - -import LineChart from './line_chart.jsx'; -import StatisticCount from './statistic_count.jsx'; -import TableChart from './table_chart.jsx'; -import {formatPostsPerDayData, formatUsersWithPostsPerDayData} from './system_analytics.jsx'; - -const LAST_ANALYTICS_TEAM = 'last_analytics_team'; - -export default class TeamAnalytics extends React.Component { - constructor(props) { - super(props); - - this.onChange = this.onChange.bind(this); - this.onAllTeamsChange = this.onAllTeamsChange.bind(this); - this.handleTeamChange = this.handleTeamChange.bind(this); - - const teams = convertTeamMapToList(AdminStore.getAllTeams()); - const teamId = BrowserStore.getGlobalItem(LAST_ANALYTICS_TEAM, teams.length > 0 ? teams[0].id : ''); - - this.state = { - teams, - teamId, - team: AdminStore.getTeam(teamId), - stats: AnalyticsStore.getAllTeam(teamId) - }; - } - - componentDidMount() { - AnalyticsStore.addChangeListener(this.onChange); - AdminStore.addAllTeamsChangeListener(this.onAllTeamsChange); - - if (this.state.teamId !== '') { - this.getData(this.state.teamId); - } - - if (this.state.teams.length === 0) { - AsyncClient.getAllTeams(); - } - } - - componentWillUpdate(nextProps, nextState) { - if (nextState.teamId !== this.state.teamId) { - this.getData(nextState.teamId); - } - } - - getData(id) { - AsyncClient.getStandardAnalytics(id); - AsyncClient.getPostsPerDayAnalytics(id); - AsyncClient.getUsersPerDayAnalytics(id); - AsyncClient.getRecentAndNewUsersAnalytics(id); - } - - componentWillUnmount() { - AnalyticsStore.removeChangeListener(this.onChange); - AdminStore.removeAllTeamsChangeListener(this.onAllTeamsChange); - } - - onChange() { - this.setState({ - stats: AnalyticsStore.getAllTeam(this.state.teamId) - }); - } - - onAllTeamsChange() { - const teams = convertTeamMapToList(AdminStore.getAllTeams()); - - if (teams.length > 0) { - if (this.state.teamId) { - this.setState({ - team: AdminStore.getTeam(this.state.teamId) - }); - } else { - this.setState({ - teamId: teams[0].id, - team: teams[0] - }); - } - } - - this.setState({ - teams - }); - } - - handleTeamChange(e) { - const teamId = e.target.value; - - this.setState({ - teamId, - team: AdminStore.getTeam(teamId) - }); - - BrowserStore.setGlobalItem(LAST_ANALYTICS_TEAM, teamId); - } - - render() { - if (this.state.teams.length === 0 || !this.state.team || !this.state.stats) { - return ; - } - - if (this.state.teamId === '') { - return ( - - } - /> - ); - } - - const stats = this.state.stats; - const postCountsDay = formatPostsPerDayData(stats[StatTypes.POST_PER_DAY]); - const userCountsWithPostsDay = formatUsersWithPostsPerDayData(stats[StatTypes.USERS_WITH_POSTS_PER_DAY]); - - let banner; - let totalPostsCount; - let postTotalGraph; - let userActiveGraph; - if (stats[StatTypes.TOTAL_POSTS] === -1) { - banner = ( - - } - /> - ); - } else { - totalPostsCount = ( - - } - icon='fa-comment' - count={stats[StatTypes.TOTAL_POSTS]} - /> - ); - - postTotalGraph = ( -
- - } - data={postCountsDay} - options={{ - legend: { - display: false - } - }} - width='740' - height='225' - /> -
- ); - - userActiveGraph = ( -
- - } - data={userCountsWithPostsDay} - options={{ - legend: { - display: false - } - }} - width='740' - height='225' - /> -
- ); - } - - const recentActiveUsers = formatRecentUsersData(stats[StatTypes.RECENTLY_ACTIVE_USERS]); - const newlyCreatedUsers = formatNewUsersData(stats[StatTypes.NEWLY_CREATED_USERS]); - - const teams = this.state.teams.map((team) => { - return ( - - ); - }); - - return ( -
-
-
-

- -

-
-
- -
-
- {banner} -
- - } - icon='fa-user' - count={stats[StatTypes.TOTAL_USERS]} - /> - - } - icon='fa-users' - count={stats[StatTypes.TOTAL_PUBLIC_CHANNELS]} - /> - - } - icon='fa-globe' - count={stats[StatTypes.TOTAL_PRIVATE_GROUPS]} - /> - {totalPostsCount} -
- {postTotalGraph} - {userActiveGraph} -
- - } - data={recentActiveUsers} - /> - - } - data={newlyCreatedUsers} - /> -
-
- ); - } -} - -export function formatRecentUsersData(data) { - if (data == null) { - return []; - } - - const formattedData = data.map((user) => { - const item = {}; - item.name = user.username; - item.value = ( - - ); - item.tip = user.email; - - return item; - }); - - return formattedData; -} - -export function formatNewUsersData(data) { - if (data == null) { - return []; - } - - const formattedData = data.map((user) => { - const item = {}; - item.name = user.username; - item.value = ( - - ); - item.tip = user.email; - - return item; - }); - - return formattedData; -} diff --git a/webapp/components/analytics/team_analytics/index.js b/webapp/components/analytics/team_analytics/index.js new file mode 100644 index 000000000..270967a1b --- /dev/null +++ b/webapp/components/analytics/team_analytics/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 {getTeams} from 'mattermost-redux/actions/teams'; + +import TeamAnalytics from './team_analytics.jsx'; + +function mapStateToProps(state, ownProps) { + return { + ...ownProps + }; +} + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + getTeams + }, dispatch) + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(TeamAnalytics); diff --git a/webapp/components/analytics/team_analytics/team_analytics.jsx b/webapp/components/analytics/team_analytics/team_analytics.jsx new file mode 100644 index 000000000..828f29f51 --- /dev/null +++ b/webapp/components/analytics/team_analytics/team_analytics.jsx @@ -0,0 +1,366 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import React from 'react'; +import {FormattedDate, FormattedMessage, FormattedHTMLMessage} from 'react-intl'; + +import Banner from 'components/admin_console/banner.jsx'; +import LoadingScreen from 'components/loading_screen.jsx'; + +import AdminStore from 'stores/admin_store.jsx'; +import AnalyticsStore from 'stores/analytics_store.jsx'; +import BrowserStore from 'stores/browser_store.jsx'; + +import * as AsyncClient from 'utils/async_client.jsx'; +import {StatTypes} from 'utils/constants.jsx'; +import {convertTeamMapToList} from 'utils/team_utils.jsx'; + +import LineChart from 'components/analytics/line_chart.jsx'; +import StatisticCount from 'components/analytics/statistic_count.jsx'; +import TableChart from 'components/analytics/table_chart.jsx'; +import {formatPostsPerDayData, formatUsersWithPostsPerDayData} from 'components/analytics/system_analytics.jsx'; + +const LAST_ANALYTICS_TEAM = 'last_analytics_team'; + +export default class TeamAnalytics extends React.Component { + static propTypes = { + actions: React.PropTypes.shape({ + getTeams: React.PropTypes.func.isRequired + }).isRequired + } + + constructor(props) { + super(props); + + this.onChange = this.onChange.bind(this); + this.onAllTeamsChange = this.onAllTeamsChange.bind(this); + this.handleTeamChange = this.handleTeamChange.bind(this); + + const teams = convertTeamMapToList(AdminStore.getAllTeams()); + const teamId = BrowserStore.getGlobalItem(LAST_ANALYTICS_TEAM, teams.length > 0 ? teams[0].id : ''); + + this.state = { + teams, + teamId, + team: AdminStore.getTeam(teamId), + stats: AnalyticsStore.getAllTeam(teamId) + }; + } + + componentDidMount() { + AnalyticsStore.addChangeListener(this.onChange); + AdminStore.addAllTeamsChangeListener(this.onAllTeamsChange); + + if (this.state.teamId !== '') { + this.getData(this.state.teamId); + } + + if (this.state.teams.length === 0) { + this.props.actions.getTeams(0, 1000); + } + } + + componentWillUpdate(nextProps, nextState) { + if (nextState.teamId !== this.state.teamId) { + this.getData(nextState.teamId); + } + } + + getData(id) { + AsyncClient.getStandardAnalytics(id); + AsyncClient.getPostsPerDayAnalytics(id); + AsyncClient.getUsersPerDayAnalytics(id); + AsyncClient.getRecentAndNewUsersAnalytics(id); + } + + componentWillUnmount() { + AnalyticsStore.removeChangeListener(this.onChange); + AdminStore.removeAllTeamsChangeListener(this.onAllTeamsChange); + } + + onChange() { + this.setState({ + stats: AnalyticsStore.getAllTeam(this.state.teamId) + }); + } + + onAllTeamsChange() { + const teams = convertTeamMapToList(AdminStore.getAllTeams()); + + if (teams.length > 0) { + if (this.state.teamId) { + this.setState({ + team: AdminStore.getTeam(this.state.teamId) + }); + } else { + this.setState({ + teamId: teams[0].id, + team: teams[0] + }); + } + } + + this.setState({ + teams + }); + } + + handleTeamChange(e) { + const teamId = e.target.value; + + this.setState({ + teamId, + team: AdminStore.getTeam(teamId) + }); + + BrowserStore.setGlobalItem(LAST_ANALYTICS_TEAM, teamId); + } + + render() { + if (this.state.teams.length === 0 || !this.state.team || !this.state.stats) { + return ; + } + + if (this.state.teamId === '') { + return ( + + } + /> + ); + } + + const stats = this.state.stats; + const postCountsDay = formatPostsPerDayData(stats[StatTypes.POST_PER_DAY]); + const userCountsWithPostsDay = formatUsersWithPostsPerDayData(stats[StatTypes.USERS_WITH_POSTS_PER_DAY]); + + let banner; + let totalPostsCount; + let postTotalGraph; + let userActiveGraph; + if (stats[StatTypes.TOTAL_POSTS] === -1) { + banner = ( + + } + /> + ); + } else { + totalPostsCount = ( + + } + icon='fa-comment' + count={stats[StatTypes.TOTAL_POSTS]} + /> + ); + + postTotalGraph = ( +
+ + } + data={postCountsDay} + options={{ + legend: { + display: false + } + }} + width='740' + height='225' + /> +
+ ); + + userActiveGraph = ( +
+ + } + data={userCountsWithPostsDay} + options={{ + legend: { + display: false + } + }} + width='740' + height='225' + /> +
+ ); + } + + const recentActiveUsers = formatRecentUsersData(stats[StatTypes.RECENTLY_ACTIVE_USERS]); + const newlyCreatedUsers = formatNewUsersData(stats[StatTypes.NEWLY_CREATED_USERS]); + + const teams = this.state.teams.map((team) => { + return ( + + ); + }); + + return ( +
+
+
+

+ +

+
+
+ +
+
+ {banner} +
+ + } + icon='fa-user' + count={stats[StatTypes.TOTAL_USERS]} + /> + + } + icon='fa-users' + count={stats[StatTypes.TOTAL_PUBLIC_CHANNELS]} + /> + + } + icon='fa-globe' + count={stats[StatTypes.TOTAL_PRIVATE_GROUPS]} + /> + {totalPostsCount} +
+ {postTotalGraph} + {userActiveGraph} +
+ + } + data={recentActiveUsers} + /> + + } + data={newlyCreatedUsers} + /> +
+
+ ); + } +} + +export function formatRecentUsersData(data) { + if (data == null) { + return []; + } + + const formattedData = data.map((user) => { + const item = {}; + item.name = user.username; + item.value = ( + + ); + item.tip = user.email; + + return item; + }); + + return formattedData; +} + +export function formatNewUsersData(data) { + if (data == null) { + return []; + } + + const formattedData = data.map((user) => { + const item = {}; + item.name = user.username; + item.value = ( + + ); + item.tip = user.email; + + return item; + }); + + return formattedData; +} diff --git a/webapp/components/channel_invite_modal/channel_invite_modal.jsx b/webapp/components/channel_invite_modal/channel_invite_modal.jsx index 847af16f6..4be0d23e5 100644 --- a/webapp/components/channel_invite_modal/channel_invite_modal.jsx +++ b/webapp/components/channel_invite_modal/channel_invite_modal.jsx @@ -11,7 +11,6 @@ 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'; @@ -29,7 +28,8 @@ export default class ChannelInviteModal extends React.Component { onHide: React.PropTypes.func.isRequired, channel: React.PropTypes.object.isRequired, actions: React.PropTypes.shape({ - getProfilesNotInChannel: React.PropTypes.func.isRequired + getProfilesNotInChannel: React.PropTypes.func.isRequired, + getTeamStats: React.PropTypes.func.isRequired }).isRequired } @@ -64,7 +64,7 @@ export default class ChannelInviteModal extends React.Component { UserStore.addStatusesChangeListener(this.onStatusChange); this.props.actions.getProfilesNotInChannel(TeamStore.getCurrentId(), this.props.channel.id, 0); - AsyncClient.getTeamStats(TeamStore.getCurrentId()); + this.props.actions.getTeamStats(TeamStore.getCurrentId()); } componentWillUnmount() { diff --git a/webapp/components/channel_invite_modal/index.js b/webapp/components/channel_invite_modal/index.js index c8bdb54f5..a89a94a4c 100644 --- a/webapp/components/channel_invite_modal/index.js +++ b/webapp/components/channel_invite_modal/index.js @@ -4,6 +4,7 @@ import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; import {getProfilesNotInChannel} from 'mattermost-redux/actions/users'; +import {getTeamStats} from 'mattermost-redux/actions/teams'; import ChannelInviteModal from './channel_invite_modal.jsx'; @@ -16,7 +17,8 @@ function mapStateToProps(state, ownProps) { function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ - getProfilesNotInChannel + getProfilesNotInChannel, + getTeamStats }, dispatch) }; } diff --git a/webapp/components/member_list_team.jsx b/webapp/components/member_list_team.jsx deleted file mode 100644 index 212536dc8..000000000 --- a/webapp/components/member_list_team.jsx +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import SearchableUserList from 'components/searchable_user_list/searchable_user_list_container.jsx'; -import TeamMembersDropdown from 'components/team_members_dropdown'; - -import UserStore from 'stores/user_store.jsx'; -import TeamStore from 'stores/team_store.jsx'; - -import {searchUsers, loadProfilesAndTeamMembers, loadTeamMembersForProfilesList} from 'actions/user_actions.jsx'; -import {getTeamStats} from 'utils/async_client.jsx'; - -import Constants from 'utils/constants.jsx'; - -import * as UserAgent from 'utils/user_agent.jsx'; - -import React from 'react'; - -import store from 'stores/redux_store.jsx'; -import {searchProfilesInCurrentTeam} from 'mattermost-redux/selectors/entities/users'; - -const USERS_PER_PAGE = 50; - -export default class MemberListTeam extends React.Component { - constructor(props) { - super(props); - - this.onChange = this.onChange.bind(this); - this.onTeamChange = this.onTeamChange.bind(this); - this.onStatsChange = this.onStatsChange.bind(this); - this.search = this.search.bind(this); - this.loadComplete = this.loadComplete.bind(this); - - this.searchTimeoutId = 0; - this.term = ''; - - const stats = TeamStore.getCurrentStats(); - - this.state = { - users: UserStore.getProfileListInTeam(), - teamMembers: Object.assign([], TeamStore.getMembersInTeam()), - total: stats.total_member_count, - loading: true - }; - } - - componentDidMount() { - UserStore.addInTeamChangeListener(this.onTeamChange); - UserStore.addStatusesChangeListener(this.onChange); - TeamStore.addChangeListener(this.onTeamChange); - TeamStore.addStatsChangeListener(this.onStatsChange); - - loadProfilesAndTeamMembers(0, Constants.PROFILE_CHUNK_SIZE, TeamStore.getCurrentId(), this.loadComplete); - getTeamStats(TeamStore.getCurrentId()); - } - - componentWillUnmount() { - UserStore.removeInTeamChangeListener(this.onTeamChange); - UserStore.removeStatusesChangeListener(this.onChange); - TeamStore.removeChangeListener(this.onTeamChange); - TeamStore.removeStatsChangeListener(this.onStatsChange); - } - - loadComplete() { - this.setState({loading: false}); - } - - onTeamChange() { - this.onChange(true); - } - - onChange() { - let users; - if (this.term) { - users = searchProfilesInCurrentTeam(store.getState(), this.term); - } else { - users = UserStore.getProfileListInTeam(); - } - - this.setState({users, teamMembers: Object.assign([], TeamStore.getMembersInTeam())}); - } - - onStatsChange() { - const stats = TeamStore.getCurrentStats(); - this.setState({total: stats.total_member_count}); - } - - nextPage(page) { - loadProfilesAndTeamMembers(page, USERS_PER_PAGE); - } - - search(term) { - clearTimeout(this.searchTimeoutId); - this.term = term; - - if (term === '') { - this.setState({loading: false}); - this.searchTimeoutId = ''; - this.onChange(); - return; - } - - const searchTimeoutId = setTimeout( - () => { - searchUsers( - term, - TeamStore.getCurrentId(), - {}, - (users) => { - if (searchTimeoutId !== this.searchTimeoutId) { - return; - } - this.setState({loading: true}); - loadTeamMembersForProfilesList(users, TeamStore.getCurrentId(), this.loadComplete); - } - ); - }, - Constants.SEARCH_TIMEOUT_MILLISECONDS - ); - - this.searchTimeoutId = searchTimeoutId; - } - - render() { - let teamMembersDropdown = null; - if (this.props.isAdmin) { - teamMembersDropdown = [TeamMembersDropdown]; - } - - const teamMembers = this.state.teamMembers; - const users = this.state.users; - const actionUserProps = {}; - - let usersToDisplay; - if (this.state.loading) { - usersToDisplay = null; - } else { - usersToDisplay = []; - - for (let i = 0; i < users.length; i++) { - const user = users[i]; - - if (teamMembers[user.id]) { - usersToDisplay.push(user); - actionUserProps[user.id] = { - teamMember: teamMembers[user.id] - }; - } - } - } - - return ( - - ); - } -} - -MemberListTeam.propTypes = { - isAdmin: React.PropTypes.bool -}; diff --git a/webapp/components/member_list_team/index.js b/webapp/components/member_list_team/index.js new file mode 100644 index 000000000..dc5b0b9f2 --- /dev/null +++ b/webapp/components/member_list_team/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 {getTeamStats} from 'mattermost-redux/actions/teams'; + +import MemberListTeam from './member_list_team.jsx'; + +function mapStateToProps(state, ownProps) { + return { + ...ownProps + }; +} + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + getTeamStats + }, dispatch) + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(MemberListTeam); diff --git a/webapp/components/member_list_team/member_list_team.jsx b/webapp/components/member_list_team/member_list_team.jsx new file mode 100644 index 000000000..40d65c7f1 --- /dev/null +++ b/webapp/components/member_list_team/member_list_team.jsx @@ -0,0 +1,166 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import SearchableUserList from 'components/searchable_user_list/searchable_user_list_container.jsx'; +import TeamMembersDropdown from 'components/team_members_dropdown'; + +import UserStore from 'stores/user_store.jsx'; +import TeamStore from 'stores/team_store.jsx'; + +import {searchUsers, loadProfilesAndTeamMembers, loadTeamMembersForProfilesList} from 'actions/user_actions.jsx'; + +import Constants from 'utils/constants.jsx'; + +import * as UserAgent from 'utils/user_agent.jsx'; + +import React from 'react'; + +import store from 'stores/redux_store.jsx'; +import {searchProfilesInCurrentTeam} from 'mattermost-redux/selectors/entities/users'; + +const USERS_PER_PAGE = 50; + +export default class MemberListTeam extends React.Component { + static propTypes = { + isAdmin: React.PropTypes.bool, + actions: React.PropTypes.shape({ + getTeamStats: React.PropTypes.func.isRequired + }).isRequired + } + + constructor(props) { + super(props); + + this.onChange = this.onChange.bind(this); + this.onStatsChange = this.onStatsChange.bind(this); + this.search = this.search.bind(this); + this.loadComplete = this.loadComplete.bind(this); + + this.searchTimeoutId = 0; + this.term = ''; + + const stats = TeamStore.getCurrentStats(); + + this.state = { + users: UserStore.getProfileListInTeam(), + teamMembers: Object.assign([], TeamStore.getMembersInTeam()), + total: stats.total_member_count, + loading: true + }; + } + + componentDidMount() { + UserStore.addInTeamChangeListener(this.onChange); + UserStore.addStatusesChangeListener(this.onChange); + TeamStore.addChangeListener(this.onChange); + TeamStore.addStatsChangeListener(this.onStatsChange); + + loadProfilesAndTeamMembers(0, Constants.PROFILE_CHUNK_SIZE, TeamStore.getCurrentId(), this.loadComplete); + this.props.actions.getTeamStats(TeamStore.getCurrentId()); + } + + componentWillUnmount() { + UserStore.removeInTeamChangeListener(this.onChange); + UserStore.removeStatusesChangeListener(this.onChange); + TeamStore.removeChangeListener(this.onChange); + TeamStore.removeStatsChangeListener(this.onStatsChange); + } + + loadComplete() { + this.setState({loading: false}); + } + + onChange() { + let users; + if (this.term) { + users = searchProfilesInCurrentTeam(store.getState(), this.term); + } else { + users = UserStore.getProfileListInTeam(); + } + + this.setState({users, teamMembers: Object.assign([], TeamStore.getMembersInTeam())}); + } + + onStatsChange() { + const stats = TeamStore.getCurrentStats(); + this.setState({total: stats.total_member_count}); + } + + nextPage(page) { + loadProfilesAndTeamMembers(page, USERS_PER_PAGE); + } + + search(term) { + clearTimeout(this.searchTimeoutId); + this.term = term; + + if (term === '') { + this.setState({loading: false}); + this.searchTimeoutId = ''; + this.onChange(); + return; + } + + const searchTimeoutId = setTimeout( + () => { + searchUsers( + term, + TeamStore.getCurrentId(), + {}, + (users) => { + if (searchTimeoutId !== this.searchTimeoutId) { + return; + } + this.setState({loading: true}); + loadTeamMembersForProfilesList(users, TeamStore.getCurrentId(), this.loadComplete); + } + ); + }, + Constants.SEARCH_TIMEOUT_MILLISECONDS + ); + + this.searchTimeoutId = searchTimeoutId; + } + + render() { + let teamMembersDropdown = null; + if (this.props.isAdmin) { + teamMembersDropdown = [TeamMembersDropdown]; + } + + const teamMembers = this.state.teamMembers; + const users = this.state.users; + const actionUserProps = {}; + + let usersToDisplay; + if (this.state.loading) { + usersToDisplay = null; + } else { + usersToDisplay = []; + + for (let i = 0; i < users.length; i++) { + const user = users[i]; + + if (teamMembers[user.id]) { + usersToDisplay.push(user); + actionUserProps[user.id] = { + teamMember: teamMembers[user.id] + }; + } + } + } + + return ( + + ); + } +} diff --git a/webapp/components/select_team/index.js b/webapp/components/select_team/index.js new file mode 100644 index 000000000..87691a853 --- /dev/null +++ b/webapp/components/select_team/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 {getTeams} from 'mattermost-redux/actions/teams'; + +import SelectTeam from './select_team.jsx'; + +function mapStateToProps(state, ownProps) { + return { + ...ownProps + }; +} + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + getTeams + }, dispatch) + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(SelectTeam); diff --git a/webapp/components/select_team/select_team.jsx b/webapp/components/select_team/select_team.jsx index 43472bdad..e6179a2fd 100644 --- a/webapp/components/select_team/select_team.jsx +++ b/webapp/components/select_team/select_team.jsx @@ -7,7 +7,6 @@ import * as UserAgent from 'utils/user_agent.jsx'; import * as Utils from 'utils/utils.jsx'; import ErrorBar from 'components/error_bar.jsx'; import LoadingScreen from 'components/loading_screen.jsx'; -import * as AsyncClient from 'utils/async_client.jsx'; import * as GlobalActions from 'actions/global_actions.jsx'; import SelectTeamItem from './components/select_team_item.jsx'; @@ -19,6 +18,11 @@ import React from 'react'; import logoImage from 'images/logo.png'; export default class SelectTeam extends React.Component { + static propTypes = { + actions: React.PropTypes.shape({ + getTeams: React.PropTypes.func.isRequired + }).isRequired + } constructor(props) { super(props); @@ -33,7 +37,7 @@ export default class SelectTeam extends React.Component { componentDidMount() { TeamStore.addChangeListener(this.onTeamChange); - AsyncClient.getAllTeamListings(); + this.props.actions.getTeams(0, 200); } componentWillUnmount() { diff --git a/webapp/components/team_general_tab.jsx b/webapp/components/team_general_tab.jsx index 21ad6a8a2..0a71546e8 100644 --- a/webapp/components/team_general_tab.jsx +++ b/webapp/components/team_general_tab.jsx @@ -86,7 +86,7 @@ class GeneralTab extends React.Component { var state = {serverError: '', clientError: ''}; - var data = this.props.team; + var data = {...this.props.team}; data.allow_open_invite = this.state.allow_open_invite; updateTeam(data, () => { @@ -119,7 +119,7 @@ class GeneralTab extends React.Component { return; } - var data = this.props.team; + var data = {...this.props.team}; data.display_name = this.state.name; updateTeam(data, () => { @@ -152,7 +152,7 @@ class GeneralTab extends React.Component { return; } - var data = this.props.team; + var data = {...this.props.team}; data.invite_id = this.state.invite_id; updateTeam(data, () => { @@ -189,7 +189,7 @@ class GeneralTab extends React.Component { return; } - var data = this.props.team; + var data = {...this.props.team}; data.description = this.state.description; updateTeam(data, () => { diff --git a/webapp/components/team_members_dropdown/index.js b/webapp/components/team_members_dropdown/index.js index 54e002a6e..9486c89fa 100644 --- a/webapp/components/team_members_dropdown/index.js +++ b/webapp/components/team_members_dropdown/index.js @@ -4,6 +4,7 @@ import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; import {getUser} from 'mattermost-redux/actions/users'; +import {getTeamStats} from 'mattermost-redux/actions/teams'; import TeamMembersDropdown from './team_members_dropdown.jsx'; @@ -16,7 +17,8 @@ function mapStateToProps(state, ownProps) { function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ - getUser + getUser, + getTeamStats }, dispatch) }; } diff --git a/webapp/components/team_members_dropdown/team_members_dropdown.jsx b/webapp/components/team_members_dropdown/team_members_dropdown.jsx index 704a60dae..00441ba37 100644 --- a/webapp/components/team_members_dropdown/team_members_dropdown.jsx +++ b/webapp/components/team_members_dropdown/team_members_dropdown.jsx @@ -22,7 +22,8 @@ export default class TeamMembersDropdown extends React.Component { user: React.PropTypes.object.isRequired, teamMember: React.PropTypes.object.isRequired, actions: React.PropTypes.shape({ - getUser: React.PropTypes.func.isRequired + getUser: React.PropTypes.func.isRequired, + getTeamStats: React.PropTypes.func.isRequired }).isRequired } @@ -76,7 +77,7 @@ export default class TeamMembersDropdown extends React.Component { () => { UserStore.removeProfileFromTeam(this.props.teamMember.team_id, this.props.user.id); UserStore.emitInTeamChange(); - AsyncClient.getTeamStats(this.props.teamMember.team_id); + this.props.actions.getTeamStats(this.props.teamMember.team_id); }, (err) => { this.setState({serverError: err.message}); @@ -88,7 +89,7 @@ export default class TeamMembersDropdown extends React.Component { updateActive(this.props.user.id, true, () => { AsyncClient.getChannelStats(ChannelStore.getCurrentId()); - AsyncClient.getTeamStats(this.props.teamMember.team_id); + this.props.actions.getTeamStats(this.props.teamMember.team_id); }, (err) => { this.setState({serverError: err.message}); @@ -100,7 +101,7 @@ export default class TeamMembersDropdown extends React.Component { updateActive(this.props.user.id, false, () => { AsyncClient.getChannelStats(ChannelStore.getCurrentId()); - AsyncClient.getTeamStats(this.props.teamMember.team_id); + this.props.actions.getTeamStats(this.props.teamMember.team_id); }, (err) => { this.setState({serverError: err.message}); diff --git a/webapp/components/team_members_modal.jsx b/webapp/components/team_members_modal.jsx index 87b0ff294..a2b963e09 100644 --- a/webapp/components/team_members_modal.jsx +++ b/webapp/components/team_members_modal.jsx @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -import MemberListTeam from './member_list_team.jsx'; +import MemberListTeam from 'components/member_list_team'; import TeamStore from 'stores/team_store.jsx'; import {FormattedMessage} from 'react-intl'; diff --git a/webapp/components/team_sidebar/index.js b/webapp/components/team_sidebar/index.js new file mode 100644 index 000000000..d130555fd --- /dev/null +++ b/webapp/components/team_sidebar/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 {getTeams} from 'mattermost-redux/actions/teams'; + +import TeamSidebar from './team_sidebar_controller.jsx'; + +function mapStateToProps(state, ownProps) { + return { + ...ownProps + }; +} + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + getTeams + }, dispatch) + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(TeamSidebar); diff --git a/webapp/components/team_sidebar/team_sidebar_controller.jsx b/webapp/components/team_sidebar/team_sidebar_controller.jsx index 758b51426..316466c06 100644 --- a/webapp/components/team_sidebar/team_sidebar_controller.jsx +++ b/webapp/components/team_sidebar/team_sidebar_controller.jsx @@ -6,7 +6,6 @@ import TeamButton from './components/team_button.jsx'; import TeamStore from 'stores/team_store.jsx'; import UserStore from 'stores/user_store.jsx'; -import * as AsyncClient from 'utils/async_client.jsx'; import {sortTeamsByDisplayName} from 'utils/team_utils.jsx'; import * as Utils from 'utils/utils.jsx'; @@ -15,6 +14,12 @@ import React from 'react'; import {FormattedMessage} from 'react-intl'; export default class TeamSidebar extends React.Component { + static propTypes = { + actions: React.PropTypes.shape({ + getTeams: React.PropTypes.func.isRequired + }).isRequired + } + constructor(props) { super(props); @@ -44,7 +49,7 @@ export default class TeamSidebar extends React.Component { window.addEventListener('resize', this.handleResize); TeamStore.addChangeListener(this.onChange); TeamStore.addUnreadChangeListener(this.onChange); - AsyncClient.getAllTeamListings(); + this.props.actions.getTeams(0, 200); this.setStyles(); } -- cgit v1.2.3-1-g7c22