diff --git a/bigbluebutton-html5/imports/ui/components/chat/component.jsx b/bigbluebutton-html5/imports/ui/components/chat/component.jsx index 45b2d71626419368442fd266d40fa0118fbacfe7..4f55fc1f518a03ae07f14b0ebf0ad6adb65d9e48 100755 --- a/bigbluebutton-html5/imports/ui/components/chat/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/chat/component.jsx @@ -21,18 +21,6 @@ const intlMessages = defineMessages({ id: 'app.chat.hideChatLabel', description: 'aria-label for hiding chat button', }, - singularTyping: { - id: 'app.chat.singularTyping', - description: 'used to indicate when 1 user is typing', - }, - pluralTyping: { - id: 'app.chat.pluralTyping', - description: 'used to indicate when multiple user are typing', - }, - severalPeople: { - id: 'app.chat.severalPeople', - description: 'displayed when 4 or more users are typing', - }, }); const Chat = (props) => { const { @@ -46,10 +34,6 @@ const Chat = (props) => { intl, shortcuts, isMeteorConnected, - typingUsers, - currentUserId, - startUserTyping, - stopUserTyping, lastReadMessageTime, hasUnreadMessages, scrollPosition, @@ -61,47 +45,6 @@ const Chat = (props) => { const HIDE_CHAT_AK = shortcuts.hidePrivateChat; const CLOSE_CHAT_AK = shortcuts.closePrivateChat; - let names = []; - - names = typingUsers.map((user) => { - const currentChatPartner = chatID; - const { userId: typingUserId, isTypingTo, name } = user; - let userNameTyping = null; - userNameTyping = currentUserId !== typingUserId ? name : userNameTyping; - const isPrivateMsg = currentChatPartner !== isTypingTo; - if (isPrivateMsg) { - const isMsgParticipant = typingUserId === currentChatPartner && currentUserId === isTypingTo; - userNameTyping = isMsgParticipant ? name : null; - } - return userNameTyping; - }).filter(e => e); - - const renderIsTypingString = () => { - if (names) { - const { length } = names; - const noTypers = length < 1; - const singleTyper = length === 1; - const multipleTypersShown = length > 1 && length <= 3; - if (noTypers) return null; - - if (singleTyper) { - if (names[0].length < 20) { - return ` ${names[0]} ${intl.formatMessage(intlMessages.singularTyping)}`; - } - return (` ${names[0].slice(0, 20)}... ${intl.formatMessage(intlMessages.singularTyping)}`); - } - - if (multipleTypersShown) { - const formattedNames = names.map((name) => { - if (name.length < 15) return ` ${name}`; - return ` ${name.slice(0, 15)}...`; - }); - return (`${formattedNames} ${intl.formatMessage(intlMessages.pluralTyping)}`); - } - return (` ${intl.formatMessage(intlMessages.severalPeople)} ${intl.formatMessage(intlMessages.pluralTyping)}`); - } - }; - return ( <div data-test="publicChat" @@ -165,9 +108,6 @@ const Chat = (props) => { chatName, minMessageLength, maxMessageLength, - renderIsTypingString, - startUserTyping, - stopUserTyping, }} chatId={chatID} chatTitle={title} diff --git a/bigbluebutton-html5/imports/ui/components/chat/container.jsx b/bigbluebutton-html5/imports/ui/components/chat/container.jsx index b24e3d5c67b2a232bb66bcdda0bc6ceca974ebf0..b604e2c36a0547857773a3ba626a46e78b400851 100755 --- a/bigbluebutton-html5/imports/ui/components/chat/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/chat/container.jsx @@ -3,9 +3,6 @@ import { defineMessages, injectIntl } from 'react-intl'; import { withTracker } from 'meteor/react-meteor-data'; import { Session } from 'meteor/session'; import Auth from '/imports/ui/services/auth'; -import Users from '/imports/api/users'; -import { UsersTyping } from '/imports/api/group-chat-msg'; -import { makeCall } from '/imports/ui/services/api'; import Chat from './component'; import ChatService from './service'; @@ -41,9 +38,10 @@ class ChatContainer extends PureComponent { } render() { + const { children } = this.props; return ( <Chat {...this.props}> - {this.props.children} + {children} </Chat> ); } @@ -148,28 +146,7 @@ export default injectIntl(withTracker(({ intl }) => { const { connected: isMeteorConnected } = Meteor.status(); - const typingUsers = UsersTyping.find({ - meetingId: Auth.meetingID, - $or: [ - { isTypingTo: PUBLIC_CHAT_KEY }, - { isTypingTo: Auth.userID }, - ], - }).fetch(); - - const currentUser = Users.findOne({ - meetingId: Auth.meetingID, - userId: Auth.userID, - }, { - fields: { - userId: 1, - }, - }); - return { - startUserTyping: chatId => makeCall('startUserTyping', chatId), - stopUserTyping: () => makeCall('stopUserTyping'), - currentUserId: currentUser ? currentUser.userId : null, - typingUsers, chatID, chatName, title, diff --git a/bigbluebutton-html5/imports/ui/components/chat/message-form/component.jsx b/bigbluebutton-html5/imports/ui/components/chat/message-form/component.jsx index 9850c158adb38feee5ab2cbdec35ecb02d02d2cd..4d4cd4cf298446d2d0e1d28e86ae53977ba5ba94 100755 --- a/bigbluebutton-html5/imports/ui/components/chat/message-form/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/chat/message-form/component.jsx @@ -22,9 +22,11 @@ const propTypes = { connected: PropTypes.bool.isRequired, locked: PropTypes.bool.isRequired, partnerIsLoggedOut: PropTypes.bool.isRequired, - renderIsTypingString: PropTypes.func.isRequired, stopUserTyping: PropTypes.func.isRequired, startUserTyping: PropTypes.func.isRequired, + currentChatPartner: PropTypes.string.isRequired, + currentUserId: PropTypes.string.isRequired, + typingUsers: PropTypes.arrayOf(Object).isRequired, }; const defaultProps = { @@ -56,6 +58,18 @@ const messages = defineMessages({ errorChatLocked: { id: 'app.chat.locked', }, + singularTyping: { + id: 'app.chat.singularTyping', + description: 'used to indicate when 1 user is typing', + }, + pluralTyping: { + id: 'app.chat.pluralTyping', + description: 'used to indicate when multiple user are typing', + }, + severalPeople: { + id: 'app.chat.severalPeople', + description: 'displayed when 4 or more users are typing', + }, }); const CHAT_ENABLED = Meteor.settings.public.chat.enabled; @@ -76,6 +90,7 @@ class MessageForm extends PureComponent { this.handleMessageKeyDown = this.handleMessageKeyDown.bind(this); this.handleSubmit = this.handleSubmit.bind(this); this.setMessageHint = this.setMessageHint.bind(this); + this.renderIsTypingString = this.renderIsTypingString.bind(this); } componentDidMount() { @@ -257,6 +272,53 @@ class MessageForm extends PureComponent { ); } + renderIsTypingString() { + const { + intl, typingUsers, currentUserId, currentChatPartner, + } = this.props; + let names = []; + + names = typingUsers.map((user) => { + const { userId: typingUserId, isTypingTo, name } = user; + let userNameTyping = null; + userNameTyping = currentUserId !== typingUserId ? name : userNameTyping; + const isPrivateMsg = currentChatPartner !== isTypingTo; + if (isPrivateMsg) { + const isMsgParticipant = typingUserId === currentChatPartner + && currentUserId === isTypingTo; + + userNameTyping = isMsgParticipant ? name : null; + } + return userNameTyping; + }).filter(e => e); + + if (names) { + const { length } = names; + const noTypers = length < 1; + const singleTyper = length === 1; + const multipleTypersShown = length > 1 && length <= 3; + if (noTypers) return null; + + if (singleTyper) { + if (names[0].length < 20) { + return ` ${names[0]} ${intl.formatMessage(messages.singularTyping)}`; + } + return (` ${names[0].slice(0, 20)}... ${intl.formatMessage(messages.singularTyping)}`); + } + + if (multipleTypersShown) { + const formattedNames = names.map((name) => { + if (name.length < 15) return ` ${name}`; + return ` ${name.slice(0, 15)}...`; + }); + return (`${formattedNames} ${intl.formatMessage(messages.pluralTyping)}`); + } + return (` ${intl.formatMessage(messages.severalPeople)} ${intl.formatMessage(messages.pluralTyping)}`); + } + + return null; + } + render() { const { intl, @@ -265,7 +327,6 @@ class MessageForm extends PureComponent { disabled, className, chatAreaId, - renderIsTypingString, } = this.props; const { hasErrors, error, message } = this.state; @@ -309,8 +370,11 @@ class MessageForm extends PureComponent { </div> <div className={error ? styles.error : styles.info}> <span> - <span>{error || renderIsTypingString()}</span> - {!error && renderIsTypingString() ? <span className={styles.connectingAnimation} /> : null} + <span>{error || this.renderIsTypingString()}</span> + {!error && this.renderIsTypingString() + ? <span className={styles.connectingAnimation} /> + : null + } </span> </div> </form> diff --git a/bigbluebutton-html5/imports/ui/components/chat/message-form/container.jsx b/bigbluebutton-html5/imports/ui/components/chat/message-form/container.jsx index 780ed324e625826a94624e7c024b49a110125b3e..814529adb8fb246e5e6a22d06fc488ae0bbf6d46 100644 --- a/bigbluebutton-html5/imports/ui/components/chat/message-form/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/chat/message-form/container.jsx @@ -1,9 +1,15 @@ import React, { PureComponent } from 'react'; import { withTracker } from 'meteor/react-meteor-data'; +import Auth from '/imports/ui/services/auth'; +import { UsersTyping } from '/imports/api/group-chat-msg'; +import { makeCall } from '/imports/ui/services/api'; +import Users from '/imports/api/users'; import ChatForm from './component'; import ChatService from '../service'; const CHAT_CONFIG = Meteor.settings.public.chat; +const PUBLIC_CHAT_KEY = CHAT_CONFIG.public_id; + class ChatContainer extends PureComponent { render() { return ( @@ -17,7 +23,34 @@ export default withTracker(() => { ChatService.updateScrollPosition(null); return ChatService.sendGroupMessage(message); }; + + const typingUsers = UsersTyping.find({ + meetingId: Auth.meetingID, + $or: [ + { isTypingTo: PUBLIC_CHAT_KEY }, + { isTypingTo: Auth.userID }, + ], + }).fetch(); + + const currentUser = Users.findOne({ + meetingId: Auth.meetingID, + userId: Auth.userID, + }, { + fields: { + userId: 1, + }, + }); + + const startUserTyping = chatId => makeCall('startUserTyping', chatId); + + const stopUserTyping = () => makeCall('stopUserTyping'); + return { + startUserTyping, + stopUserTyping, + currentUserId: currentUser ? currentUser.userId : null, + typingUsers, + currentChatPartner: Session.get('idChatOpen'), UnsentMessagesCollection: ChatService.UnsentMessagesCollection, minMessageLength: CHAT_CONFIG.min_message_length, maxMessageLength: CHAT_CONFIG.max_message_length,