123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- /* global APP, $ */
-
- import { processReplacements, linkify } from './Replacement';
- import CommandsProcessor from './Commands';
- import VideoLayout from '../../videolayout/VideoLayout';
-
- import UIUtil from '../../util/UIUtil';
- import UIEvents from '../../../../service/UI/UIEvents';
-
- import { smileys } from './smileys';
-
- import { dockToolbox, setSubject } from '../../../../react/features/toolbox';
-
- let unreadMessages = 0;
- const sidePanelsContainerId = 'sideToolbarContainer';
- const htmlStr = `
- <div id="chat_container" class="sideToolbarContainer__inner">
- <div id="nickname">
- <span data-i18n="chat.nickname.title"></span>
- <form>
- <input type='text'
- class="input-control" id="nickinput" autofocus
- data-i18n="[placeholder]chat.nickname.popover">
- </form>
- </div>
-
- <div id="chatconversation"></div>
- <audio id="chatNotification" src="sounds/incomingMessage.wav"
- preload="auto"></audio>
- <textarea id="usermsg" autofocus
- data-i18n="[placeholder]chat.messagebox"></textarea>
- <div id="smileysarea">
- <div id="smileys">
- <img src="images/smile.svg"/>
- </div>
- </div>
- </div>`;
-
- /**
- *
- */
- function initHTML() {
- $(`#${sidePanelsContainerId}`)
- .append(htmlStr);
- }
-
- /**
- * The container id, which is and the element id.
- */
- const CHAT_CONTAINER_ID = 'chat_container';
-
- /**
- * Updates visual notification, indicating that a message has arrived.
- */
- function updateVisualNotification() {
- // XXX The rewrite of the toolbar in React delayed the availability of the
- // element unreadMessages. In order to work around the delay, I introduced
- // and utilized unreadMsgSelector in addition to unreadMsgElement.
- const unreadMsgSelector = $('#unreadMessages');
- const unreadMsgElement
- = unreadMsgSelector.length > 0 ? unreadMsgSelector[0] : undefined;
-
- if (unreadMessages && unreadMsgElement) {
- unreadMsgElement.innerHTML = unreadMessages.toString();
-
- APP.store.dispatch(dockToolbox(true));
-
- const chatButtonElement
- = document.getElementById('toolbar_button_chat');
- const leftIndent
- = (UIUtil.getTextWidth(chatButtonElement)
- - UIUtil.getTextWidth(unreadMsgElement)) / 2;
- const topIndent
- = ((UIUtil.getTextHeight(chatButtonElement)
- - UIUtil.getTextHeight(unreadMsgElement)) / 2) - 5;
-
- unreadMsgElement.setAttribute(
- 'style',
- `top:${topIndent}; left:${leftIndent};`);
- } else {
- unreadMsgSelector.html('');
- }
-
- if (unreadMsgElement) {
- unreadMsgSelector.parent()[unreadMessages > 0 ? 'show' : 'hide']();
- }
- }
-
-
- /**
- * Returns the current time in the format it is shown to the user
- * @returns {string}
- */
- function getCurrentTime(stamp) {
- const now = stamp ? new Date(stamp) : new Date();
- let hour = now.getHours();
- let minute = now.getMinutes();
- let second = now.getSeconds();
-
- if (hour.toString().length === 1) {
- hour = `0${hour}`;
- }
- if (minute.toString().length === 1) {
- minute = `0${minute}`;
- }
- if (second.toString().length === 1) {
- second = `0${second}`;
- }
-
- return `${hour}:${minute}:${second}`;
- }
-
- /**
- *
- */
- function toggleSmileys() {
- const smileys = $('#smileysContainer'); // eslint-disable-line no-shadow
-
- if (smileys.is(':visible')) {
- smileys.hide('slide', { direction: 'down',
- duration: 300 });
- } else {
- smileys.show('slide', { direction: 'down',
- duration: 300 });
- }
- $('#usermsg').focus();
- }
-
- /**
- *
- */
- function addClickFunction(smiley, number) {
- smiley.onclick = function addSmileyToMessage() {
- const usermsg = $('#usermsg');
- let message = usermsg.val();
-
- message += smileys[`smiley${number}`];
- usermsg.val(message);
- usermsg.get(0).setSelectionRange(message.length, message.length);
- toggleSmileys();
- usermsg.focus();
- };
- }
-
- /**
- * Adds the smileys container to the chat
- */
- function addSmileys() {
- const smileysContainer = document.createElement('div');
-
- smileysContainer.id = 'smileysContainer';
- for (let i = 1; i <= 21; i++) {
- const smileyContainer = document.createElement('div');
-
- smileyContainer.id = `smiley${i}`;
- smileyContainer.className = 'smileyContainer';
- const smiley = document.createElement('img');
-
- smiley.src = `images/smileys/smiley${i}.svg`;
- smiley.className = 'smiley';
- addClickFunction(smiley, i);
- smileyContainer.appendChild(smiley);
- smileysContainer.appendChild(smileyContainer);
- }
-
- $('#chat_container').append(smileysContainer);
- }
-
- /**
- * Resizes the chat conversation.
- */
- function resizeChatConversation() {
- const msgareaHeight = $('#usermsg').outerHeight();
- const chatspace = $(`#${CHAT_CONTAINER_ID}`);
- const width = chatspace.width();
- const chat = $('#chatconversation');
- const smileys = $('#smileysarea'); // eslint-disable-line no-shadow
-
- smileys.height(msgareaHeight);
- $('#smileys').css('bottom', (msgareaHeight - 26) / 2);
- $('#smileysContainer').css('bottom', msgareaHeight);
- chat.width(width - 10);
- chat.height(window.innerHeight - 15 - msgareaHeight);
- }
-
- /**
- * Focus input after 400 ms
- * Found input by id
- *
- * @param id {string} input id
- */
- function deferredFocus(id) {
- setTimeout(() => $(`#${id}`).focus(), 400);
- }
-
- /**
- * Chat related user interface.
- */
- const Chat = {
- /**
- * Initializes chat related interface.
- */
- init(eventEmitter) {
- initHTML();
- if (APP.conference.getLocalDisplayName()) {
- Chat.setChatConversationMode(true);
- }
-
- $('#smileys').click(() => {
- Chat.toggleSmileys();
- });
-
- $('#nickinput').keydown(function(event) {
- if (event.keyCode === 13) {
- event.preventDefault();
- const val = this.value; // eslint-disable-line no-invalid-this
-
- this.value = '';// eslint-disable-line no-invalid-this
- eventEmitter.emit(UIEvents.NICKNAME_CHANGED, val);
- deferredFocus('usermsg');
- }
- });
-
- const usermsg = $('#usermsg');
-
- usermsg.keydown(function(event) {
- if (event.keyCode === 13) {
- event.preventDefault();
- const value = this.value; // eslint-disable-line no-invalid-this
-
- usermsg.val('').trigger('autosize.resize');
- this.focus();// eslint-disable-line no-invalid-this
- const command = new CommandsProcessor(value, eventEmitter);
-
- if (command.isCommand()) {
- command.processCommand();
- } else {
- const message = UIUtil.escapeHtml(value);
-
- eventEmitter.emit(UIEvents.MESSAGE_CREATED, message);
- }
- }
- });
-
- const onTextAreaResize = function() {
- resizeChatConversation();
- Chat.scrollChatToBottom();
- };
-
- usermsg.autosize({ callback: onTextAreaResize });
-
- eventEmitter.on(UIEvents.SIDE_TOOLBAR_CONTAINER_TOGGLED,
- (containerId, isVisible) => {
- if (containerId !== CHAT_CONTAINER_ID || !isVisible) {
- return;
- }
-
- unreadMessages = 0;
- updateVisualNotification();
-
- // Undock the toolbar when the chat is shown and if we're in a
- // video mode.
- if (VideoLayout.isLargeVideoVisible()) {
- APP.store.dispatch(dockToolbox(false));
- }
-
- // if we are in conversation mode focus on the text input
- // if we are not, focus on the display name input
- deferredFocus(
- APP.conference.getLocalDisplayName()
- ? 'usermsg'
- : 'nickinput');
- });
-
- addSmileys();
- updateVisualNotification();
- },
-
- /**
- * Appends the given message to the chat conversation.
- */
- // eslint-disable-next-line max-params
- updateChatConversation(id, displayName, message, stamp) {
- let divClassName = '';
-
- if (APP.conference.isLocalId(id)) {
- divClassName = 'localuser';
- } else {
- divClassName = 'remoteuser';
-
- if (!Chat.isVisible()) {
- unreadMessages++;
- UIUtil.playSoundNotification('chatNotification');
- updateVisualNotification();
- }
- }
-
- // replace links and smileys
- // Strophe already escapes special symbols on sending,
- // so we escape here only tags to avoid double &
- const escMessage = message.replace(/</g, '<')
- .replace(/>/g, '>')
- .replace(/\n/g, '<br/>');
- const escDisplayName = UIUtil.escapeHtml(displayName);
-
- // eslint-disable-next-line no-param-reassign
- message = processReplacements(escMessage);
-
- const messageContainer
- = `${'<div class="chatmessage">'
- + '<img src="images/chatArrow.svg" class="chatArrow">'
- + '<div class="username '}${divClassName}">${escDisplayName
- }</div><div class="timestamp">${getCurrentTime(stamp)
- }</div><div class="usermessage">${message}</div>`
- + '</div>';
-
- $('#chatconversation').append(messageContainer);
- $('#chatconversation').animate(
- { scrollTop: $('#chatconversation')[0].scrollHeight }, 1000);
- },
-
- /**
- * Appends error message to the conversation
- * @param errorMessage the received error message.
- * @param originalText the original message.
- */
- chatAddError(errorMessage, originalText) {
- // eslint-disable-next-line no-param-reassign
- errorMessage = UIUtil.escapeHtml(errorMessage);
- // eslint-disable-next-line no-param-reassign
- originalText = UIUtil.escapeHtml(originalText);
-
- $('#chatconversation').append(
- `${'<div class="errorMessage"><b>Error: </b>Your message'}${
- originalText ? ` "${originalText}"` : ''
- } was not sent.${
- errorMessage ? ` Reason: ${errorMessage}` : ''}</div>`);
- $('#chatconversation').animate(
- { scrollTop: $('#chatconversation')[0].scrollHeight }, 1000);
- },
-
- /**
- * Sets the subject to the UI
- * @param subject the subject
- */
- setSubject(subject) {
- if (subject) {
- // eslint-disable-next-line no-param-reassign
- subject = subject.trim();
- }
-
- const html = linkify(UIUtil.escapeHtml(subject));
-
- APP.store.dispatch(setSubject(html));
- },
-
- /**
- * Sets the chat conversation mode.
- * Conversation mode is the normal chat mode, non conversation mode is
- * where we ask user to input its display name.
- * @param {boolean} isConversationMode if chat should be in
- * conversation mode or not.
- */
- setChatConversationMode(isConversationMode) {
- $(`#${CHAT_CONTAINER_ID}`)
- .toggleClass('is-conversation-mode', isConversationMode);
- },
-
- /**
- * Resizes the chat area.
- */
- resizeChat(width, height) {
- $(`#${CHAT_CONTAINER_ID}`).width(width)
- .height(height);
-
- resizeChatConversation();
- },
-
- /**
- * Indicates if the chat is currently visible.
- */
- isVisible() {
- return UIUtil.isVisible(
- document.getElementById(CHAT_CONTAINER_ID));
- },
-
- /**
- * Shows and hides the window with the smileys
- */
- toggleSmileys,
-
- /**
- * Scrolls chat to the bottom.
- */
- scrollChatToBottom() {
- setTimeout(
- () => {
- const chatconversation = $('#chatconversation');
-
- // XXX Prevent TypeError: undefined is not an object when the
- // Web browser does not support WebRTC (yet).
- chatconversation.length > 0
- && chatconversation.scrollTop(
- chatconversation[0].scrollHeight);
- },
- 5);
- }
- };
-
- export default Chat;
|