summaryrefslogtreecommitdiffstats
path: root/web/react
diff options
context:
space:
mode:
Diffstat (limited to 'web/react')
-rw-r--r--web/react/components/channel_header.jsx127
-rw-r--r--web/react/components/edit_channel_modal.jsx2
-rw-r--r--web/react/components/get_link_modal.jsx2
-rw-r--r--web/react/components/member_list_item.jsx2
-rw-r--r--web/react/components/member_list_team.jsx6
-rw-r--r--web/react/components/mention.jsx10
-rw-r--r--web/react/components/mention_list.jsx118
-rw-r--r--web/react/components/navbar.jsx159
-rw-r--r--web/react/components/new_channel.jsx2
-rw-r--r--web/react/components/post_body.jsx15
-rw-r--r--web/react/components/post_info.jsx7
-rw-r--r--web/react/components/post_list.jsx7
-rw-r--r--web/react/components/post_right.jsx6
-rw-r--r--web/react/components/search_bar.jsx39
-rw-r--r--web/react/components/search_results.jsx73
-rw-r--r--web/react/components/sidebar.jsx4
-rw-r--r--web/react/components/sidebar_header.jsx50
-rw-r--r--web/react/components/signup_user_complete.jsx33
-rw-r--r--web/react/components/user_profile.jsx4
-rw-r--r--web/react/components/user_settings.jsx130
-rw-r--r--web/react/stores/channel_store.jsx53
-rw-r--r--web/react/stores/user_store.jsx18
-rw-r--r--web/react/utils/utils.jsx57
23 files changed, 521 insertions, 403 deletions
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx
index 68de80228..30435dc08 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 class='text--nowrap'>" + m.username + "</div>";
});
}
return (
- <div style={{"cursor" : "pointer"}} id="member_popover" data-toggle="popover" data-content={data_content} data-original-title="Members" >
+ <div 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 channelName = this.state.channel.name;
+ var channel = this.state.channel;
+ var description = utils.textToJsx(channel.description, {"singleline": true, "noMentionHighlight": true});
+ var popoverContent = React.renderToString(<MessageWrapper message={channel.description}/>);
+ var channelTitle = channel.display_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.nickname || contact.username} />;
}
}
@@ -192,25 +169,28 @@ module.exports = React.createClass({
<span className="glyphicon glyphicon-chevron-down header-dropdown__icon"></span>
</a>
<ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_dropdown">
- <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_info" data-channelid={this.state.channel.id} href="#">View Info</a></li>
- <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_info" data-channelid={channel.id} href="#">View Info</a></li>
+ { !ChannelStore.isDefault(channel) ?
+ <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li>
+ : null
+ }
+ { isAdmin && !ChannelStore.isDefault(channel) ?
<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>
- { 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="#edit_channel" data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}>Set Channel Description...</a></li>
+ <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#channel_notifications" data-title={channel.display_name} data-channelid={channel.id}>Notification Preferences</a></li>
+ { isAdmin && !ChannelStore.isDefault(channel) ?
+ <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#rename_channel" data-display={channel.display_name} data-name={channel.name} data-channelid={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>
- : ""
+ { isAdmin && !ChannelStore.isDefault(channel) ?
+ <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#delete_channel" data-title={channel.display_name} data-channelid={channel.id}>Delete Channel...</a></li>
+ : null
}
- { channelName != Constants.DEFAULT_CHANNEL ?
+ { !ChannelStore.isDefault(channel) ?
<li role="presentation"><a role="menuitem" href="#" onClick={this.handleLeave}>Leave Channel</a></li>
- : ""
+ : null
}
</ul>
</div>
@@ -220,14 +200,13 @@ 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={channel.id} /></th>
+ <th className="search-bar__container"><NavbarSearchBox /></th>
<th>
- <div className="dropdown" style={{"marginLeft":"5px", "marginRight":"10px"}}>
+ <div className="dropdown channel-header__links">
<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"}}>
+ <span dangerouslySetInnerHTML={{__html: " <svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'width='4px' height='16px' viewBox='0 0 8 32' enable-background='new 0 0 8 32' xml:space='preserve'> <g> <circle cx='4' cy='4.062' r='4'/> <circle cx='4' cy='16' r='4'/> <circle cx='4' cy='28' r='4'/> </g> </svg>"}} /> </a>
+ <ul className="dropdown-menu dropdown-menu-right" role="menu" aria-labelledby="channel_header_right_dropdown">
<li role="presentation"><a role="menuitem" href="#" onClick={this.searchMentions}>Recent Mentions</a></li>
</ul>
</div>
@@ -237,5 +216,3 @@ module.exports = React.createClass({
);
}
});
-
-
diff --git a/web/react/components/edit_channel_modal.jsx b/web/react/components/edit_channel_modal.jsx
index 255654fd5..c0818959a 100644
--- a/web/react/components/edit_channel_modal.jsx
+++ b/web/react/components/edit_channel_modal.jsx
@@ -43,7 +43,7 @@ module.exports = React.createClass({
<h4 className="modal-title" ref="title">Edit {this.state.title} Description</h4>
</div>
<div className="modal-body">
- <textarea className="form-control" style={{resize: "none"}} rows="6" ref="channelDesc" maxLength="1024" value={this.state.description} onChange={this.handleUserInput}></textarea>
+ <textarea className="form-control no-resize" rows="6" ref="channelDesc" maxLength="1024" value={this.state.description} onChange={this.handleUserInput}></textarea>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
diff --git a/web/react/components/get_link_modal.jsx b/web/react/components/get_link_modal.jsx
index bbfdce63a..af5314e64 100644
--- a/web/react/components/get_link_modal.jsx
+++ b/web/react/components/get_link_modal.jsx
@@ -38,7 +38,7 @@ module.exports = React.createClass({
<div className="modal-body">
<p>{"The link below is used for open " + strings.TeamPlural + " or if you allowed your " + strings.Team + " members to sign up using their " + strings.Company + " email addresses."}
</p>
- <textarea className="form-control" style={{resize: "none"}} readOnly="true" value={this.state.value}></textarea>
+ <textarea className="form-control no-resize" readOnly="true" value={this.state.value}></textarea>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
diff --git a/web/react/components/member_list_item.jsx b/web/react/components/member_list_item.jsx
index cf8c71d7e..a5279909b 100644
--- a/web/react/components/member_list_item.jsx
+++ b/web/react/components/member_list_item.jsx
@@ -49,7 +49,7 @@ module.exports = React.createClass({
</div>
);
} else {
- invite = <div className="member-role text-capitalize" style={{marginRight: 15}}>{member.roles || 'Member'}</div>;
+ invite = <div className="member-role text-capitalize">{member.roles || 'Member'}<span className="caret hidden"></span></div>;
}
return (
diff --git a/web/react/components/member_list_team.jsx b/web/react/components/member_list_team.jsx
index aa53c5db6..6f1d83193 100644
--- a/web/react/components/member_list_team.jsx
+++ b/web/react/components/member_list_team.jsx
@@ -59,7 +59,7 @@ var MemberListTeamItem = React.createClass({
return {};
},
render: function() {
- var server_error = this.state.server_error ? <div style={{ clear: "both" }} className="has-error"><label className='has-error control-label'>{this.state.server_error}</label></div> : null;
+ var server_error = this.state.server_error ? <div className="has-error"><label className='has-error control-label'>{this.state.server_error}</label></div> : null;
var user = this.props.user;
var currentRoles = "Member";
var timestamp = UserStore.getCurrentUser().update_at;
@@ -85,8 +85,8 @@ var MemberListTeamItem = React.createClass({
return (
<div className="row member-div">
<img className="post-profile-img pull-left" src={"/api/v1/users/" + user.id + "/image?time=" + timestamp} height="36" width="36" />
- <span className="member-name">{user.full_name.trim() ? user.full_name : user.username}</span>
- <span className="member-email">{user.full_name.trim() ? user.username : email}</span>
+ <span className="member-name">{utils.getDisplayName(user)}</span>
+ <span className="member-email">{email}</span>
<div className="dropdown member-drop">
<a href="#" className="dropdown-toggle theme" type="button" id="channel_header_dropdown" data-toggle="dropdown" aria-expanded="true">
<span>{currentRoles} </span>
diff --git a/web/react/components/mention.jsx b/web/react/components/mention.jsx
index 520b81cbb..114dc183f 100644
--- a/web/react/components/mention.jsx
+++ b/web/react/components/mention.jsx
@@ -6,16 +6,22 @@ module.exports = React.createClass({
handleClick: function() {
this.props.handleClick(this.props.username);
},
+ getInitialState: function() {
+ return null;
+ },
render: function() {
+ var self = this;
var icon;
var timestamp = UserStore.getCurrentUser().update_at;
- if (this.props.id != null) {
+ if (this.props.id === "allmention" || this.props.id === "channelmention") {
+ icon = <span><i className="mention-img fa fa-users fa-2x"></i></span>;
+ } else if (this.props.id != null) {
icon = <span><img className="mention-img" src={"/api/v1/users/" + this.props.id + "/image?time=" + timestamp}/></span>;
} else {
icon = <span><i className="mention-img fa fa-users fa-2x"></i></span>;
}
return (
- <div className="mentions-name" onClick={this.handleClick}>
+ <div className={"mentions-name " + this.props.isFocused} id={this.props.id + "_mentions"} onClick={this.handleClick} onMouseEnter={this.props.handleMouseEnter}>
<div className="pull-left">{icon}</div>
<div className="pull-left mention-align"><span>@{this.props.username}</span><span className="mention-fullname">{this.props.secondary_text}</span></div>
</div>
diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx
index 103ff29bb..524f1b337 100644
--- a/web/react/components/mention_list.jsx
+++ b/web/react/components/mention_list.jsx
@@ -17,14 +17,37 @@ module.exports = React.createClass({
displayName: "MentionList",
componentDidMount: function() {
PostStore.addMentionDataChangeListener(this._onChange);
-
var self = this;
- $('body').on('keypress.mentionlist', '#'+this.props.id,
+
+ $('body').on('keydown.mentionlist', '#'+this.props.id,
function(e) {
- if (!self.isEmpty() && self.state.mentionText != '-1' && e.which === 13) {
+ if (!self.isEmpty() && self.state.mentionText != '-1' && (e.which === 13 || e.which === 9)) {
+ e.stopPropagation();
+ e.preventDefault();
+ self.addCurrentMention();
+ }
+ else if (!self.isEmpty() && self.state.mentionText != '-1' && (e.which === 38 || e.which === 40)) {
e.stopPropagation();
e.preventDefault();
- self.addFirstMention();
+
+ var tempSelectedMention = -1;
+ if (e.which === 38) {
+ if (self.getSelection(self.state.selectedMention - 1))
+ self.setState({ selectedMention: self.state.selectedMention - 1, selectedUsername: self.refs['mention' + (self.state.selectedMention - 1)].props.username });
+ else {
+ while (self.getSelection(++tempSelectedMention))
+ ; //Need to find the top of the list
+ self.setState({ selectedMention: tempSelectedMention - 1, selectedUsername: self.refs['mention' + (tempSelectedMention - 1)].props.username });
+ }
+ }
+ else if (e.which === 40) {
+ if (self.getSelection(self.state.selectedMention + 1))
+ self.setState({ selectedMention: self.state.selectedMention + 1, selectedUsername: self.refs['mention' + (self.state.selectedMention + 1)].props.username });
+ else
+ self.setState({ selectedMention: 0, selectedUsername: self.refs.mention0.props.username });
+ }
+
+ self.scrollToMention(e.which, tempSelectedMention);
}
}
);
@@ -37,7 +60,28 @@ module.exports = React.createClass({
},
componentWillUnmount: function() {
PostStore.removeMentionDataChangeListener(this._onChange);
- $('body').off('keypress.mentionlist', '#'+this.props.id);
+ $('body').off('keydown.mentionlist', '#'+this.props.id);
+ },
+ componentDidUpdate: function() {
+ if (this.state.mentionText != "-1") {
+ if (this.state.selectedUsername !== "" && (!this.getSelection(this.state.selectedMention) || this.state.selectedUsername !== this.refs['mention' + this.state.selectedMention].props.username)) {
+ var tempSelectedMention = -1;
+ var foundMatch = false;
+ while (tempSelectedMention < this.state.selectedMention && this.getSelection(++tempSelectedMention)) {
+ if (this.state.selectedUsername === this.refs['mention' + tempSelectedMention].props.username) {
+ this.setState({ selectedMention: tempSelectedMention });
+ foundMatch = true;
+ break;
+ }
+ }
+ if (this.getSelection(0) && !foundMatch) {
+ this.setState({ selectedMention: 0, selectedUsername: this.refs.mention0.props.username });
+ }
+ }
+ }
+ else if (this.state.selectedMention !== 0) {
+ this.setState({ selectedMention: 0, selectedUsername: "" });
+ }
},
_onChange: function(id, mentionText, excludeList) {
if (id !== this.props.id) return;
@@ -45,6 +89,7 @@ module.exports = React.createClass({
var newState = this.state;
if (mentionText != null) newState.mentionText = mentionText;
if (excludeList != null) newState.excludeUsers = excludeList;
+
this.setState(newState);
},
handleClick: function(name) {
@@ -56,6 +101,21 @@ module.exports = React.createClass({
this.setState({ mentionText: '-1' });
},
+ handleMouseEnter: function(listId) {
+ this.setState({ selectedMention: listId, selectedUsername: this.refs['mention' + listId].props.username });
+ },
+ getSelection: function(listId) {
+ if (!this.refs['mention' + listId])
+ return false;
+ else
+ return true;
+ },
+ addCurrentMention: function() {
+ if (!this.getSelection(this.state.selectedMention))
+ this.addFirstMention();
+ else
+ this.refs['mention' + this.state.selectedMention].handleClick();
+ },
addFirstMention: function() {
if (!this.refs.mention0) return;
this.refs.mention0.handleClick();
@@ -63,6 +123,23 @@ module.exports = React.createClass({
isEmpty: function() {
return (!this.refs.mention0);
},
+ scrollToMention: function(keyPressed, ifLoopUp) {
+ var direction = keyPressed === 38 ? "up" : "down";
+ var scrollAmount = 0;
+
+ if (direction === "up" && ifLoopUp !== -1)
+ scrollAmount = $("#mentionsbox").height() * 100; //Makes sure that it scrolls all the way to the bottom
+ else if (direction === "down" && this.state.selectedMention === 0)
+ scrollAmount = 0;
+ else if (direction === "up")
+ scrollAmount = "-=" + ($('#'+this.refs['mention' + this.state.selectedMention].props.id +"_mentions").innerHeight() - 5);
+ else if (direction === "down")
+ scrollAmount = "+=" + ($('#'+this.refs['mention' + this.state.selectedMention].props.id +"_mentions").innerHeight() - 5);
+
+ $("#mentionsbox").animate({
+ scrollTop: scrollAmount
+ }, 75);
+ },
alreadyMentioned: function(username) {
var excludeUsers = this.state.excludeUsers;
for (var i = 0; i < excludeUsers.length; i++) {
@@ -73,9 +150,10 @@ module.exports = React.createClass({
return false;
},
getInitialState: function() {
- return { excludeUsers: [], mentionText: "-1" };
+ return { excludeUsers: [], mentionText: "-1", selectedMention: 0, selectedUsername: "" };
},
render: function() {
+ var self = this;
var mentionText = this.state.mentionText;
if (mentionText === '-1') return null;
@@ -87,14 +165,16 @@ module.exports = React.createClass({
var all = {};
all.username = "all";
- all.full_name = "";
+ all.nickname = "";
all.secondary_text = "Notifies everyone in the team";
+ all.id = "allmention";
users.push(all);
var channel = {};
channel.username = "channel";
- channel.full_name = "";
+ channel.nickname = "";
channel.secondary_text = "Notifies everyone in the channel";
+ channel.id = "channelmention";
users.push(channel);
users.sort(function(a,b) {
@@ -108,27 +188,23 @@ module.exports = React.createClass({
for (var i = 0; i < users.length && index < MAX_ITEMS_IN_LIST; i++) {
if (this.alreadyMentioned(users[i].username)) continue;
- var firstName = "", lastName = "";
- if (users[i].full_name.length > 0) {
- var splitName = users[i].full_name.split(' ');
- firstName = splitName[0].toLowerCase();
- lastName = splitName.length > 1 ? splitName[splitName.length-1].toLowerCase() : "";
- users[i].secondary_text = users[i].full_name;
- }
-
- if (firstName.lastIndexOf(mentionText,0) === 0
- || lastName.lastIndexOf(mentionText,0) === 0 || users[i].username.lastIndexOf(mentionText,0) === 0) {
- mentions[i+1] = (
+ if (users[i].first_name.lastIndexOf(mentionText,0) === 0
+ || users[i].last_name.lastIndexOf(mentionText,0) === 0 || users[i].username.lastIndexOf(mentionText,0) === 0) {
+ mentions[index] = (
<Mention
ref={'mention' + index}
username={users[i].username}
- secondary_text={users[i].secondary_text}
+ secondary_text={users[i].first_name + " " + users[i].last_name}
id={users[i].id}
+ listId={index}
+ isFocused={this.state.selectedMention === index ? "mentions-focus" : ""}
+ handleMouseEnter={function(value) { return function() { self.handleMouseEnter(value); } }(index)}
handleClick={this.handleClick} />
);
index++;
}
}
+
var numMentions = Object.keys(mentions).length;
if (numMentions < 1) return null;
@@ -144,7 +220,7 @@ module.exports = React.createClass({
return (
<div className="mentions--top" style={style}>
- <div ref="mentionlist" className="mentions-box">
+ <div ref="mentionlist" className="mentions-box" id="mentionsbox">
{ mentions }
</div>
</div>
diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx
index 35f7d9044..78cf7d8b8 100644
--- a/web/react/components/navbar.jsx
+++ b/web/react/components/navbar.jsx
@@ -2,21 +2,20 @@
// See License.txt for license information.
-var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var utils = require('../utils/utils.jsx');
var client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
-var Sidebar = require('./sidebar.jsx');
var UserStore = require('../stores/user_store.jsx');
-var SocketStore = require('../stores/socket_store.jsx');
var ChannelStore = require('../stores/channel_store.jsx');
-var Constants = require('../utils/constants.jsx');
+
var UserProfile = require('./user_profile.jsx');
var MessageWrapper = require('./message_wrapper.jsx');
+
+var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
+var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
function getCountsStateFromStores() {
-
var count = 0;
var channels = ChannelStore.getAll();
var members = ChannelStore.getAllMembers();
@@ -34,7 +33,7 @@ function getCountsStateFromStores() {
}
});
- return { count: count }
+ return { count: count };
}
var NotifyCounts = React.createClass({
@@ -54,11 +53,10 @@ var NotifyCounts = React.createClass({
return getCountsStateFromStores();
},
render: function() {
- if (this.state.count == 0) {
- return (<span></span>);
- }
- else {
- return (<span className="badge badge-notify">{ this.state.count }</span>);
+ if (this.state.count) {
+ return <span className="badge badge-notify">{ this.state.count }</span>;
+ } else {
+ return null;
}
}
});
@@ -66,25 +64,25 @@ var NotifyCounts = React.createClass({
var NavbarLoginForm = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
- var state = { }
+ var state = { };
var domain = this.refs.domain.getDOMNode().value.trim();
if (!domain) {
- state.server_error = "A domain is required"
+ state.server_error = "A domain is required";
this.setState(state);
return;
}
var email = this.refs.email.getDOMNode().value.trim();
if (!email) {
- state.server_error = "An email is required"
+ state.server_error = "An email is required";
this.setState(state);
return;
}
var password = this.refs.password.getDOMNode().value.trim();
if (!password) {
- state.server_error = "A password is required"
+ state.server_error = "A password is required";
this.setState(state);
return;
}
@@ -105,7 +103,7 @@ var NavbarLoginForm = React.createClass({
window.location.href = '/channels/town-square';
}
- }.bind(this),
+ },
function(err) {
if (err.message == "Login failed because email address has not been verified") {
window.location.href = '/verify?domain=' + encodeURIComponent(domain) + '&email=' + encodeURIComponent(email);
@@ -159,13 +157,14 @@ function getStateFromStores() {
}
module.exports = React.createClass({
+ displayName: 'Navbar',
+
componentDidMount: function() {
ChannelStore.addChangeListener(this._onChange);
ChannelStore.addExtraInfoChangeListener(this._onChange);
- var self = this;
- $('.inner__wrap').click(self.hideSidebars);
+ $('.inner__wrap').click(this.hideSidebars);
- $('body').on('click.infopopover', function(e){
+ $('body').on('click.infopopover', function(e) {
if ($(e.target).attr('data-toggle') !== 'popover'
&& $(e.target).parents('.popover.in').length === 0) {
$('.info-popover').popover('hide');
@@ -181,13 +180,13 @@ module.exports = React.createClass({
},
handleLeave: function(e) {
client.leaveChannel(this.state.channel.id,
- function(data) {
+ function() {
AsyncClient.getChannels(true);
window.location.href = '/channels/town-square';
- }.bind(this),
+ },
function(err) {
AsyncClient.dispatchError(err, "handleLeave");
- }.bind(this)
+ }
);
},
hideSidebars: function(e) {
@@ -204,7 +203,7 @@ module.exports = React.createClass({
});
if (e.target.className != 'navbar-toggle' && e.target.className != 'icon-bar') {
- $('.inner__wrap').removeClass('move--right').removeClass('move--left').removeClass('move--left-small');
+ $('.inner__wrap').removeClass('move--right move--left move--left-small');
$('.sidebar--left').removeClass('move--right');
$('.sidebar--right').removeClass('move--left');
$('.sidebar--menu').removeClass('move--left');
@@ -229,24 +228,22 @@ module.exports = React.createClass({
render: function() {
var currentId = UserStore.getCurrentId();
- var channelName = "";
var popoverContent = "";
var channelTitle = this.props.teamName;
var isAdmin = false;
var isDirect = false;
var description = ""
+ var channel = this.state.channel;
- if (this.state.channel) {
- var channel = this.state.channel;
- description = utils.textToJsx(this.state.channel.description, {"singleline": true, "noMentionHighlight": true});
- popoverContent = React.renderToString(<MessageWrapper message={this.state.channel.description}/>);
- channelName = this.state.channel.name;
+ if (channel) {
+ description = utils.textToJsx(channel.description, {"singleline": true, "noMentionHighlight": true});
+ popoverContent = React.renderToString(<MessageWrapper message={channel.description}/>);
isAdmin = this.state.member.roles.indexOf("admin") > -1;
if (channel.type === 'O') {
- channelTitle = this.state.channel.display_name;
+ channelTitle = channel.display_name;
} else if (channel.type === 'P') {
- channelTitle = this.state.channel.display_name;
+ channelTitle = channel.display_name;
} else if (channel.type === 'D') {
isDirect = true;
if (this.state.users.length > 1) {
@@ -258,12 +255,11 @@ module.exports = React.createClass({
}
}
- if(this.state.channel.description.length == 0){
- popoverContent = React.renderToString(<div>No channel description yet. <br /><a href='#' data-toggle='modal' data-desc={this.state.channel.description} data-title={this.state.channel.display_name} data-channelid={this.state.channel.id} data-target='#edit_channel'>Click here</a> to add one.</div>);
+ if (channel.description.length == 0) {
+ popoverContent = React.renderToString(<div>No channel description yet. <br /><a href='#' data-toggle='modal' data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id} data-target='#edit_channel'>Click here</a> to add one.</div>);
}
}
- var loginForm = currentId == null ? <NavbarLoginForm /> : null;
var navbar_collapse_button = currentId != null ? null :
<button type="button" className="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse-1">
<span className="sr-only">Toggle sidebar</span>
@@ -292,60 +288,61 @@ module.exports = React.createClass({
{ navbar_collapse_button }
{ sidebar_collapse_button }
{ right_sidebar_collapse_button }
- { !isDirect && this.state.channel ?
- <div className="navbar-brand">
- <div className="dropdown">
- <div data-toggle="popover" data-content={popoverContent} className="description info-popover"></div>
- <a href="#" className="dropdown-toggle theme" type="button" id="channel_header_dropdown" data-toggle="dropdown" aria-expanded="true">
- <strong className="heading">{channelTitle} </strong>
- <span className="glyphicon glyphicon-chevron-down header-dropdown__icon"></span>
- </a>
- <ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_dropdown">
- <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>
- : ""
- }
- <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>
- { 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>
- : ""
- }
- { 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>
- : ""
- }
- { channelName != Constants.DEFAULT_CHANNEL ?
- <li role="presentation"><a role="menuitem" href="#" onClick={this.handleLeave}>Leave Channel</a></li>
- : ""
- }
- </ul>
+ { !isDirect && channel ?
+ <div className="navbar-brand">
+ <div className="dropdown">
+ <div data-toggle="popover" data-content={popoverContent} className="description info-popover"></div>
+ <a href="#" className="dropdown-toggle theme" type="button" id="channel_header_dropdown" data-toggle="dropdown" aria-expanded="true">
+ <span className="heading">{channelTitle} </span>
+ <span className="glyphicon glyphicon-chevron-down header-dropdown__icon"></span>
+ </a>
+ <ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_dropdown">
+ { !ChannelStore.isDefault(channel) ?
+ <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li>
+ : null
+ }
+ { isAdmin && !ChannelStore.isDefault(channel) ?
+ <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={channel.description} data-title={channel.display_name} data-channelid={channel.id}>Set Channel Description...</a></li>
+ <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#channel_notifications" data-title={channel.display_name} data-channelid={channel.id}>Notification Preferences</a></li>
+ { isAdmin && !ChannelStore.isDefault(channel) ?
+ <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#rename_channel" data-display={channel.display_name} data-name={channel.name} data-channelid={channel.id}>Rename Channel...</a></li>
+ : null
+ }
+ { isAdmin && !ChannelStore.isDefault(channel) ?
+ <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#delete_channel" data-title={channel.display_name} data-channelid={channel.id}>Delete Channel...</a></li>
+ : null
+ }
+ { !ChannelStore.isDefault(channel) ?
+ <li role="presentation"><a role="menuitem" href="#" onClick={this.handleLeave}>Leave Channel</a></li>
+ : null
+ }
+ </ul>
+ </div>
</div>
- </div>
- : "" }
- { isDirect && this.state.channel ?
+ : null
+ }
+ { isDirect && channel ?
<div className="navbar-brand">
- <strong>
- <a href="#"><strong className="heading">{channelTitle}</strong></a>
- </strong>
+ <a href="#" className="heading">{ channelTitle }</a>
</div>
- : "" }
- { !this.state.channel ?
+ : null }
+ { !channel ?
<div className="navbar-brand">
- <strong>
- <a href="/"><strong className="heading">{ channelTitle }</strong></a>
- </strong>
+ <a href="/" className="heading">{ channelTitle }</a>
</div>
- : "" }
- </div>
- <div className="collapse navbar-collapse" id="navbar-collapse-1">
- { loginForm }
+ : null }
</div>
+ { !currentId ?
+ <div className="collapse navbar-collapse" id="navbar-collapse-1">
+ <NavbarLoginForm />
+ </div>
+ : null
+ }
</div>
</nav>
);
-}
+ }
});
-
-
diff --git a/web/react/components/new_channel.jsx b/web/react/components/new_channel.jsx
index 160241c1c..069e0d6b1 100644
--- a/web/react/components/new_channel.jsx
+++ b/web/react/components/new_channel.jsx
@@ -122,7 +122,7 @@ module.exports = React.createClass({
</div>
<div className="form-group">
<label className='control-label'>Description</label>
- <textarea className="form-control" style={{resize: "none"}} ref="channel_desc" rows="3" placeholder="Description" maxLength="1024"></textarea>
+ <textarea className="form-control no-resize" ref="channel_desc" rows="3" placeholder="Description" maxLength="1024"></textarea>
</div>
{ server_error }
</form>
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx
index cf542a98f..d9678df30 100644
--- a/web/react/components/post_body.jsx
+++ b/web/react/components/post_body.jsx
@@ -71,11 +71,22 @@ module.exports = React.createClass({
name = <a className="theme" onClick={function(){ utils.searchForTerm(profile.username); }}>{profile.username}</a>;
}
- var message = parentPost.message;
+ var message = ""
+ if(parentPost.message) {
+ message = utils.replaceHtmlEntities(parentPost.message)
+ } else if (parentPost.filenames.length) {
+ message = parentPost.filenames[0].split('/').pop();
+
+ if (parentPost.filenames.length === 2) {
+ message += " plus 1 other file";
+ } else if (parentPost.filenames.length > 2) {
+ message += " plus " + (parentPost.filenames.length - 1) + " other files";
+ }
+ }
comment = (
<p className="post-link">
- <span>Commented on {name}{apostrophe} message: <a className="theme" onClick={this.props.handleCommentClick}>{utils.replaceHtmlEntities(message)}</a></span>
+ <span>Commented on {name}{apostrophe} message: <a className="theme" onClick={this.props.handleCommentClick}>{message}</a></span>
</p>
);
diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx
index cf01747f0..d6422fe3a 100644
--- a/web/react/components/post_info.jsx
+++ b/web/react/components/post_info.jsx
@@ -11,6 +11,7 @@ module.exports = React.createClass({
render: function() {
var post = this.props.post;
var isOwner = UserStore.getCurrentId() == post.user_id;
+ var isAdmin = UserStore.getCurrentUser().roles.indexOf("admin") > -1
var type = "Post"
if (post.root_id.length > 0) {
@@ -30,13 +31,11 @@ module.exports = React.createClass({
<div className="dropdown">
{ isOwner || (this.props.allowReply === "true" && type != "Comment") ?
<div>
- <a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false">
- [...]
- </a>
+ <a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false" />
<ul className="dropdown-menu" role="menu">
{ isOwner ? <li role="presentation"><a href="#" role="menuitem" data-toggle="modal" data-target="#edit_post" data-title={type} data-message={post.message} data-postid={post.id} data-channelid={post.channel_id} data-comments={type === "Post" ? this.props.commentCount : 0}>Edit</a></li>
: "" }
- { isOwner ? <li role="presentation"><a href="#" role="menuitem" data-toggle="modal" data-target="#delete_post" data-title={type} data-postid={post.id} data-channelid={post.channel_id} data-comments={type === "Post" ? this.props.commentCount : 0}>Delete</a></li>
+ { isOwner || isAdmin ? <li role="presentation"><a href="#" role="menuitem" data-toggle="modal" data-target="#delete_post" data-title={type} data-postid={post.id} data-channelid={post.channel_id} data-comments={type === "Post" ? this.props.commentCount : 0}>Delete</a></li>
: "" }
{ this.props.allowReply === "true" ? <li role="presentation"><a className="reply-link theme" href="#" onClick={this.props.handleCommentClick}>Reply</a></li>
: "" }
diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx
index 9349d0240..5439ca43d 100644
--- a/web/react/components/post_list.jsx
+++ b/web/react/components/post_list.jsx
@@ -6,7 +6,6 @@ var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx');
var UserProfile = require( './user_profile.jsx' );
var AsyncClient = require('../utils/async_client.jsx');
-var CreatePost = require('./create_post.jsx');
var Post = require('./post.jsx');
var LoadingScreen = require('./loading_screen.jsx');
var SocketStore = require('../stores/socket_store.jsx');
@@ -265,7 +264,7 @@ module.exports = React.createClass({
},
function(err) {
$(self.refs.loadmore.getDOMNode()).text("Load more messages");
- dispatchError(err, "getPosts");
+ AsyncClient.dispatchError(err, "getPosts");
}
);
},
@@ -306,7 +305,7 @@ module.exports = React.createClass({
var teammate = utils.getDirectTeammate(channel.id)
if (teammate) {
- var teammate_name = teammate.full_name.length > 0 ? teammate.full_name : teammate.username;
+ var teammate_name = teammate.nickname.length > 0 ? teammate.nickname : teammate.username;
more_messages = (
<div className="channel-intro">
<div className="post-profile-img__container channel-intro-img">
@@ -341,7 +340,7 @@ module.exports = React.createClass({
}
}
- if (channel.name === Constants.DEFAULT_CHANNEL) {
+ if (ChannelStore.isDefault(channel)) {
more_messages = (
<div className="channel-intro">
<h4 className="channel-intro-title">Welcome</h4>
diff --git a/web/react/components/post_right.jsx b/web/react/components/post_right.jsx
index 408fbf83a..cc6751738 100644
--- a/web/react/components/post_right.jsx
+++ b/web/react/components/post_right.jsx
@@ -43,7 +43,7 @@ RhsHeaderPost = React.createClass({
});
},
render: function() {
- var back = this.props.fromSearch ? <a href="#" onClick={this.handleBack} style={{color:"black"}}>{"< "}</a> : "";
+ var back = this.props.fromSearch ? <a href="#" onClick={this.handleBack} className="sidebar--right__back"><i className="fa fa-chevron-left"></i></a> : "";
return (
<div className="sidebar--right__header">
@@ -129,9 +129,7 @@ RootPost = React.createClass({
<div className="dropdown">
{ isOwner ?
<div>
- <a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false">
- [...]
- </a>
+ <a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false" />
<ul className="dropdown-menu" role="menu">
<li role="presentation"><a href="#" role="menuitem" data-toggle="modal" data-target="#edit_post" data-title={type} data-message={this.props.post.message} data-postid={this.props.post.id} data-channelid={this.props.post.channel_id}>Edit</a></li>
<li role="presentation"><a href="#" role="menuitem" data-toggle="modal" data-target="#delete_post" data-title={type} data-postid={this.props.post.id} data-channelid={this.props.post.channel_id} data-comments={this.props.commentCount}>Delete</a></li>
diff --git a/web/react/components/search_bar.jsx b/web/react/components/search_bar.jsx
index cddb738f9..f21f0cd58 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();
@@ -92,11 +92,18 @@ module.exports = React.createClass({
render: function() {
return (
<div>
- <div className="sidebar__collapse" onClick={this.handleClose}></div>
+ <div className="sidebar__collapse" onClick={this.handleClose}>Cancel</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>
diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx
index cae9425d3..65727c597 100644
--- a/web/react/components/sidebar.jsx
+++ b/web/react/components/sidebar.jsx
@@ -131,7 +131,7 @@ function getStateFromStores() {
var channel = ChannelStore.getByName(channelName);
if (channel != null) {
- channel.display_name = teammate.full_name.trim() != "" ? teammate.full_name : teammate.username;
+ channel.display_name = utils.getDisplayName(teammate);
channel.teammate_username = teammate.username;
channel.status = UserStore.getStatus(teammate.id);
@@ -150,7 +150,7 @@ function getStateFromStores() {
var tempChannel = {};
tempChannel.fake = true;
tempChannel.name = channelName;
- tempChannel.display_name = teammate.full_name.trim() != "" ? teammate.full_name : teammate.username;
+ tempChannel.display_name = utils.getDisplayName(teammate);
tempChannel.status = UserStore.getStatus(teammate.id);
tempChannel.last_post_at = 0;
readDirectChannels.push(tempChannel);
diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx
index 54858a04d..45c9ca629 100644
--- a/web/react/components/sidebar_header.jsx
+++ b/web/react/components/sidebar_header.jsx
@@ -37,17 +37,13 @@ var NavbarDropdown = React.createClass({
var invite_link = "";
var manage_link = "";
var rename_link = "";
- var currentUser = UserStore.getCurrentUser()
+ var currentUser = UserStore.getCurrentUser();
var isAdmin = false;
if (currentUser != null) {
isAdmin = currentUser.roles.indexOf("admin") > -1;
- invite_link = (
- <li>
- <a href="#" data-toggle="modal" data-target="#invite_member">Invite New Member</a>
- </li>
- );
+ invite_link = ( <li> <a href="#" data-toggle="modal" data-target="#invite_member">Invite New Member</a> </li>);
if (this.props.teamType == "O") {
team_link = (
@@ -59,16 +55,8 @@ var NavbarDropdown = React.createClass({
}
if (isAdmin) {
- manage_link = (
- <li>
- <a href="#" data-toggle="modal" data-target="#team_members">Manage Team</a>
- </li>
- );
- rename_link = (
- <li>
- <a href="#" data-toggle="modal" data-target="#rename_team_link">Rename</a>
- </li>
- );
+ manage_link = ( <li> <a href="#" data-toggle="modal" data-target="#team_members">Manage Team</a> </li>);
+ rename_link = ( <li> <a href="#" data-toggle="modal" data-target="#rename_team_link">Rename</a> </li>);
}
var teams = [];
@@ -91,11 +79,11 @@ var NavbarDropdown = React.createClass({
<ul className="nav navbar-nav navbar-right">
<li className="dropdown">
<a href="#" className="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
- <i className="dropdown__icon"></i>
+ <span className="dropdown__icon" dangerouslySetInnerHTML={{__html: " <svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'width='4px' height='16px' viewBox='0 0 8 32' enable-background='new 0 0 8 32' xml:space='preserve'> <g> <circle cx='4' cy='4.062' r='4'/> <circle cx='4' cy='16' r='4'/> <circle cx='4' cy='28' r='4'/> </g> </svg>"}} />
</a>
<ul className="dropdown-menu" role="menu">
<li><a href="#" data-toggle="modal" data-target="#user_settings1">Account Settings</a></li>
- { isAdmin ? <li><a href="#" data-toggle="modal" data-target="#team_settings">Team Settings</a></li> : "" }
+ { isAdmin ? <li><a href="#" data-toggle="modal" data-target="#team_settings">Team Settings</a></li> : null }
{ invite_link }
{ team_link }
{ manage_link }
@@ -113,28 +101,30 @@ var NavbarDropdown = React.createClass({
});
module.exports = React.createClass({
- handleSubmit: function(e) {
- e.preventDefault();
- },
- getInitialState: function() {
- return { };
+ displayName: 'SidebarHeader',
+
+ getDefaultProps: function() {
+ return {
+ teamName: config.SiteName
+ };
},
+
render: function() {
- var teamName = this.props.teamName ? this.props.teamName : config.SiteName;
- var me = UserStore.getCurrentUser()
+ var me = UserStore.getCurrentUser();
+
+ if (!me) {
+ return null;
+ }
return (
<div className="team__header theme">
<img className="user__picture" src={"/api/v1/users/" + me.id + "/image?time=" + me.update_at} />
<div className="header__info">
- <div className="user__name">@{me.username}</div>
- <a className="team__name" href="/channels/town-square">{ teamName }</a>
+ <div className="user__name">{'@' + me.username}</div>
+ <a className="team__name" href="/channels/town-square">{this.props.teamName}</a>
</div>
<NavbarDropdown teamType={this.props.teamType} />
</div>
);
}
});
-
-
-
diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx
index fa0c26017..d1053c778 100644
--- a/web/react/components/signup_user_complete.jsx
+++ b/web/react/components/signup_user_complete.jsx
@@ -46,25 +46,24 @@ module.exports = React.createClass({
function(data) {
client.track('signup', 'signup_user_02_complete');
- if (data.email_verified) {
- client.loginByEmail(this.props.domain, this.state.user.email, this.state.user.password,
- function(data) {
- UserStore.setLastDomain(this.props.domain);
- UserStore.setLastEmail(this.state.user.email);
- UserStore.setCurrentUser(data);
- if (this.props.hash > 0)
- BrowserStore.setGlobalItem(this.props.hash, {wizard: "finished"});
- window.location.href = '/channels/town-square';
- }.bind(this),
- function(err) {
+ client.loginByEmail(this.props.domain, this.state.user.email, this.state.user.password,
+ function(data) {
+ UserStore.setLastDomain(this.props.domain);
+ UserStore.setLastEmail(this.state.user.email);
+ UserStore.setCurrentUser(data);
+ if (this.props.hash > 0)
+ BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: "finished"}));
+ window.location.href = '/channels/town-square';
+ }.bind(this),
+ function(err) {
+ if (err.message == "Login failed because email address has not been verified") {
+ window.location.href = "/verify?email="+ encodeURIComponent(this.state.user.email) + "&domain=" + encodeURIComponent(this.props.domain);
+ } else {
this.state.server_error = err.message;
this.setState(this.state);
- }.bind(this)
- );
- }
- else {
- window.location.href = "/verify?email="+ encodeURIComponent(this.state.user.email) + "&domain=" + encodeURIComponent(this.props.domain);
- }
+ }
+ }.bind(this)
+ );
}.bind(this),
function(err) {
this.state.server_error = err.message;
diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx
index 89d0a80ff..65f025919 100644
--- a/web/react/components/user_profile.jsx
+++ b/web/react/components/user_profile.jsx
@@ -53,7 +53,7 @@ module.exports = React.createClass({
var name = this.props.overwriteName ? this.props.overwriteName : this.state.profile.username;
- var data_content = "<img style='margin: 10px' src='/api/v1/users/" + this.state.profile.id + "/image?time=" + this.state.profile.update_at + "' height='128' width='128' />";
+ var data_content = "<img class='user-popover__image' src='/api/v1/users/" + this.state.profile.id + "/image?time=" + this.state.profile.update_at + "' height='128' width='128' />";
if (!config.ShowEmail) {
data_content += "<div class='text-nowrap'>Email not shared</div>";
} else {
@@ -61,7 +61,7 @@ module.exports = React.createClass({
}
return (
- <div style={{"cursor" : "pointer", "display" : "inline-block"}} className="user-popover" id={"profile_" + this.uniqueId} data-toggle="popover" data-content={data_content} data-original-title={this.state.profile.username} >
+ <div className="user-popover" id={"profile_" + this.uniqueId} data-toggle="popover" data-content={data_content} data-original-title={this.state.profile.username} >
{ name }
</div>
);
diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx
index b4c3747af..59c97c309 100644
--- a/web/react/components/user_settings.jsx
+++ b/web/react/components/user_settings.jsx
@@ -156,6 +156,8 @@ var NotificationsTab = React.createClass({
var self = this;
+ var user = this.props.user;
+
var desktopSection;
if (this.props.activeSection === 'desktop') {
var notifyActive = [false, false, false];
@@ -314,20 +316,14 @@ var NotificationsTab = React.createClass({
var keysSection;
if (this.props.activeSection === 'keys') {
- var user = this.props.user;
- var first_name = "";
- if (user.full_name.length > 0) {
- first_name = user.full_name.split(' ')[0];
- }
-
var inputs = [];
- if (first_name != "") {
+ if (user.first_name) {
inputs.push(
<div>
<div className="checkbox">
<label>
- <input type="checkbox" checked={this.state.first_name_key} onChange={function(e){self.updateFirstNameKey(e.target.checked);}}>{'Your case sensitive first name "' + first_name + '"'}</input>
+ <input type="checkbox" checked={this.state.first_name_key} onChange={function(e){self.updateFirstNameKey(e.target.checked);}}>{'Your case sensitive first name "' + user.first_name + '"'}</input>
</label>
</div>
</div>
@@ -396,14 +392,9 @@ var NotificationsTab = React.createClass({
);
} else {
var keys = [];
- if (this.state.first_name_key) {
- var first_name = "";
- var user = this.props.user;
- if (user.full_name.length > 0) first_name = user.full_name.split(' ')[0];
- if (first_name != "") keys.push(first_name);
- }
- if (this.state.username_key) keys.push(this.props.user.username);
- if (this.state.mention_key) keys.push('@'+this.props.user.username);
+ if (this.state.first_name_key) keys.push(user.first_name);
+ if (this.state.username_key) keys.push(user.username);
+ if (this.state.mention_key) keys.push('@'+user.username);
if (this.state.all_key) keys.push('@all');
if (this.state.channel_key) keys.push('@channel');
if (this.state.custom_keys.length > 0) keys = keys.concat(this.state.custom_keys.split(','));
@@ -560,7 +551,7 @@ var AuditTab = React.createClass({
<div className="user-settings">
<h3 className="tab-header">Activity Log</h3>
<div className="divider-dark first"/>
- <div className="table-responsive" style={{ maxWidth: "560px", maxHeight: "300px" }}>
+ <div className="table-responsive">
<table className="table-condensed small">
<thead>
<tr>
@@ -576,11 +567,11 @@ var AuditTab = React.createClass({
this.state.audits.map(function(value, index) {
return (
<tr key={ "" + index }>
- <td style={{ whiteSpace: "nowrap" }}>{ new Date(value.create_at).toLocaleString() }</td>
- <td style={{ whiteSpace: "nowrap" }}>{ value.action.replace("/api/v1", "") }</td>
- <td style={{ whiteSpace: "nowrap" }}>{ value.ip_address }</td>
- <td style={{ whiteSpace: "nowrap" }}>{ value.session_id }</td>
- <td style={{ whiteSpace: "nowrap" }}>{ value.extra_info }</td>
+ <td className="text-nowrap">{ new Date(value.create_at).toLocaleString() }</td>
+ <td className="text-nowrap">{ value.action.replace("/api/v1", "") }</td>
+ <td className="text-nowrap">{ value.ip_address }</td>
+ <td className="text-nowrap">{ value.session_id }</td>
+ <td className="text-nowrap">{ value.extra_info }</td>
</tr>
);
}, this)
@@ -626,7 +617,7 @@ var SecurityTab = React.createClass({
client.updatePassword(data,
function(data) {
- this.updateSection("");
+ this.props.updateSection("");
AsyncClient.getMe();
this.setState({ current_password: '', new_password: '', confirm_password: '' });
}.bind(this),
@@ -752,6 +743,21 @@ var GeneralTab = React.createClass({
this.submitUser(user);
},
+ submitNickname: function(e) {
+ e.preventDefault();
+
+ var user = UserStore.getCurrentUser();
+ var nickname = this.state.nickname.trim();
+
+ if (user.nickname === nickname) {
+ this.setState({client_error: "You must submit a new nickname"})
+ return;
+ }
+
+ user.nickname = nickname;
+
+ this.submitUser(user);
+ },
submitName: function(e) {
e.preventDefault();
@@ -759,14 +765,13 @@ var GeneralTab = React.createClass({
var firstName = this.state.first_name.trim();
var lastName = this.state.last_name.trim();
- var fullName = firstName + ' ' + lastName;
-
- if (user.full_name === fullName) {
- this.setState({client_error: "You must submit a new name"})
+ if (user.first_name === firstName && user.last_name === lastName) {
+ this.setState({client_error: "You must submit a new first or last name"})
return;
}
- user.full_name = fullName;
+ user.first_name = firstName;
+ user.last_name = lastName;
this.submitUser(user);
},
@@ -820,6 +825,7 @@ var GeneralTab = React.createClass({
client.uploadProfileImage(formData,
function(data) {
this.submitActive = false;
+ AsyncClient.getMe();
window.location.reload();
}.bind(this),
function(err) {
@@ -838,6 +844,9 @@ var GeneralTab = React.createClass({
updateLastName: function(e) {
this.setState({ last_name: e.target.value});
},
+ updateNickname: function(e) {
+ this.setState({nickname: e.target.value});
+ },
updateEmail: function(e) {
this.setState({ email: e.target.value});
},
@@ -860,11 +869,7 @@ var GeneralTab = React.createClass({
getInitialState: function() {
var user = this.props.user;
- var splitStr = user.full_name.split(' ');
- var firstName = splitStr.shift();
- var lastName = splitStr.join(' ');
-
- return { username: user.username, first_name: firstName, last_name: lastName,
+ return { username: user.username, first_name: user.first_name, last_name: user.last_name, nickname: user.nickname,
email: user.email, picture: null };
},
render: function() {
@@ -900,7 +905,7 @@ var GeneralTab = React.createClass({
nameSection = (
<SettingItemMax
- title="Name"
+ title="Full Name"
inputs={inputs}
submit={this.submitName}
server_error={server_error}
@@ -909,15 +914,58 @@ var GeneralTab = React.createClass({
/>
);
} else {
+ var full_name = "";
+
+ if (user.first_name && user.last_name) {
+ full_name = user.first_name + " " + user.last_name;
+ } else if (user.first_name) {
+ full_name = user.first_name;
+ } else if (user.last_name) {
+ full_name = user.last_name;
+ }
+
nameSection = (
<SettingItemMin
- title="Name"
- describe={UserStore.getCurrentUser().full_name}
+ title="Full Name"
+ describe={full_name}
updateSection={function(){self.updateSection("name");}}
/>
);
}
+ var nicknameSection;
+ if (this.props.activeSection === 'nickname') {
+ var inputs = [];
+
+ inputs.push(
+ <div className="form-group">
+ <label className="col-sm-5 control-label">{utils.isMobile() ? "": "Nickname"}</label>
+ <div className="col-sm-7">
+ <input className="form-control" type="text" onChange={this.updateNickname} value={this.state.nickname}/>
+ </div>
+ </div>
+ );
+
+ nicknameSection = (
+ <SettingItemMax
+ title="Nickname"
+ inputs={inputs}
+ submit={this.submitNickname}
+ server_error={server_error}
+ client_error={client_error}
+ updateSection={function(e){self.updateSection("");e.preventDefault();}}
+ />
+ );
+ } else {
+ nicknameSection = (
+ <SettingItemMin
+ title="Nickname"
+ describe={UserStore.getCurrentUser().nickname}
+ updateSection={function(){self.updateSection("nickname");}}
+ />
+ );
+ }
+
var usernameSection;
if (this.props.activeSection === 'username') {
var inputs = [];
@@ -989,7 +1037,7 @@ var GeneralTab = React.createClass({
<SettingPicture
title="Profile Picture"
submit={this.submitPicture}
- src={"/api/v1/users/" + user.id + "/image"}
+ src={"/api/v1/users/" + user.id + "/image?time=" + user.last_picture_update}
server_error={server_error}
client_error={client_error}
updateSection={function(e){self.updateSection("");e.preventDefault();}}
@@ -1000,10 +1048,14 @@ var GeneralTab = React.createClass({
);
} else {
+ var minMessage = "Click Edit to upload an image.";
+ if (user.last_picture_update) {
+ minMessage = "Image last updated " + utils.displayDate(user.last_picture_update)
+ }
pictureSection = (
<SettingItemMin
title="Profile Picture"
- describe="Picture inside."
+ describe={minMessage}
updateSection={function(){self.updateSection("picture");}}
/>
);
@@ -1021,6 +1073,8 @@ var GeneralTab = React.createClass({
<div className="divider-light"/>
{usernameSection}
<div className="divider-light"/>
+ {nicknameSection}
+ <div className="divider-light"/>
{emailSection}
<div className="divider-light"/>
{pictureSection}
diff --git a/web/react/stores/channel_store.jsx b/web/react/stores/channel_store.jsx
index 4a27e5f17..a97f13391 100644
--- a/web/react/stores/channel_store.jsx
+++ b/web/react/stores/channel_store.jsx
@@ -44,40 +44,24 @@ var ChannelStore = assign({}, EventEmitter.prototype, {
removeExtraInfoChangeListener: function(callback) {
this.removeListener(EXTRA_INFO_EVENT, callback);
},
- get: function(id) {
- var current = null;
- var c = this._getChannels();
-
- c.some(function(channel) {
- if (channel.id == id) {
- current = channel;
- return true;
+ findFirstBy: function(field, value) {
+ var channels = this._getChannels();
+ for (var i = 0; i < channels.length; i++) {
+ if (channels[i][field] == value) {
+ return channels[i];
}
- return false;
- });
+ }
- return current;
+ return null;
+ },
+ get: function(id) {
+ return this.findFirstBy('id', id);
},
getMember: function(id) {
- var current = null;
return this.getAllMembers()[id];
},
getByName: function(name) {
- var current = null;
- var c = this._getChannels();
-
- c.some(function(channel) {
- if (channel.name == name) {
- current = channel;
- return true;
- }
-
- return false;
-
- });
-
- return current;
-
+ return this.findFirstBy('name', name);
},
getAll: function() {
return this._getChannels();
@@ -120,7 +104,7 @@ var ChannelStore = assign({}, EventEmitter.prototype, {
getCurrent: function() {
var currentId = this.getCurrentId();
- if (currentId != null)
+ if (currentId)
return this.get(currentId);
else
return null;
@@ -128,7 +112,7 @@ var ChannelStore = assign({}, EventEmitter.prototype, {
getCurrentMember: function() {
var currentId = ChannelStore.getCurrentId();
- if (currentId != null)
+ if (currentId)
return this.getAllMembers()[currentId];
else
return null;
@@ -143,7 +127,7 @@ var ChannelStore = assign({}, EventEmitter.prototype, {
var currentId = ChannelStore.getCurrentId();
var extra = null;
- if (currentId != null)
+ if (currentId)
extra = this._getExtraInfos()[currentId];
if (extra == null)
@@ -154,7 +138,7 @@ var ChannelStore = assign({}, EventEmitter.prototype, {
getExtraInfo: function(channel_id) {
var extra = null;
- if (channel_id != null)
+ if (channel_id)
extra = this._getExtraInfos()[channel_id];
if (extra == null)
@@ -192,7 +176,10 @@ var ChannelStore = assign({}, EventEmitter.prototype, {
},
_getExtraInfos: function() {
return BrowserStore.getItem("extra_infos", {});
- }
+ },
+ isDefault: function(channel) {
+ return channel.name == Constants.DEFAULT_CHANNEL;
+ }
});
ChannelStore.dispatchToken = AppDispatcher.register(function(payload) {
@@ -231,4 +218,4 @@ ChannelStore.dispatchToken = AppDispatcher.register(function(payload) {
}
});
-module.exports = ChannelStore;
+module.exports = ChannelStore; \ No newline at end of file
diff --git a/web/react/stores/user_store.jsx b/web/react/stores/user_store.jsx
index 93ddfec70..b0ea719d4 100644
--- a/web/react/stores/user_store.jsx
+++ b/web/react/stores/user_store.jsx
@@ -177,21 +177,15 @@ var UserStore = assign({}, EventEmitter.prototype, {
},
getCurrentMentionKeys: function() {
var user = this.getCurrentUser();
- if (user.notify_props && user.notify_props.mention_keys) {
- var keys = user.notify_props.mention_keys.split(',');
- if (user.full_name.length > 0 && user.notify_props.first_name === "true") {
- var first = user.full_name.split(' ')[0];
- if (first.length > 0) keys.push(first);
- }
+ var keys = [];
- if (user.notify_props.all === "true") keys.push('@all');
- if (user.notify_props.channel === "true") keys.push('@channel');
+ if (user.notify_props && user.notify_props.mention_keys) keys = keys.concat(user.notify_props.mention_keys.split(','));
+ if (user.first_name && user.notify_props.first_name === "true") keys.push(user.first_name);
+ if (user.notify_props.all === "true") keys.push('@all');
+ if (user.notify_props.channel === "true") keys.push('@channel');
- return keys;
- } else {
- return [];
- }
+ return keys;
},
getLastVersion: function() {
return BrowserStore.getItem("last_version", '');
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 19c074606..416ea5ae4 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -198,7 +198,13 @@ module.exports.getTimestamp = function() {
}
var testUrlMatch = function(text) {
- var urlMatcher = new Autolinker.matchParser.MatchParser;
+ var urlMatcher = new Autolinker.matchParser.MatchParser({
+ urls: true,
+ emails: false,
+ twitter: false,
+ phone: false,
+ hashtag: false,
+ });
var result = [];
var replaceFn = function(match) {
var linkData = {};
@@ -303,18 +309,25 @@ var getYoutubeEmbed = function(link) {
};
var success = function(data) {
- $('.video-uploader.'+youtubeId).html(data.data.uploader);
- $('.video-title.'+youtubeId).find('a').html(data.data.title);
+ if(!data.items.length || !data.items[0].snippet) {
+ return;
+ }
+ var metadata = data.items[0].snippet;
+ $('.video-uploader.'+youtubeId).html(metadata.channelTitle);
+ $('.video-title.'+youtubeId).find('a').html(metadata.title);
$(".post-list-holder-by-time").scrollTop($(".post-list-holder-by-time")[0].scrollHeight);
$(".post-list-holder-by-time").perfectScrollbar('update');
};
- $.ajax({
- async: true,
- url: 'https://gdata.youtube.com/feeds/api/videos/'+youtubeId+'?v=2&alt=jsonc',
- type: 'GET',
- success: success
- });
+ if(config.GoogleDeveloperKey) {
+ $.ajax({
+ async: true,
+ url: "https://www.googleapis.com/youtube/v3/videos",
+ type: 'GET',
+ data: {part:"snippet", id:youtubeId, key:config.GoogleDeveloperKey},
+ success: success
+ });
+ }
return (
<div className="post-comment">
@@ -783,7 +796,6 @@ module.exports.getHomeLink = function() {
return window.location.protocol + "//" + parts.join(".");
}
-
module.exports.changeColor =function(col, amt) {
var usePound = false;
@@ -811,5 +823,30 @@ module.exports.changeColor =function(col, amt) {
else if (g < 0) g = 0;
return (usePound?"#":"") + String("000000" + (g | (b << 8) | (r << 16)).toString(16)).slice(-6);
+};
+module.exports.getFullName = function(user) {
+ if (user.first_name && user.last_name) {
+ return user.first_name + " " + user.last_name;
+ } else if (user.first_name) {
+ return user.first_name;
+ } else if (user.last_name) {
+ return user.last_name;
+ } else {
+ return "";
+ }
+};
+
+module.exports.getDisplayName = function(user) {
+ if (user.nickname && user.nickname.trim().length > 0) {
+ return user.nickname;
+ } else {
+ var fullName = module.exports.getFullName(user);
+
+ if (fullName) {
+ return fullName;
+ } else {
+ return user.username;
+ }
+ }
};