diff options
Diffstat (limited to 'web/react')
-rw-r--r-- | web/react/components/channel_header.jsx | 103 | ||||
-rw-r--r-- | web/react/components/post_list.jsx | 2 | ||||
-rw-r--r-- | web/react/components/search_bar.jsx | 37 | ||||
-rw-r--r-- | web/react/components/search_results.jsx | 73 |
4 files changed, 92 insertions, 123 deletions
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx index 68de80228..fe381a59e 100644 --- a/web/react/components/channel_header.jsx +++ b/web/react/components/channel_header.jsx @@ -1,6 +1,7 @@ // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // See License.txt for license information. + var ChannelStore = require('../stores/channel_store.jsx'); var UserStore = require('../stores/user_store.jsx'); var PostStore = require('../stores/post_store.jsx'); @@ -16,7 +17,7 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; -var ExtraMembers = React.createClass({ +var PopoverListMembers = React.createClass({ componentDidMount: function() { var originalLeave = $.fn.popover.Constructor.prototype.leave; $.fn.popover.Constructor.prototype.leave = function(obj) { @@ -35,30 +36,29 @@ var ExtraMembers = React.createClass({ $("#member_popover").popover({placement : 'bottom', trigger: 'click', html: true}); $('body').on('click', function (e) { - if ($(e.target.parentNode.parentNode)[0] !== $("#member_popover")[0] && $(e.target).parents('.popover.in').length === 0) { + if ($(e.target.parentNode.parentNode)[0] !== $("#member_popover")[0] && $(e.target).parents('.popover.in').length === 0) { $("#member_popover").popover('hide'); } }); - }, + render: function() { - var count = this.props.members.length == 0 ? "-" : this.props.members.length; - count = this.props.members.length > 19 ? "20+" : count; - var data_content = ""; - var sortedMembers = this.props.members; + var popoverHtml = ''; + var members = this.props.members; + var count = (members.length > 20) ? "20+" : (members.length || '-'); - if(sortedMembers) { - sortedMembers.sort(function(a,b) { + if (members) { + members.sort(function(a,b) { return a.username.localeCompare(b.username); - }) + }); - sortedMembers.forEach(function(m) { - data_content += "<div style='white-space: nowrap'>" + m.username + "</div>"; + members.forEach(function(m) { + popoverHtml += "<div style='white-space: nowrap'>" + m.username + "</div>"; }); } return ( - <div style={{"cursor" : "pointer"}} id="member_popover" data-toggle="popover" data-content={data_content} data-original-title="Members" > + <div style={{cursor : "pointer"}} id="member_popover" data-toggle="popover" data-content={popoverHtml} data-original-title="Members" > <div id="member_tooltip" data-toggle="tooltip" title="View Channel Members"> {count} <span className="glyphicon glyphicon-user" aria-hidden="true"></span> </div> @@ -78,6 +78,7 @@ function getStateFromStores() { } module.exports = React.createClass({ + displayName: 'ChannelHeader', componentDidMount: function() { ChannelStore.addChangeListener(this._onChange); ChannelStore.addExtraInfoChangeListener(this._onChange); @@ -99,7 +100,7 @@ module.exports = React.createClass({ $(".channel-header__info .description").popover({placement : 'bottom', trigger: 'hover', html: true, delay: {show: 500, hide: 500}}); }, _onSocketChange: function(msg) { - if(msg.action === "new_user") { + if (msg.action === "new_user") { AsyncClient.getChannelExtraInfo(true); } }, @@ -107,15 +108,14 @@ module.exports = React.createClass({ return getStateFromStores(); }, handleLeave: function(e) { - var self = this; Client.leaveChannel(this.state.channel.id, function(data) { var townsquare = ChannelStore.getByName('town-square'); utils.switchChannel(townsquare); - }.bind(this), + }, function(err) { AsyncClient.dispatchError(err, "handleLeave"); - }.bind(this) + } ); }, searchMentions: function(e) { @@ -131,52 +131,29 @@ module.exports = React.createClass({ AppDispatcher.handleServerAction({ type: ActionTypes.RECIEVED_SEARCH_TERM, term: terms, - do_search: false + do_search: true, + is_mention_search: true }); - - Client.search( - terms, - function(data) { - AppDispatcher.handleServerAction({ - type: ActionTypes.RECIEVED_SEARCH, - results: data, - is_mention_search: true - }); - }, - function(err) { - dispatchError(err, "search"); - } - ); }, + render: function() { if (this.state.channel == null) { - return ( - <div></div> - ); + return null; } var description = utils.textToJsx(this.state.channel.description, {"singleline": true, "noMentionHighlight": true}); var popoverContent = React.renderToString(<MessageWrapper message={this.state.channel.description}/>); - var channelTitle = ""; + var channelTitle = this.state.channel.display_name; var channelName = this.state.channel.name; var currentId = UserStore.getCurrentId(); var isAdmin = this.state.memberChannel.roles.indexOf("admin") > -1 || this.state.memberTeam.roles.indexOf("admin") > -1; - var searchForm = <th className="search-bar__container"><NavbarSearchBox /></th>; - var isDirect = false; - - if (this.state.channel.type === 'O') { - channelTitle = this.state.channel.display_name; - } else if (this.state.channel.type === 'P') { - channelTitle = this.state.channel.display_name; - } else if (this.state.channel.type === 'D') { - isDirect = true; + var isDirect = (this.state.channel.type === 'D'); + + if (isDirect) { if (this.state.users.length > 1) { - if (this.state.users[0].id === UserStore.getCurrentId()) { - channelTitle = <UserProfile userId={this.state.users[1].id} overwriteName={this.state.users[1].full_name ? this.state.users[1].full_name : this.state.users[1].username} />; - } else { - channelTitle = <UserProfile userId={this.state.users[0].id} overwriteName={this.state.users[0].full_name ? this.state.users[0].full_name : this.state.users[0].username} />; - } + var contact = this.state.users[((this.state.users[0].id === currentId) ? 1 : 0)]; + channelTitle = <UserProfile userId={contact.id} overwriteName={contact.full_name || contact.username} />; } } @@ -196,21 +173,21 @@ module.exports = React.createClass({ <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li> { isAdmin ? <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_members" href="#">Manage Members</a></li> - : "" + : null } - <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#edit_channel" data-desc={this.state.channel.description} data-title={this.state.channel.display_name} data-channelid={this.state.channel.id}>Set Channel Description...</a></li> - <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#channel_notifications" data-title={this.state.channel.display_name} data-channelid={this.state.channel.id}>Notification Preferences</a></li> + <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#edit_channel" data-desc={this.state.channel.description} data-title={channelTitle} data-channelid={this.state.channel.id}>Set Channel Description...</a></li> + <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#channel_notifications" data-title={channelTitle} data-channelid={this.state.channel.id}>Notification Preferences</a></li> { isAdmin && channelName != Constants.DEFAULT_CHANNEL ? - <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#rename_channel" data-display={this.state.channel.display_name} data-name={this.state.channel.name} data-channelid={this.state.channel.id}>Rename Channel...</a></li> - : "" + <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#rename_channel" data-display={channelTitle} data-name={this.state.channel.name} data-channelid={this.state.channel.id}>Rename Channel...</a></li> + : null } { isAdmin && channelName != Constants.DEFAULT_CHANNEL ? - <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#delete_channel" data-title={this.state.channel.display_name} data-channelid={this.state.channel.id}>Delete Channel...</a></li> - : "" + <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#delete_channel" data-title={channelTitle} data-channelid={this.state.channel.id}>Delete Channel...</a></li> + : null } { channelName != Constants.DEFAULT_CHANNEL ? <li role="presentation"><a role="menuitem" href="#" onClick={this.handleLeave}>Leave Channel</a></li> - : "" + : null } </ul> </div> @@ -220,14 +197,14 @@ module.exports = React.createClass({ <a href="#"><strong className="heading">{channelTitle}</strong></a> } </th> - <th><ExtraMembers members={this.state.users} channelId={this.state.channel.id} /></th> - { searchForm } + <th><PopoverListMembers members={this.state.users} channelId={this.state.channel.id} /></th> + <th className="search-bar__container"><NavbarSearchBox /></th> <th> - <div className="dropdown" style={{"marginLeft":"5px", "marginRight":"10px"}}> + <div className="dropdown" style={{marginLeft:5, marginRight:10}}> <a href="#" className="dropdown-toggle theme" type="button" id="channel_header_right_dropdown" data-toggle="dropdown" aria-expanded="true"> <i className="fa fa-caret-down"></i> </a> - <ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_right_dropdown" style={{"left": "-150px"}}> + <ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_right_dropdown" style={{left: "-150px"}}> <li role="presentation"><a role="menuitem" href="#" onClick={this.searchMentions}>Recent Mentions</a></li> </ul> </div> @@ -237,5 +214,3 @@ module.exports = React.createClass({ ); } }); - - diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index e6e028209..2a01b8089 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -297,7 +297,7 @@ module.exports = React.createClass({ }, function(err) { $(self.refs.loadmore.getDOMNode()).text("Load more messages"); - dispatchError(err, "getPosts"); + AsyncClient.dispatchError(err, "getPosts"); } ); }, diff --git a/web/react/components/search_bar.jsx b/web/react/components/search_bar.jsx index cddb738f9..ab7e99d60 100644 --- a/web/react/components/search_bar.jsx +++ b/web/react/components/search_bar.jsx @@ -3,6 +3,7 @@ var client = require('../utils/client.jsx'); +var AsyncClient = require('../utils/async_client.jsx'); var PostStore = require('../stores/post_store.jsx'); var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var utils = require('../utils/utils.jsx'); @@ -10,14 +11,14 @@ var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; function getSearchTermStateFromStores() { - term = PostStore.getSearchTerm(); - if (!term) term = ""; + var term = PostStore.getSearchTerm() || ''; return { search_term: term }; } module.exports = React.createClass({ + displayName: 'SearchBar', componentDidMount: function() { PostStore.addSearchTermChangeListener(this._onChange); }, @@ -58,14 +59,14 @@ module.exports = React.createClass({ e.target.select(); }, performSearch: function(terms, isMentionSearch) { - if (terms.length > 0) { - $("#search-spinner").removeClass("hidden"); + if (terms.length) { + this.setState({isSearching: true}); client.search( terms, function(data) { - $("#search-spinner").addClass("hidden"); - if(utils.isMobile()) { - $('#search')[0].value = ""; + this.setState({isSearching: false}); + if (utils.isMobile()) { + React.findDOMNode(this.refs.search).value = ''; } AppDispatcher.handleServerAction({ @@ -73,18 +74,17 @@ module.exports = React.createClass({ results: data, is_mention_search: isMentionSearch }); - }, + }.bind(this), function(err) { - $("#search-spinner").addClass("hidden"); - dispatchError(err, "search"); - } + this.setState({isSearching: false}); + AsyncClient.dispatchError(err, "search"); + }.bind(this) ); } }, handleSubmit: function(e) { e.preventDefault(); - terms = this.state.search_term.trim(); - this.performSearch(terms); + this.performSearch(this.state.search_term.trim()); }, getInitialState: function() { return getSearchTermStateFromStores(); @@ -95,8 +95,15 @@ module.exports = React.createClass({ <div className="sidebar__collapse" onClick={this.handleClose}></div> <span className="glyphicon glyphicon-search sidebar__search-icon"></span> <form role="form" className="search__form relative-div" onSubmit={this.handleSubmit}> - <input type="text" className="form-control search-bar-box" ref="search" id="search" placeholder="Search" value={this.state.search_term} onFocus={this.handleUserFocus} onChange={this.handleUserInput} /> - <span id="search-spinner" className="glyphicon glyphicon-refresh glyphicon-refresh-animate hidden"></span> + <input + type="text" + ref="search" + className="form-control search-bar-box" + placeholder="Search" + value={this.state.search_term} + onFocus={this.handleUserFocus} + onChange={this.handleUserInput} /> + {this.state.isSearching ? <span className={"glyphicon glyphicon-refresh glyphicon-refresh-animate"}></span> : null} </form> </div> ); diff --git a/web/react/components/search_results.jsx b/web/react/components/search_results.jsx index 156cf0120..1fd974642 100644 --- a/web/react/components/search_results.jsx +++ b/web/react/components/search_results.jsx @@ -8,12 +8,13 @@ var UserStore = require('../stores/user_store.jsx'); var UserProfile = require( './user_profile.jsx' ); var SearchBox =require('./search_bar.jsx'); var utils = require('../utils/utils.jsx'); -var client =require('../utils/client.jsx'); +var client = require('../utils/client.jsx'); +var AsyncClient = require('../utils/async_client.jsx'); var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; -RhsHeaderSearch = React.createClass({ +var RhsHeaderSearch = React.createClass({ handleClose: function(e) { e.preventDefault(); @@ -32,13 +33,13 @@ RhsHeaderSearch = React.createClass({ return ( <div className="sidebar--right__header"> <span className="sidebar--right__title">{title}</span> - <button type="button" className="sidebar--right__close" aria-label="Close" onClick={this.handleClose}></button> + <button type="button" className="sidebar--right__close" aria-label="Close" title="Close" onClick={this.handleClose}></button> </div> ); } }); -SearchItem = React.createClass({ +var SearchItem = React.createClass({ handleClick: function(e) { e.preventDefault(); @@ -62,15 +63,16 @@ SearchItem = React.createClass({ }); }, function(err) { - dispatchError(err, "getPost"); + AsyncClient.dispatchError(err, "getPost"); } ); - var postChannel = ChannelStore.get(this.props.post.channel_id); - var teammate = postChannel.type === 'D' ? utils.getDirectTeammate(this.props.post.channel_id).username : ""; + var postChannel = ChannelStore.get(this.props.post.channel_id); + var teammate = postChannel.type === 'D' ? utils.getDirectTeammate(this.props.post.channel_id).username : ""; - utils.switchChannel(postChannel,teammate); + utils.switchChannel(postChannel, teammate); }, + render: function() { var message = utils.textToJsx(this.props.post.message, {searchTerm: this.props.term, noMentionHighlight: !this.props.isMentionSearch}); @@ -79,14 +81,10 @@ SearchItem = React.createClass({ var timestamp = UserStore.getCurrentUser().update_at; if (channel) { - if (channel.type === 'D') { - channelName = "Private Message"; - } else { - channelName = channel.display_name; - } + channelName = (channel.type === 'D') ? "Private Message" : channel.display_name; } - return ( + return ( <div className="search-item-container post" onClick={this.handleClick}> <div className="search-channel__name">{ channelName }</div> <div className="post-profile-img__container"> @@ -95,7 +93,11 @@ SearchItem = React.createClass({ <div className="post__content"> <ul className="post-header"> <li className="post-header-col"><strong><UserProfile userId={this.props.post.user_id} /></strong></li> - <li className="post-header-col"><time className="search-item-time">{ utils.displayDate(this.props.post.create_at)+' '+utils.displayTime(this.props.post.create_at) }</time></li> + <li className="post-header-col"> + <time className="search-item-time"> + { utils.displayDate(this.props.post.create_at) + ' ' + utils.displayTime(this.props.post.create_at) } + </time> + </li> </ul> <div className="search-item-snippet"><span>{message}</span></div> </div> @@ -104,11 +106,13 @@ SearchItem = React.createClass({ } }); + function getStateFromStores() { return { results: PostStore.getSearchResults() }; } module.exports = React.createClass({ + displayName: 'SearchResults', componentDidMount: function() { PostStore.addSearchChangeListener(this._onChange); this.resize(); @@ -144,41 +148,24 @@ module.exports = React.createClass({ var results = this.state.results; var currentId = UserStore.getCurrentId(); - var searchForm = currentId == null ? null : <SearchBox />; - - if (results == null) { - return ( - <div className="sidebar--right__header"> - <div className="sidebar__heading">Search Results</div> - </div> - ); - } + var searchForm = currentId ? <SearchBox /> : null; + var noResults = (!results || !results.order || !results.order.length); + var searchTerm = PostStore.getSearchTerm(); - if (!results.order || results.order.length == 0) { - return ( - <div className="sidebar--right__content"> - <div className="search-bar__container">{searchForm}</div> - <div className="sidebar-right__body"> - <RhsHeaderSearch /> - <div id="search-items-container" className="search-items-container"> - <div className="sidebar--right__subheader">No results</div> - </div> - </div> - </div> - ); - } - - var self = this; return ( <div className="sidebar--right__content"> <div className="search-bar__container sidebar--right__search-header">{searchForm}</div> <div className="sidebar-right__body"> <RhsHeaderSearch isMentionSearch={this.props.isMentionSearch} /> <div id="search-items-container" className="search-items-container"> - {results.order.map(function(id) { - var post = results.posts[id]; - return <SearchItem key={post.id} post={post} term={PostStore.getSearchTerm()} isMentionSearch={self.props.isMentionSearch} /> - })} + + { noResults ? <div className="sidebar--right__subheader">No results</div> + : results.order.map(function(id) { + var post = results.posts[id]; + return <SearchItem key={post.id} post={post} term={searchTerm} isMentionSearch={this.props.isMentionSearch} /> + }, this) + } + </div> </div> </div> |