diff --git a/app/src/Server.js b/app/src/Server.js index 83947cb0..8394f68d 100644 --- a/app/src/Server.js +++ b/app/src/Server.js @@ -40,7 +40,7 @@ dependencies: { * @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon * @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com - * @version 1.0.6 + * @version 1.0.7 * */ diff --git a/package.json b/package.json index feb038b3..bb99a5c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mirotalksfu", - "version": "1.0.6", + "version": "1.0.7", "description": "WebRTC SFU browser-based video calls", "main": "Server.js", "scripts": { @@ -34,9 +34,9 @@ "author": "Miroslav Pejic", "license": "AGPL-3.0", "dependencies": { - "@sentry/integrations": "7.70.0", - "@sentry/node": "7.70.0", - "axios": "^1.5.0", + "@sentry/integrations": "7.73.0", + "@sentry/node": "7.73.0", + "axios": "^1.5.1", "body-parser": "1.20.2", "colors": "1.4.0", "compression": "1.7.4", @@ -47,7 +47,7 @@ "mediasoup": "3.12.13", "mediasoup-client": "3.6.101", "ngrok": "^4.3.3", - "openai": "^4.10.0", + "openai": "^4.11.1", "qs": "6.11.2", "socket.io": "4.7.2", "swagger-ui-express": "5.0.0", diff --git a/public/css/Room.css b/public/css/Room.css index d50ee49d..cdcb8aeb 100644 --- a/public/css/Room.css +++ b/public/css/Room.css @@ -193,7 +193,7 @@ body { } #control button { - font-size: 1.2rem; + font-size: 1rem; padding: 10px; transition: all 0.3s ease-in-out; background: var(--btns-bg-color); @@ -202,11 +202,15 @@ body { /* box-shadow: var(--box-shadow); */ } +#control button i { + font-size: 1.2rem; +} + @media screen and (max-width: 540px) { #control button { font-size: 0.8rem; } - #control i { + #control button i { font-size: 0.8rem; cursor: default; } @@ -753,16 +757,46 @@ a:hover { } /*-------------------------------------------------------------- -# Chat room emoji picker +# Chat/room/user emoji picker --------------------------------------------------------------*/ -em-emoji-picker { +.chatEmojiPicker { z-index: 0; position: absolute; bottom: 110px; left: 10px; } +.roomEmoji { + z-index: 9; + position: absolute; + display: none; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.userEmoji { + z-index: 9; + position: absolute; + left: 80px; + bottom: 60px; +} + +.emojiPickerHeader { + display: flex; + justify-content: flex-start; + background: #151616; + border-radius: 10px; + padding: 10px; + color: #fff; + cursor: move; +} + +#closeEmojiPickerContainer { + font-size: 1.3rem; +} + /*-------------------------------------------------------------- # swal2 --------------------------------------------------------------*/ @@ -1272,4 +1306,5 @@ z-index: - 6 settings - 7 participants/lobby - 8 send receive progress + - 9 room/user emoji */ diff --git a/public/js/Room.js b/public/js/Room.js index d0e6d14f..06fd2b27 100644 --- a/public/js/Room.js +++ b/public/js/Room.js @@ -11,7 +11,7 @@ if (location.href.substr(0, 5) !== 'https') location.href = 'https' + location.h * @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon * @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com - * @version 1.0.6 + * @version 1.0.7 * */ @@ -145,6 +145,7 @@ function initClient() { setTheme(); if (!DetectRTC.isMobileDevice) { refreshMainButtonsToolTipPlacement(); + setTippy('closeEmojiPickerContainer', 'Close', 'bottom'); setTippy('mySettingsCloseBtn', 'Close', 'bottom'); setTippy( 'switchPushToTalk', @@ -228,6 +229,7 @@ function refreshMainButtonsToolTipPlacement() { setTippy('stopRecButton', 'Stop recording', placement); setTippy('raiseHandButton', 'Raise your hand', placement); setTippy('lowerHandButton', 'Lower your hand', placement); + setTippy('roomEmojiPicker', 'Toggle emoji reaction', placement); setTippy('swapCameraButton', 'Swap the camera', placement); setTippy('chatButton', 'Toggle the chat', placement); setTippy('participantsButton', 'Toggle participants', placement); @@ -935,6 +937,7 @@ function roomIsReady() { } BUTTONS.main.chatButton && show(chatButton); BUTTONS.main.participantsButton && show(participantsButton); + BUTTONS.main.emojiRoomButton && show(roomEmojiPicker); !BUTTONS.chat.chatSaveButton && hide(chatSaveButton); BUTTONS.chat.chatEmojiButton && show(chatEmojiButton); BUTTONS.chat.chatMarkdownButton && show(chatMarkdownButton); @@ -955,6 +958,7 @@ function roomIsReady() { hide(chatMaxButton); hide(chatMinButton); } else { + rc.makeDraggable(emojiPickerContainer, emojiPickerHeader); rc.makeDraggable(chatRoom, chatHeader); rc.makeDraggable(mySettings, mySettingsHeader); rc.makeDraggable(participants, participantsHeader); @@ -993,6 +997,8 @@ function roomIsReady() { handleButtons(); handleSelects(); handleInputs(); + handleChatEmojiPicker(); + handleRoomEmojiPicker(); loadSettingsFromLocalStorage(); startSessionTimer(); document.body.addEventListener('mousemove', (e) => { @@ -1742,7 +1748,13 @@ function handleInputs() { isChatPasteTxt = true; rc.checkLineBreaks(); }; +} +// #################################################### +// EMOJI PIKER +// #################################################### + +function handleChatEmojiPicker() { const pickerOptions = { theme: 'dark', onEmojiSelect: addEmojiToMsg, @@ -1756,6 +1768,42 @@ function handleInputs() { } } +function handleRoomEmojiPicker() { + const pickerRoomOptions = { + theme: 'dark', + onEmojiSelect: sendEmojiToRoom, + }; + + const emojiRoomPicker = new EmojiMart.Picker(pickerRoomOptions); + emojiPickerContainer.appendChild(emojiRoomPicker); + emojiPickerContainer.style.display = 'none'; + + roomEmojiPicker.onclick = () => { + toggleEmojiPicker(); + }; + closeEmojiPickerContainer.onclick = () => { + toggleEmojiPicker(); + }; + + function sendEmojiToRoom(data) { + console.log('Selected Emoji:', data.native); + const cmd = `roomEmoji|${peer_name}|${data.native}`; + rc.emitCmd(cmd); + rc.handleCmd(cmd); + // toggleEmojiPicker(); + } + + function toggleEmojiPicker() { + if (emojiPickerContainer.style.display === 'block') { + emojiPickerContainer.style.display = 'none'; + setColor(roomEmojiPicker, 'white'); + } else { + emojiPickerContainer.style.display = 'block'; + setColor(roomEmojiPicker, 'yellow'); + } + } +} + // #################################################### // LOAD SETTINGS FROM LOCAL STORAGE // #################################################### @@ -2549,7 +2597,7 @@ function wbUpdate() { function wbCanvasToJson() { if (!isPresenter && wbIsLock) return; - if (rc.thereIsParticipants()) { + if (rc.thereAreParticipants()) { let wbCanvasJson = JSON.stringify(wbCanvas.toJSON()); rc.socket.emit('wbCanvasToJson', wbCanvasJson); } @@ -2593,7 +2641,7 @@ function confirmClearBoard() { function whiteboardAction(data, emit = true) { if (emit) { - if (rc.thereIsParticipants()) { + if (rc.thereAreParticipants()) { rc.socket.emit('whiteboardAction', data); } } else { diff --git a/public/js/RoomClient.js b/public/js/RoomClient.js index 499f57dd..6f9f152d 100644 --- a/public/js/RoomClient.js +++ b/public/js/RoomClient.js @@ -9,7 +9,7 @@ * @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon * @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com - * @version 1.0.6 + * @version 1.0.7 * */ @@ -2412,7 +2412,7 @@ class RoomClient { } } - thereIsParticipants() { + thereAreParticipants() { // console.log('participantsCount ---->', participantsCount); if (this.consumers.size > 0 || participantsCount > 1) { return true; @@ -2945,7 +2945,7 @@ class RoomClient { } sendMessage() { - if (!this.thereIsParticipants() && !isChatGPTOn) { + if (!this.thereAreParticipants() && !isChatGPTOn) { this.cleanMessage(); isChatPasteTxt = false; return this.userLog('info', 'No participants in the room', 'top-end'); @@ -3001,7 +3001,7 @@ class RoomClient { } sendMessageTo(to_peer_id, to_peer_name) { - if (!this.thereIsParticipants()) { + if (!this.thereAreParticipants()) { isChatPasteTxt = false; this.cleanMessage(); return this.userLog('info', 'No participants in the room except you', 'top-end'); @@ -3616,7 +3616,7 @@ class RoomClient { } recordingAction(action) { - if (!this.thereIsParticipants()) return; + if (!this.thereAreParticipants()) return; this.socket.emit('recordingAction', { peer_name: this.peer_name, peer_id: this.peer_id, @@ -3706,7 +3706,7 @@ class RoomClient { this.fileToSend = file; // if (this.fileToSend && this.fileToSend.size > 0) { - if (!this.thereIsParticipants()) { + if (!this.thereAreParticipants()) { return userLog('info', 'No participants detected', 'top-end'); } // prevent XSS injection @@ -4001,7 +4001,7 @@ class RoomClient { }).then((result) => { if (result.value) { result.value = filterXSS(result.value); - if (!this.thereIsParticipants()) { + if (!this.thereAreParticipants()) { return userLog('info', 'No participants detected', 'top-end'); } if (!this.isVideoTypeSupported(result.value)) { @@ -4699,12 +4699,30 @@ class RoomClient { case 'privacy': this.setVideoPrivacyStatus(words[1], words[2] == 'true'); break; + case 'roomEmoji': + this.handleRoomEmoji(words); + break; default: break; //... } } + handleRoomEmoji(words, duration = 5000) { + const userEmoji = document.getElementById(`userEmoji`); + if (userEmoji) { + const emojiDisplay = document.createElement('div'); + emojiDisplay.className = 'animate__animated animate__backInUp'; + emojiDisplay.style.fontSize = '3vh'; + emojiDisplay.style.color = 'grey'; + emojiDisplay.innerText = `${words[2]} ${words[1]}`; + userEmoji.appendChild(emojiDisplay); + setTimeout(() => { + emojiDisplay.remove(); + }, duration); + } + } + // #################################################### // PEER ACTION // #################################################### @@ -4723,7 +4741,7 @@ class RoomClient { broadcast: broadcast, }; - if (!this.thereIsParticipants()) { + if (!this.thereAreParticipants()) { if (info) return this.userLog('info', 'No participants detected', 'top-end'); } switch (action) { diff --git a/public/js/Rules.js b/public/js/Rules.js index 96d71e04..cfb9bafa 100644 --- a/public/js/Rules.js +++ b/public/js/Rules.js @@ -19,6 +19,7 @@ const BUTTONS = { chatButton: true, participantsButton: true, whiteboardButton: true, + emojiRoomButton: true, settingsButton: true, aboutButton: true, // Please keep me always visible, thank you! exitButton: true, diff --git a/public/views/Room.html b/public/views/Room.html index 7434dce0..7ca5be93 100644 --- a/public/views/Room.html +++ b/public/views/Room.html @@ -146,6 +146,7 @@ access to use this app. + @@ -154,6 +155,13 @@ access to use this app. +