diff options
author | Joram Wilander <jwawilander@gmail.com> | 2016-10-19 14:49:25 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-10-19 14:49:25 -0400 |
commit | 365b8b465e8a53ebb2da2bf3aef659ac81a2bc6a (patch) | |
tree | 643b2dd52b478c2c0b049ac28798d870b9dfd397 /webapp/components/searchable_user_list.jsx | |
parent | 0512bd26ee85473aa47206d5f207a9a506019138 (diff) | |
download | chat-365b8b465e8a53ebb2da2bf3aef659ac81a2bc6a.tar.gz chat-365b8b465e8a53ebb2da2bf3aef659ac81a2bc6a.tar.bz2 chat-365b8b465e8a53ebb2da2bf3aef659ac81a2bc6a.zip |
Merging performance branch into master (#4268)
* improve performance on sendNotifications
* Fix SQL queries
* Remove get direct profiles, not needed anymore
* Add raw data to error details if AppError fails to decode
* men
* Fix decode (#4052)
* Fixing json decode
* Adding unit test
* Initial work for client scaling (#4051)
* Begin adding paging to profiles API
* Added more paging functionality
* Finish hooking up admin console user lists
* Add API for searching users and add searching to all user lists
* Add lazy loading of profiles
* Revert config.json
* Fix unit tests and some style issues
* Add GetProfilesFromList to Go driver and fix web unit test
* Update etag for GetProfiles
* Updating ui for filters and pagination (#4044)
* Updating UI for pagination
* Adjusting margins for filter row
* Adjusting margin for specific modals
* Adding relative padding to system console
* Adjusting responsive view
* Update client user tests
* Minor fixes for direct messages modal (#4056)
* Remove some unneeded initial load calls (#4057)
* UX updates to user lists, added smart counts and bug fixes (#4059)
* Improved getExplicitMentions and unit tests (#4064)
* Refactor getting posts to lazy load profiles correctly (#4062)
* Comment out SetActiveChannel test (#4066)
* Profiler cpu, block, and memory profiler. (#4081)
* Fix TestSetActiveChannel unit test (#4071)
* Fixing build failure caused by dependancies updating (#4076)
* Adding profiler
* Fix admin_team_member_dropdown eslint errors
* Bumping session cache size (#4077)
* Bumping session cache size
* Bumping status cache
* Refactor how the client handles channel members to be large team friendly (#4106)
* Refactor how the client handles channel members to be large team friendly
* Change Id to ChannelId in ChannelStats model
* Updated getChannelMember and getProfilesByIds routes to match proposal
* Performance improvements (#4100)
* Performance improvements
* Fixing re-connect issue
* Fixing error message
* Some other minor perf tweaks
* Some other minor perf tweaks
* Fixing config file
* Fixing buffer size
* Fixing web socket send message
* adding some error logging
* fix getMe to be user required
* Fix websocket event for new user
* Fixing shutting down
* Reverting web socket changes
* Fixing logging lvl
* Adding caching to GetMember
* Adding some logging
* Fixing caching
* Fixing caching invalidate
* Fixing direct message caching
* Fixing caching
* Fixing caching
* Remove GetDirectProfiles from initial load
* Adding logging and fixing websocket client
* Adding back caching from bad merge.
* Explicitly close go driver requests (#4162)
* Refactored how the client handles team members to be more large team friendly (#4159)
* Refactor getProfilesForDirectMessageList API into getAllProfiles API
* Refactored how the client handles team members to be more large team friendly
* Fix js error when receiving a notification
* Fix JS error caused by current user being overwritten with sanitized version (#4165)
* Adding error message to status failure (#4167)
* Fix a few bugs caused by client scaling refactoring (#4170)
* When there is no read replica, don't open a second set of connections to the master database (#4173)
* Adding connection tacking to stats (#4174)
* Reduce DB writes for statuses and other status related changes (#4175)
* Fix bug preventing opening of DM channels from more modal (#4181)
* Fixing socket timing error (#4183)
* Fixing ping/pong handler
* Fixing socket timing error
* Commenting out status broadcasting
* Removing user status changes
* Removing user status changes
* Removing user status changes
* Removing user status changes
* Adding DoPreComputeJson()
* Performance improvements (#4194)
* * Fix System Console Analytics queries
* Add db.SetConnMaxLifetime to 15 minutes
* Add "net/http/pprof" for profiling
* Add FreeOSMemory() to manually release memory on reload config
* Add flag to enable http profiler
* Fix memory leak (#4197)
* Fix memory leak
* removed unneeded nil assignment
* Fixing go routine leak (#4208)
* Merge fixes
* Merge fix
* Refactored statuses to be queried by the client rather than broadcast by the server (#4212)
* Refactored server code to reduce status broadcasts and to allow getting statuses by IDs
* Refactor client code to periodically fetch statuses
* Add store unit test for getting statuses by ids
* Fix status unit test
* Add getStatusesByIds REST API and move the client over to use that instead of the WebSocket
* Adding multiple threads to websocket hub (#4230)
* Adding multiple threads to websocket hub
* Fixing unit tests
* Fixing so websocket connections from the same user end up in the sameā¦ (#4240)
* Fixing so websocket connections from the same user end up in the same list
* Removing old comment
* Refactor user autocomplete to query the server (#4239)
* Add API for autocompleting users
* Converted at mention autocomplete to query server
* Converted user search autocomplete to query server
* Switch autocomplete API naming to use term instead of username
* Split autocomplete API into two, one for channels and for teams
* Fix copy/paste error
* Some final client scaling fixes (#4246)
* Add lazy loading of profiles to integration pages
* Add lazy loading of profiles to emoji page
* Fix JS error when receiving post in select team menu and also clean up channel store
Diffstat (limited to 'webapp/components/searchable_user_list.jsx')
-rw-r--r-- | webapp/components/searchable_user_list.jsx | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/webapp/components/searchable_user_list.jsx b/webapp/components/searchable_user_list.jsx new file mode 100644 index 000000000..8d4f74ab3 --- /dev/null +++ b/webapp/components/searchable_user_list.jsx @@ -0,0 +1,226 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import UserList from 'components/user_list.jsx'; + +import * as Utils from 'utils/utils.jsx'; +import Constants from 'utils/constants.jsx'; +const KeyCodes = Constants.KeyCodes; + +import $ from 'jquery'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import {FormattedMessage} from 'react-intl'; + +const NEXT_BUTTON_TIMEOUT = 500; + +export default class SearchableUserList extends React.Component { + constructor(props) { + super(props); + + this.nextPage = this.nextPage.bind(this); + this.previousPage = this.previousPage.bind(this); + this.doSearch = this.doSearch.bind(this); + this.onSearchBoxKeyPress = this.onSearchBoxKeyPress.bind(this); + this.onSearchBoxChange = this.onSearchBoxChange.bind(this); + + this.nextTimeoutId = 0; + + this.state = { + page: 0, + search: false, + nextDisabled: false + }; + } + + componentDidUpdate(prevProps, prevState) { + if (this.state.page !== prevState.page) { + $(ReactDOM.findDOMNode(this.refs.userList)).scrollTop(0); + } + } + + componentWillUnmount() { + clearTimeout(this.nextTimeoutId); + } + + nextPage(e) { + e.preventDefault(); + this.setState({page: this.state.page + 1, nextDisabled: true}); + this.nextTimeoutId = setTimeout(() => this.setState({nextDisabled: false}), NEXT_BUTTON_TIMEOUT); + this.props.nextPage(this.state.page + 1); + } + + previousPage(e) { + e.preventDefault(); + this.setState({page: this.state.page - 1}); + } + + doSearch() { + const term = this.refs.filter.value; + this.props.search(term); + if (term === '') { + this.setState({page: 0, search: false}); + } else { + this.setState({search: true}); + } + } + + onSearchBoxKeyPress(e) { + if (e.charCode === KeyCodes.ENTER) { + e.preventDefault(); + this.doSearch(); + } + } + + onSearchBoxChange(e) { + if (e.target.value === '') { + this.props.search(''); // clear search + this.setState({page: 0, search: false}); + } + } + + render() { + let nextButton; + let previousButton; + let usersToDisplay; + let count; + + if (this.props.users == null) { + usersToDisplay = this.props.users; + } else if (this.state.search || this.props.users == null) { + usersToDisplay = this.props.users; + + if (this.props.total) { + count = ( + <FormattedMessage + id='filtered_user_list.countTotal' + defaultMessage='{count} {count, plural, =0 {0 members} one {member} other {members}} of {total} total' + values={{ + count: usersToDisplay.length || 0, + total: this.props.total + }} + /> + ); + } + } else { + const pageStart = this.state.page * this.props.usersPerPage; + const pageEnd = pageStart + this.props.usersPerPage; + usersToDisplay = this.props.users.slice(pageStart, pageEnd); + + if (usersToDisplay.length >= this.props.usersPerPage) { + nextButton = ( + <button + className='btn btn-default filter-control filter-control__next' + onClick={this.nextPage} + disabled={this.state.nextDisabled} + > + {'Next'} + </button> + ); + } + + if (this.state.page > 0) { + previousButton = ( + <button + className='btn btn-default filter-control filter-control__prev' + onClick={this.previousPage} + > + {'Previous'} + </button> + ); + } + + if (this.props.total) { + const startCount = this.state.page * this.props.usersPerPage; + const endCount = startCount + usersToDisplay.length; + + count = ( + <FormattedMessage + id='filtered_user_list.countTotalPage' + defaultMessage='{startCount, number} - {endCount, number} {count, plural, =0 {0 members} one {member} other {members}} of {total} total' + values={{ + count: usersToDisplay.length, + startCount: startCount + 1, + endCount, + total: this.props.total + }} + /> + ); + } + } + + return ( + <div + className='filtered-user-list' + style={this.props.style} + > + <div className='filter-row'> + <div className='col-sm-5'> + <input + ref='filter' + className='form-control filter-textbox' + placeholder={Utils.localizeMessage('filtered_user_list.search', 'Press enter to search')} + onKeyPress={this.onSearchBoxKeyPress} + onChange={this.onSearchBoxChange} + /> + </div> + <div className='col-sm-2 filter-button'> + <button + type='button' + className='btn btn-primary' + onClick={this.doSearch} + disabled={this.props.users == null} + > + <FormattedMessage + id='filtered_user_list.searchButton' + defaultMessage='Search' + /> + </button> + </div> + <div className='col-sm-12'> + <span className='member-count pull-left'>{count}</span> + </div> + </div> + <div + ref='userList' + className='more-modal__list' + > + <UserList + users={usersToDisplay} + extraInfo={this.props.extraInfo} + actions={this.props.actions} + actionProps={this.props.actionProps} + actionUserProps={this.props.actionUserProps} + /> + </div> + <div className='filter-controls'> + {previousButton} + {nextButton} + </div> + </div> + ); + } +} + +SearchableUserList.defaultProps = { + users: [], + usersPerPage: 50, //eslint-disable-line no-magic-numbers + extraInfo: {}, + actions: [], + actionProps: {}, + actionUserProps: {}, + showTeamToggle: false +}; + +SearchableUserList.propTypes = { + users: React.PropTypes.arrayOf(React.PropTypes.object), + usersPerPage: React.PropTypes.number, + total: React.PropTypes.number, + extraInfo: React.PropTypes.object, + nextPage: React.PropTypes.func.isRequired, + search: React.PropTypes.func.isRequired, + actions: React.PropTypes.arrayOf(React.PropTypes.func), + actionProps: React.PropTypes.object, + actionUserProps: React.PropTypes.object, + style: React.PropTypes.object +}; |