diff options
Diffstat (limited to 'web/react/components')
-rw-r--r-- | web/react/components/create_comment.jsx | 2 | ||||
-rw-r--r-- | web/react/components/create_post.jsx | 2 | ||||
-rw-r--r-- | web/react/components/error_bar.jsx | 12 | ||||
-rw-r--r-- | web/react/components/msg_typing.jsx | 55 | ||||
-rw-r--r-- | web/react/components/sidebar.jsx | 64 |
5 files changed, 92 insertions, 43 deletions
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx index 435c7d542..18936e808 100644 --- a/web/react/components/create_comment.jsx +++ b/web/react/components/create_comment.jsx @@ -147,7 +147,7 @@ export default class CreateComment extends React.Component { } const t = Date.now(); - if ((t - this.lastTime) > 5000) { + if ((t - this.lastTime) > Constants.UPDATE_TYPING_MS) { SocketStore.sendMessage({channel_id: this.props.channelId, action: 'typing', props: {parent_id: this.props.rootId}}); this.lastTime = t; } diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx index 0867bfdf2..32ee31efe 100644 --- a/web/react/components/create_post.jsx +++ b/web/react/components/create_post.jsx @@ -208,7 +208,7 @@ export default class CreatePost extends React.Component { } const t = Date.now(); - if ((t - this.lastTime) > 5000) { + if ((t - this.lastTime) > Constants.UPDATE_TYPING_MS) { SocketStore.sendMessage({channel_id: this.state.channelId, action: 'typing', props: {parent_id: ''}, state: {}}); this.lastTime = t; } diff --git a/web/react/components/error_bar.jsx b/web/react/components/error_bar.jsx index 6311d9460..f098384aa 100644 --- a/web/react/components/error_bar.jsx +++ b/web/react/components/error_bar.jsx @@ -9,12 +9,8 @@ export default class ErrorBar extends React.Component { this.onErrorChange = this.onErrorChange.bind(this); this.handleClose = this.handleClose.bind(this); - this.prevTimer = null; this.state = ErrorStore.getLastError(); - if (this.isValidError(this.state)) { - this.prevTimer = setTimeout(this.handleClose, 10000); - } } isValidError(s) { @@ -56,16 +52,8 @@ export default class ErrorBar extends React.Component { onErrorChange() { var newState = ErrorStore.getLastError(); - if (this.prevTimer != null) { - clearInterval(this.prevTimer); - this.prevTimer = null; - } - if (newState) { this.setState(newState); - if (!this.isConnectionError(newState)) { - this.prevTimer = setTimeout(this.handleClose, 10000); - } } else { this.setState({message: null}); } diff --git a/web/react/components/msg_typing.jsx b/web/react/components/msg_typing.jsx index 1bd23c55c..ccf8a2445 100644 --- a/web/react/components/msg_typing.jsx +++ b/web/react/components/msg_typing.jsx @@ -11,11 +11,11 @@ export default class MsgTyping extends React.Component { constructor(props) { super(props); - this.timer = null; - this.lastTime = 0; - this.onChange = this.onChange.bind(this); + this.updateTypingText = this.updateTypingText.bind(this); + this.componentWillReceiveProps = this.componentWillReceiveProps.bind(this); + this.typingUsers = {}; this.state = { text: '' }; @@ -27,7 +27,7 @@ export default class MsgTyping extends React.Component { componentWillReceiveProps(newProps) { if (this.props.channelId !== newProps.channelId) { - this.setState({text: ''}); + this.updateTypingText(); } } @@ -36,28 +36,51 @@ export default class MsgTyping extends React.Component { } onChange(msg) { + let username = 'Someone'; if (msg.action === SocketEvents.TYPING && this.props.channelId === msg.channel_id && this.props.parentId === msg.props.parent_id) { - this.lastTime = new Date().getTime(); - - var username = 'Someone'; if (UserStore.hasProfile(msg.user_id)) { username = UserStore.getProfile(msg.user_id).username; } - this.setState({text: username + ' is typing...'}); - - if (!this.timer) { - this.timer = setInterval(function myTimer() { - if ((new Date().getTime() - this.lastTime) > 8000) { - this.setState({text: ''}); - } - }.bind(this), 3000); + if (this.typingUsers[username]) { + clearTimeout(this.typingUsers[username]); } + + this.typingUsers[username] = setTimeout(function myTimer(user) { + delete this.typingUsers[user]; + this.updateTypingText(); + }.bind(this, username), Constants.UPDATE_TYPING_MS); + + this.updateTypingText(); } else if (msg.action === SocketEvents.POSTED && msg.channel_id === this.props.channelId) { - this.setState({text: ''}); + if (UserStore.hasProfile(msg.user_id)) { + username = UserStore.getProfile(msg.user_id).username; + } + clearTimeout(this.typingUsers[username]); + delete this.typingUsers[username]; + this.updateTypingText(); + } + } + + updateTypingText() { + const users = Object.keys(this.typingUsers); + let text = ''; + switch (users.length) { + case 0: + text = ''; + break; + case 1: + text = users[0] + ' is typing...'; + break; + default: + const last = users.pop(); + text = users.join(', ') + ' and ' + last + ' are typing...'; + break; } + + this.setState({text}); } render() { diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index ed2c84057..5cb6d168b 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -40,6 +40,9 @@ export default class Sidebar extends React.Component { this.hideMoreDirectChannelsModal = this.hideMoreDirectChannelsModal.bind(this); this.createChannelElement = this.createChannelElement.bind(this); + this.updateTitle = this.updateTitle.bind(this); + this.setUnreadCountPerChannel = this.setUnreadCountPerChannel.bind(this); + this.getUnreadCount = this.getUnreadCount.bind(this); this.isLeaving = new Map(); @@ -48,8 +51,45 @@ export default class Sidebar extends React.Component { state.showDirectChannelsModal = false; state.loadingDMChannel = -1; state.windowWidth = Utils.windowWidth(); - this.state = state; + + this.unreadCountPerChannel = {}; + this.setUnreadCountPerChannel(); + } + setUnreadCountPerChannel() { + const channels = ChannelStore.getAll(); + const members = ChannelStore.getAllMembers(); + const channelUnreadCounts = {}; + + channels.forEach((ch) => { + const chMember = members[ch.id]; + let chMentionCount = chMember.mention_count; + let chUnreadCount = ch.total_msg_count - chMember.msg_count - chMentionCount; + + if (ch.type === 'D') { + chMentionCount = chUnreadCount; + chUnreadCount = 0; + } + + channelUnreadCounts[ch.id] = {msgs: chUnreadCount, mentions: chMentionCount}; + }); + + this.unreadCountPerChannel = channelUnreadCounts; + } + getUnreadCount(channelId) { + let mentions = 0; + let msgs = 0; + + if (channelId) { + return this.unreadCountPerChannel[channelId] ? this.unreadCountPerChannel[channelId] : {msgs, mentions}; + } + + Object.keys(this.unreadCountPerChannel).forEach((chId) => { + msgs += this.unreadCountPerChannel[chId].msgs; + mentions += this.unreadCountPerChannel[chId].mentions; + }); + + return {msgs, mentions}; } getStateFromStores() { const members = ChannelStore.getAllMembers(); @@ -192,7 +232,10 @@ export default class Sidebar extends React.Component { currentChannelName = Utils.getDirectTeammate(channel.id).username; } - document.title = currentChannelName + ' - ' + this.props.teamDisplayName + ' ' + currentSiteName; + const unread = this.getUnreadCount(); + const mentionTitle = unread.mentions > 0 ? '(' + unread.mentions + ') ' : ''; + const unreadTitle = unread.msgs > 0 ? '* ' : ''; + document.title = mentionTitle + unreadTitle + currentChannelName + ' - ' + this.props.teamDisplayName + ' ' + currentSiteName; } } onScroll() { @@ -273,6 +316,7 @@ export default class Sidebar extends React.Component { var members = this.state.members; var activeId = this.state.activeId; var channelMember = members[channel.id]; + var unreadCount = this.getUnreadCount(channel.id); var msgCount; var linkClass = ''; @@ -284,7 +328,7 @@ export default class Sidebar extends React.Component { var unread = false; if (channelMember) { - msgCount = channel.total_msg_count - channelMember.msg_count; + msgCount = unreadCount.msgs + unreadCount.mentions; unread = (msgCount > 0 && channelMember.notify_props.mark_unread !== 'mention') || channelMember.mention_count > 0; } @@ -301,16 +345,8 @@ export default class Sidebar extends React.Component { var badge = null; if (channelMember) { - if (channel.type === 'D') { - // direct message channels show badges for any number of unread posts - msgCount = channel.total_msg_count - channelMember.msg_count; - if (msgCount > 0) { - badge = <span className='badge pull-right small'>{msgCount}</span>; - this.badgesActive = true; - } - } else if (channelMember.mention_count > 0) { - // public and private channels only show badges for mentions - badge = <span className='badge pull-right small'>{channelMember.mention_count}</span>; + if (unreadCount.mentions) { + badge = <span className='badge pull-right small'>{unreadCount.mentions}</span>; this.badgesActive = true; } } else if (this.state.loadingDMChannel === index && channel.type === 'D') { @@ -434,6 +470,8 @@ export default class Sidebar extends React.Component { render() { this.badgesActive = false; + this.setUnreadCountPerChannel(); + // keep track of the first and last unread channels so we can use them to set the unread indicators this.firstUnreadChannel = null; this.lastUnreadChannel = null; |