[mirotalksfu] - #127 add Group Chat feature

هذا الالتزام موجود في:
Miroslav Pejic
2023-11-09 19:51:35 +01:00
الأصل 200b8868ba
التزام f017115910
12 ملفات معدلة مع 1117 إضافات و739 حذوفات

عرض الملف

@@ -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.1.4
* @version 1.2.0
*
*/

عرض الملف

@@ -1,6 +1,6 @@
{
"name": "mirotalksfu",
"version": "1.1.4",
"version": "1.2.0",
"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.75.1",
"@sentry/node": "7.75.1",
"axios": "^1.6.0",
"@sentry/integrations": "7.79.0",
"@sentry/node": "7.79.0",
"axios": "^1.6.1",
"body-parser": "1.20.2",
"colors": "1.4.0",
"compression": "1.7.4",
@@ -47,7 +47,7 @@
"mediasoup": "3.12.16",
"mediasoup-client": "3.6.103",
"ngrok": "^4.3.3",
"openai": "^4.14.0",
"openai": "^4.17.0",
"qs": "6.11.2",
"socket.io": "4.7.2",
"swagger-ui-express": "5.0.0",

353
public/css/GroupChat.css Normal file
عرض الملف

@@ -0,0 +1,353 @@
.container {
padding: 2px !important;
transition: width 0.5s !important;
/* overflow-y: auto;
overflow-x: hidden; */
}
/* Chat Room */
.chat-container {
z-index: 5;
position: relative;
width: var(--msger-width);
height: var(--msger-height);
min-width: var(--msger-width);
min-height: var(--msger-height);
padding: 3px;
background: var(--msger-bg);
border: var(--border);
border-radius: 10px;
box-shadow: var(--box-shadow);
transition: background 1s;
/* border: 1px solid lime; */
}
/* Chat app container */
.chat-app {
position: absolute;
width: 100%;
height: 100%;
border-radius: 10px;
/* overflow-y: auto;
overflow-x: hidden; */
/* border: 1px solid lime; */
}
/* Chat app people container */
.chat-app .people-list {
z-index: 6;
position: absolute;
padding: 20px;
top: 0;
left: 0;
width: 300px;
height: 99%;
background: var(--msger-bg);
overflow-y: auto;
overflow-x: hidden;
/* border: 1px solid lime; */
}
/* Chat app people list container */
.people-list {
-moz-transition: 0.5s;
-o-transition: 0.5s;
-webkit-transition: 0.5s;
transition: 0.5s;
/* border: 1px solid lime; */
}
.people-list .chat-list li {
padding: 10px 15px;
list-style: none;
border-radius: 3px;
}
.people-list .chat-list li:hover {
background: rgba(0, 0, 0, 0.2);
cursor: pointer;
}
.people-list .chat-list li.active {
background: rgba(0, 0, 0, 0.2);
}
.people-list .chat-list li .name {
color: #fff;
font-size: 15px;
}
.people-list .chat-list img {
width: 45px;
border-radius: 50%;
}
.people-list img {
float: left;
border-radius: 50%;
}
.people-list .about {
float: left;
padding-left: 8px;
}
.people-list .about-buttons {
margin-top: 10px;
display: inline-flex;
}
.people-list .status {
color: #999;
font-size: 13px;
}
/* Chat app people list container */
.chat-app .chat-list {
height: 550px;
/* border: 1px solid lime; */
}
/* Chat app container */
.chat-app .chat {
position: relative;
margin-left: 300px;
border-left: var(--border);
border-radius: 10px;
/* border: 1px solid lime; */
}
/* Chat header */
.chat .chat-header {
padding: 15px 20px; /* top, right, bottom, left */
border-bottom: var(--border);
height: 70px;
max-height: 70px;
cursor: move;
/* overflow-x: auto; */
/* border: 1px solid lime; */
}
.all-participants-img {
border: var(--border);
width: 40px;
margin-right: 5px;
cursor: pointer;
}
.all-participants-img:hover {
background-color: #000000;
transition: all 0.3s ease-in-out;
}
.chat .chat-header img {
float: left;
border-radius: 40px;
width: 40px;
}
.chat .chat-header .chat-about {
float: left;
padding-left: 10px;
color: #fff;
}
.chat .chat-header .status {
color: #999;
font-size: 13px;
}
/* Chat history */
.chat .chat-history {
padding: 20px;
min-height: 500px;
max-height: 500px;
border-bottom: var(--border);
overflow-x: auto;
/* border: 1px solid lime; */
}
.chat .chat-history ul {
padding: 0;
}
.chat .chat-history ul li {
list-style: none;
margin-bottom: 30px;
}
.chat .chat-history ul li:last-child {
margin-bottom: 0px;
}
.chat .chat-history .message-data {
margin-bottom: 15px;
}
.chat .chat-history .message-data img {
border-radius: 40px;
width: 40px;
}
.chat .chat-history .message-data-time {
color: #eeeeee;
padding-left: 6px;
}
/* chat message bubble */
.chat .chat-history .message {
color: #fff;
padding: 18px 20px;
line-height: 26px;
font-size: 16px;
max-width: 100%;
border-radius: 7px;
display: inline-block;
position: relative;
/* border: 1px solid lime; */
}
.chat .chat-history .message:after {
bottom: 100%;
left: 7%;
border: solid transparent;
content: ' ';
height: 0;
width: 0;
position: absolute;
pointer-events: none;
border-bottom-color: grey;
border-width: 10px;
margin-left: -10px;
}
.chat .chat-history .my-message {
background: var(--right-msg-bg);
}
.chat .chat-history .my-message:after {
bottom: 100%;
left: 30px;
border: solid transparent;
content: ' ';
height: 0;
width: 0;
position: absolute;
pointer-events: none;
border-bottom-color: #999;
border-width: 10px;
margin-left: -10px;
}
.chat .chat-history .other-message {
background: var(--left-msg-bg);
text-align: right;
}
.chat .chat-history .other-message:after {
border-bottom-color: grey;
left: 93%;
}
/* Chat message */
.chat .chat-message {
padding: 20px;
height: 130px;
max-height: 130px;
overflow-y: auto;
overflow-x: hidden;
/* border: 1px solid lime; */
}
/* Chat emoji */
.chatEmojiPicker {
z-index: 0;
position: absolute;
left: 5px;
bottom: 135px;
}
/* status */
.online,
.offline,
.me {
margin-right: 2px;
font-size: 8px;
vertical-align: middle;
}
.online {
color: #86c541;
}
.offline {
color: #e47297;
}
.me {
color: #1d8ecd;
}
.float-right {
float: right;
}
.float-left {
float: left;
}
.inline {
display: inline-flex;
}
.absolute {
position: absolute;
}
.clearfix:after {
visibility: hidden;
display: block;
font-size: 0;
content: ' ';
clear: both;
height: 0;
}
/** common **/
.mt5 {
margin-top: 5px;
}
.mt10 {
margin-top: 10px;
}
.mr5 {
margin-right: 5px;
}
.ml5 {
margin-left: 5px;
}
.ml10 {
margin-left: 10px;
}
/* Chat search and input message */
textarea,
input[type='text'] {
background: var(--msger-bg) !important;
color: #fff !important;
transition: height 0.5s !important;
}
.form-check-input {
cursor: pointer;
}
.hr {
border: 0;
display: block;
height: 1px;
background: #000000;
margin-top: 0px;
margin-bottom: 10px;
}

عرض الملف

@@ -35,10 +35,8 @@
--body-bg: radial-gradient(#393939, #000000);
--border: 1px solid rgb(255 255 255 / 32%);
--border-radius: 1rem;
--msger-top: 50%;
--msger-left: 50%;
--msger-height: 680px;
--msger-width: 420px;
--msger-height: 700px;
--msger-width: 800px;
--msger-bubble-width: 85%;
--msger-bg: radial-gradient(#393939, #000000);
--wb-width: 800px;
@@ -698,254 +696,9 @@ th {
}
/*--------------------------------------------------------------
# Chat Room
# Room/user emoji picker
--------------------------------------------------------------*/
.chat-room {
z-index: 5;
display: none;
position: fixed;
height: var(--msger-height);
width: var(--msger-width);
min-height: var(--msger-height);
min-width: var(--msger-width);
background: var(--msger-bg);
border: var(--border);
border-radius: 10px;
box-shadow: var(--box-shadow);
resize: both;
overflow: hidden;
}
.msger {
width: 100%;
height: 100%;
display: flex;
flex-flow: column wrap;
justify-content: space-between;
background: var(--msger-bg);
}
/*--------------------------------------------------------------
# Chat room header
--------------------------------------------------------------*/
.chat-header {
display: flex;
justify-content: space-between;
padding: 10px;
background: rgba(0, 0, 0, 0.2);
color: #666;
cursor: move;
}
.chat-header-title button,
.chat-header-options button {
border: none;
transition: all 0.3s ease-in-out;
background: rgba(0, 0, 0, 0.2);
color: #fff;
border-radius: 5px;
transition: background 0.23s;
}
/*--------------------------------------------------------------
# Chat room output area
--------------------------------------------------------------*/
.chat-msger {
flex: 1;
overflow-y: auto;
padding: 10px;
background: var(--msger-bg);
max-width: 100%;
}
.chat-msger::-webkit-scrollbar {
width: 5px;
}
.chat-msger::-webkit-scrollbar-track {
background: transparent;
}
.chat-msger::-webkit-scrollbar-thumb {
background: black;
/*aqua;*/
}
.msg {
display: flex;
align-items: flex-end;
margin-bottom: 10px;
}
.msg:last-of-type {
margin: 0;
}
/*--------------------------------------------------------------
# Chat room left side
--------------------------------------------------------------*/
.left-msg .msg-bubble {
background: var(--left-msg-bg);
border-bottom-left-radius: 0;
color: #fff;
width: var(--msger-bubble-width);
}
.left-msg .msg-bubble-private {
background: var(--private-msg-bg);
border-bottom-left-radius: 0;
color: #fff;
width: var(--msger-bubble-width);
}
.left-msg .msg-img {
margin: 0px 5px 5px 0px;
}
/*--------------------------------------------------------------
# Chat room right side
--------------------------------------------------------------*/
.right-msg {
flex-direction: row-reverse;
}
.right-msg .msg-bubble {
background: var(--right-msg-bg);
border-bottom-right-radius: 0;
color: #fff;
width: var(--msger-bubble-width);
}
.right-msg .msg-bubble-private {
background: var(--private-msg-bg);
border-bottom-right-radius: 0;
color: #fff;
width: var(--msger-bubble-width);
}
.right-msg .msg-img {
margin: 0px 0px 5px 5px;
}
/*--------------------------------------------------------------
# Chat room common
--------------------------------------------------------------*/
.msg-img {
width: 50px;
height: 50px;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
}
.msg-bubble-private,
.msg-bubble {
/* max-width: 200px; */
padding: 15px;
border-radius: 15px;
}
.msg-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.msg-info-name {
margin-right: 10px;
font-weight: bold;
}
.msg-info-time {
font-size: 0.85em;
}
.msg-text {
display: inline;
overflow-wrap: break-word;
word-wrap: break-word;
hyphens: auto;
}
.msg-text iframe {
width: 100%;
}
a {
color: #fff !important;
}
a:hover {
color: grey !important;
transition: all 0.3s ease-in-out;
}
/*--------------------------------------------------------------
# Chat room input area
--------------------------------------------------------------*/
.chat-msger-inputarea {
display: flex;
padding: 10px;
}
.chat-msger-input {
flex: 1;
padding: 10px;
height: 43px;
font-size: 14px;
color: #fff;
border: none;
border: var(--border);
border-top-left-radius: 10px;
background: rgba(0, 0, 0, 0.2);
transition: height 1s;
resize: none;
}
.chat-msger-input::-webkit-scrollbar {
width: 5px;
}
.chat-msger-input::-webkit-scrollbar-track {
background: transparent;
}
.chat-msger-input::-webkit-scrollbar-thumb {
background: #404040;
}
/*--------------------------------------------------------------
# Chat room buttons area
--------------------------------------------------------------*/
.chat-msger-buttons {
display: flex;
padding: 10px;
}
#chatSendButton {
position: absolute;
right: 10px;
}
/*--------------------------------------------------------------
# Chat/room/user emoji picker
--------------------------------------------------------------*/
.chatEmojiPicker {
z-index: 0;
position: absolute;
bottom: 110px;
left: 10px;
}
.roomEmoji {
z-index: 9;
position: absolute;
@@ -1070,11 +823,11 @@ select {
}
.hidden {
display: none;
display: none !important;
}
.show {
display: block;
display: block !important;
}
.center {
@@ -1115,94 +868,6 @@ button:hover {
transform: var(--btns-hover-scale);
}
/*--------------------------------------------------------------
# Room Participants
--------------------------------------------------------------*/
#participants {
z-index: 7;
position: absolute;
margin: auto;
padding: 10px;
min-width: 320px;
background: var(--body-bg);
border: var(--border);
box-shadow: var(--box-shadow);
border-radius: 10px;
overflow: hidden;
}
.participants-header {
display: flex;
justify-content: space-between;
background: rgba(0, 0, 0, 0.2);
border-radius: 10px;
padding: 10px;
color: #fff;
cursor: move;
}
#roomParticipants {
max-height: 680px;
overflow: auto;
}
#roomParticipants table {
border-collapse: collapse;
width: 100%;
}
#roomParticipants td,
#roomParticipants th {
/* border: 1px solid #444444; */
color: #fff;
text-align: left;
padding: 8px;
}
/* #roomParticipants tr {
border-bottom: 1px solid #444444;
} */
/* #roomParticipants tr:nth-child(even) {
background-color: #0c0b0b;
} */
#roomParticipants::-webkit-scrollbar {
width: 5px;
}
#roomParticipants::-webkit-scrollbar-track {
background: transparent;
}
#roomParticipants::-webkit-scrollbar-thumb {
background: #404040;
}
#inviteParticipants {
width: 100%;
padding: 10px;
margin: 10px auto 0px auto;
background-color: #056162;
border-radius: 10px;
}
#inviteParticipants:hover {
background-color: #191919;
color: #fff;
transform: scale(1);
}
#searchParticipants {
margin-top: 15px;
width: 100%;
padding: 15px;
font-size: 1rem;
background-color: transparent;
color: #ffffff;
border: grey 1px solid;
border-radius: 10px;
}
/*--------------------------------------------------------------
# Lobby users list
--------------------------------------------------------------*/
@@ -1503,7 +1168,7 @@ z-index:
- 2 Video menu bar
- 3 control buttons
- 4 whiteboard
- 5 chat
- 5 chat group
- 6 settings
- 7 participants/lobby
- 8 send receive progress

عرض الملف

@@ -172,10 +172,10 @@
border-radius: '10px';
}
.container {
.init-container {
position: relative;
}
.container video {
.init-container video {
z-index: 0;
position: relative;
height: 240px;

ثنائية
public/images/all.png Normal file

ملف ثنائي غير معروض.

بعد

العرض:  |  الارتفاع:  |  الحجم: 9.3 KiB

ثنائية
public/images/chatgpt.png Normal file

ملف ثنائي غير معروض.

بعد

العرض:  |  الارتفاع:  |  الحجم: 3.0 KiB

عرض الملف

@@ -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.1.4
* @version 1.2.0
*
*/
@@ -203,15 +203,14 @@ function initClient() {
setTippy('chatSpeechStopButton', 'Stop speech recognition', 'top');
setTippy('chatEmojiButton', 'Emoji', 'top');
setTippy('chatMarkdownButton', 'Markdown', 'top');
setTippy('chatGPTButton', 'ChatGPT', 'top');
setTippy('chatShareFileButton', 'Share file', 'top');
setTippy('chatCleanButton', 'Clean', 'bottom');
setTippy('chatSaveButton', 'Save', 'bottom');
setTippy('chatGhostButton', 'Toggle transparent background', 'bottom');
setTippy('chatCloseButton', 'Close', 'bottom');
setTippy('chatTogglePin', 'Toggle pin', 'bottom');
setTippy('chatHideParticipantsList', 'Hide', 'bottom');
setTippy('chatShowParticipantsList', 'Toggle participants list', 'bottom');
setTippy('chatMaxButton', 'Maximize', 'bottom');
setTippy('chatMinButton', 'Minimize', 'bottom');
setTippy('participantsSaveBtn', 'Save participants info', 'bottom');
setTippy('participantsUnreadMessagesBtn', 'Toggle unread messages', 'bottom');
setTippy('transcriptionCloseBtn', 'Close', 'bottom');
setTippy('transcriptionTogglePinBtn', 'Toggle pin', 'bottom');
setTippy('transcriptionMaxBtn', 'Maximize', 'bottom');
@@ -223,8 +222,6 @@ function initClient() {
setTippy('transcriptionCleanBtn', 'Clean', 'bottom');
setTippy('transcriptionSpeechStart', 'Start transcription', 'top');
setTippy('transcriptionSpeechStop', 'Stop transcription', 'top');
setTippy('participantsCloseBtn', 'Close', 'right');
setTippy('participantsSaveBtn', 'Save participants info', 'right');
}
setupWhiteboard();
initEnumerateDevices();
@@ -257,7 +254,6 @@ function refreshMainButtonsToolTipPlacement() {
setTippy('swapCameraButton', 'Swap the camera', placement);
setTippy('chatButton', 'Toggle the chat', placement);
setTippy('transcriptionButton', 'Toggle transcription', placement);
setTippy('participantsButton', 'Toggle participants', placement);
setTippy('whiteboardButton', 'Toggle the whiteboard', placement);
setTippy('settingsButton', 'Toggle the settings', placement);
setTippy('aboutButton', 'About this project', placement);
@@ -692,6 +688,7 @@ function whoAreYou() {
hideClass: { popup: 'animate__animated animate__fadeOutUp' },
inputValidator: (name) => {
if (!name) return 'Please enter your name';
if (name.length > 15) return 'Name must be max 15 char';
name = filterXSS(name);
if (isHtml(name)) return 'Invalid name!';
if (!getCookie(room_id + '_name')) {
@@ -963,13 +960,10 @@ function roomIsReady() {
hide(tabRecordingBtn);
}
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);
BUTTONS.chat.chatGPTButton && show(chatGPTButton);
BUTTONS.chat.chatShareFileButton && show(chatShareFileButton);
isWebkitSpeechRecognitionSupported && BUTTONS.chat.chatSpeechStartButton
? show(chatSpeechStartButton)
@@ -996,7 +990,6 @@ function roomIsReady() {
rc.makeDraggable(emojiPickerContainer, emojiPickerHeader);
rc.makeDraggable(chatRoom, chatHeader);
rc.makeDraggable(mySettings, mySettingsHeader);
rc.makeDraggable(participants, participantsHeader);
rc.makeDraggable(whiteboard, whiteboardHeader);
rc.makeDraggable(sendFileDiv, imgShareSend);
rc.makeDraggable(receiveFileDiv, imgShareReceive);
@@ -1052,11 +1045,11 @@ function elemDisplay(element, display, mode = 'block') {
}
function hide(elem) {
elem.className = 'hidden';
if (!elem.classList.contains('hidden')) elem.classList.toggle('hidden');
}
function show(elem) {
elem.className = '';
if (elem.classList.contains('hidden')) elem.classList.toggle('hidden');
}
function disable(elem, disabled) {
@@ -1188,6 +1181,9 @@ function handleButtons() {
};
chatButton.onclick = () => {
rc.toggleChat();
if (rc.isMobileDevice) {
rc.toggleShowParticipants();
}
};
transcriptionButton.onclick = () => {
transcription.toggle();
@@ -1213,6 +1209,15 @@ function handleButtons() {
transcriptionCleanBtn.onclick = () => {
transcription.delete();
};
chatHideParticipantsList.onclick = (e) => {
rc.toggleShowParticipants();
};
chatShowParticipantsList.onclick = (e) => {
rc.toggleShowParticipants();
};
chatShareRoomBtn.onclick = (e) => {
shareRoom(true);
};
chatGhostButton.onclick = (e) => {
rc.chatToggleBg();
};
@@ -1250,13 +1255,6 @@ function handleButtons() {
isChatMarkdownOn = !isChatMarkdownOn;
setColor(chatMarkdownButton, isChatMarkdownOn ? 'lime' : 'white');
};
chatGPTButton.onclick = () => {
isChatGPTOn = !isChatGPTOn;
setColor(chatGPTButton, isChatGPTOn ? 'lime' : 'white');
};
chatShareFileButton.onclick = () => {
fileShareButton.click();
};
chatSpeechStartButton.onclick = () => {
startSpeech();
};
@@ -1403,15 +1401,15 @@ function handleButtons() {
whiteboardCloseBtn.onclick = () => {
whiteboardAction(getWhiteboardAction('close'));
};
participantsButton.onclick = () => {
getRoomParticipants();
};
participantsCloseBtn.onclick = () => {
toggleParticipants();
};
participantsSaveBtn.onclick = () => {
saveRoomPeers();
};
participantsUnreadMessagesBtn.onclick = () => {
rc.toggleUnreadMsg();
};
searchParticipantsFromList.onkeyup = () => {
rc.searchPeer();
};
lockRoomButton.onclick = () => {
rc.roomAction('lock');
};
@@ -2855,18 +2853,6 @@ function wbDrawing(status) {
// HANDLE PARTICIPANTS
// ####################################################
function toggleParticipants() {
let participants = rc.getId('participants');
participants.classList.toggle('show');
participants.style.top = '50%';
participants.style.left = '50%';
if (DetectRTC.isMobileDevice) {
participants.style.width = '100%';
participants.style.height = '100%';
}
isParticipantsListOpen = !isParticipantsListOpen;
}
async function getRoomPeers() {
let room_info = await rc.getRoomInfo();
return new Map(JSON.parse(room_info.peers));
@@ -2882,133 +2868,207 @@ async function saveRoomPeers() {
}
async function getRoomParticipants(refresh = false) {
let peers = await getRoomPeers();
let table = await getParticipantsTable(peers);
const peers = await getRoomPeers();
const lists = await getParticipantsList(peers);
participantsCount = peers.size;
roomParticipants.innerHTML = table;
participantsList.innerHTML = lists;
refreshParticipantsCount(participantsCount, false);
if (!refresh) {
toggleParticipants();
sound('open');
}
setParticipantsTippy(peers);
}
async function getParticipantsTable(peers) {
let table = `
<div>
<button
id="inviteParticipants"
onclick="shareRoom(true);"
><i class="fas fa-user-plus"></i>&nbsp; Invite Someone</button>
</div>
<div>
<input
id="searchParticipants"
type="text"
placeholder=" 🔍 Search participants ..."
name="search"
maxlength="36"
onkeyup="rc.searchPeer();"
async function getParticipantsList(peers) {
// CHAT-GPT
let li = `
<li
id="ChatGPT"
data-to-id="ChatGPT"
data-to-name="ChatGPT"
class="clearfix"
onclick="rc.showPeerAboutAndMessages(this.id, 'ChatGPT', event)"
>
<img
src="${image.chatgpt}"
alt="avatar"
/>
<div class="about">
<div class="name">ChatGPT</div>
<div class="status"><i class="fa fa-circle online"></i> online</div>
</div>
<table id="myTable">
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>`;
</li>
`;
// ALL
li += `
<li id="all"
data-to-id="all"
data-to-name="all"
class="clearfix active"
onclick="rc.showPeerAboutAndMessages(this.id, 'all', event)"
>
<img
src="${image.all}"
alt="avatar"
/>
<div class="about">
<div class="name">Public chat</div>
<div class="status"> <i class="fa fa-circle online"></i> online ${participantsCount}</div>
</div>`;
// ONLY PRESENTER CAN EXECUTE THIS CMD
if (!isRulesActive || isPresenter) {
table += `
<tr>
<td>&nbsp;<i class="fas fa-users fa-lg"></i></td>
<td>all</td>
<td><button id="muteAllButton" onclick="rc.peerAction('me','${socket.id}','mute',true,true)">${_PEER.audioOff}</button></td>
<td><button id="hideAllButton" onclick="rc.peerAction('me','${socket.id}','hide',true,true)">${_PEER.videoOff}</button></td>
<td><button id="stopAllButton" onclick="rc.peerAction('me','${socket.id}','stop',true,true)">${_PEER.screenOff}</button></td>
<td></td>
<td><button id="sendAllButton" onclick="rc.selectFileToShare('${socket.id}', true)">${_PEER.sendFile}</button></td>
<td><button id="sendMessageToAll" onclick="rc.sendMessageTo('all','all')">${_PEER.sendMsg}</button></td>
<td><button id="sendVideoToAll" onclick="rc.shareVideo('all');">${_PEER.sendVideo}</button></td>
<td><button id="ejectAllButton" onclick="rc.peerAction('me','${socket.id}','eject',true,true)">${_PEER.ejectPeer}</button></td>
</tr>
`;
li += `
<div style="class="dropdown">
<button
class="dropdown-toggle"
type="button"
id="${socket.id}-chatDropDownMenu"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<!-- <i class="fas fa-bars"></i> -->
</button>
<ul class="dropdown-menu" aria-labelledby="${socket.id}-chatDropDownMenu">
<li><button class="btn-sm ml5" id="sendAllButton" onclick="rc.selectFileToShare('${socket.id}', true)">${_PEER.sendFile} Share file to all</button></li>
<li><button class="btn-sm ml5" id="sendVideoToAll" onclick="rc.shareVideo('all');">${_PEER.sendVideo} Share audio/video to all</button></li>
<li><button class="btn-sm ml5" id="ejectAllButton" onclick="rc.peerAction('me','${socket.id}','eject',true,true)">${_PEER.ejectPeer} Eject all participants</button></li>
</ul>
</div>
<br/>
<div class="about-buttons mt5">
<button class="ml5" id="muteAllButton" onclick="rc.peerAction('me','${socket.id}','mute',true,true)">${_PEER.audioOff}</button>
<button class="ml5" id="hideAllButton" onclick="rc.peerAction('me','${socket.id}','hide',true,true)">${_PEER.videoOff}</button>
<button class="ml5" id="stopAllButton" onclick="rc.peerAction('me','${socket.id}','stop',true,true)">${_PEER.screenOff}</button>
</div>`;
}
for (let peer of Array.from(peers.keys())) {
let peer_info = peers.get(peer).peer_info;
let peer_name = peer_info.peer_name;
let peer_audio = peer_info.peer_audio ? _PEER.audioOn : _PEER.audioOff;
let peer_video = peer_info.peer_video ? _PEER.videoOn : _PEER.videoOff;
let peer_screen = peer_info.peer_screen ? _PEER.screenOn : _PEER.screenOff;
let peer_hand = peer_info.peer_hand ? _PEER.raiseHand : _PEER.lowerHand;
let peer_eject = _PEER.ejectPeer;
let peer_sendFile = _PEER.sendFile;
let peer_sendMsg = _PEER.sendMsg;
let peer_id = peer_info.peer_id;
let avatarImg = getParticipantAvatar(peer_name);
if (socket.id === peer_id) {
table += `
<tr id='${peer_name}'>
<td><img src="${avatarImg}"></td>
<td>${peer_name} (me)</td>
<td><button>${peer_audio}</button></td>
<td><button>${peer_video}</button></td>
<td><button>${peer_screen}</button></td>
<td><button>${peer_hand}</button></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
li += `
</li>
`;
} else {
// PEERS IN THE CURRENT ROOM
for (const peer of Array.from(peers.keys())) {
const peer_info = peers.get(peer).peer_info;
const peer_name = peer_info.peer_name;
const peer_audio = peer_info.peer_audio ? _PEER.audioOn : _PEER.audioOff;
const peer_video = peer_info.peer_video ? _PEER.videoOn : _PEER.videoOff;
const peer_screen = peer_info.peer_screen ? _PEER.screenOn : _PEER.screenOff;
const peer_hand = peer_info.peer_hand ? _PEER.raiseHand : _PEER.lowerHand;
const peer_eject = _PEER.ejectPeer;
const peer_sendFile = _PEER.sendFile;
const peer_id = peer_info.peer_id;
const avatarImg = getParticipantAvatar(peer_name);
// NOT ME
if (socket.id !== peer_id) {
// PRESENTER HAS MORE OPTIONS
if (isRulesActive && isPresenter) {
table += `
<tr id='${peer_id}'>
<td><img src="${avatarImg}"></td>
<td>${peer_name}</td>
<td><button id='${peer_id}___pAudio' onclick="rc.peerAction('me',this.id,'mute')">${peer_audio}</button></td>
<td><button id='${peer_id}___pVideo' onclick="rc.peerAction('me',this.id,'hide')">${peer_video}</button></td>
<td><button id='${peer_id}___pScreen' onclick="rc.peerAction('me',this.id,'stop')">${peer_screen}</button></td>
<td><button>${peer_hand}</button></td>
<td><button id='${peer_id}___shareFile' onclick="rc.selectFileToShare('${peer_id}', false)">${peer_sendFile}</button></td>
<td><button id="${peer_id}___sendMessageTo" onclick="rc.sendMessageTo('${peer_id}','${peer_name}')">${peer_sendMsg}</button></td>
<td><button id="${peer_id}___sendVideoTo" onclick="rc.shareVideo('${peer_id}');">${_PEER.sendVideo}</button></td>
<td><button id='${peer_id}___pEject' onclick="rc.peerAction('me',this.id,'eject')">${peer_eject}</button></td>
</tr>
li += `
<li
id='${peer_id}'
data-to-id="${peer_id}"
data-to-name="${peer_name}"
class="clearfix"
onclick="rc.showPeerAboutAndMessages(this.id, '${peer_name}', event)"
>
<img
src="${avatarImg}"
alt="avatar"
/>
<div class="about">
<div class="name">${peer_name}</div>
<div class="status"> <i class="fa fa-circle online"></i> online <i id="${peer_id}-unread-msg" class="fas fa-comments hidden"></i> </div>
</div>
<div style="class="dropdown">
<button
class="dropdown-toggle"
type="button"
id="${peer_id}-chatDropDownMenu"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<!-- <i class="fas fa-bars"></i> -->
</button>
<ul class="dropdown-menu" aria-labelledby="${peer_id}-chatDropDownMenu">
<li><button class="btn-sm ml5" id='${peer_id}___shareFile' onclick="rc.selectFileToShare('${peer_id}', false)">${peer_sendFile} Share file</button></li>
<li><button class="btn-sm ml5" id="${peer_id}___sendVideoTo" onclick="rc.shareVideo('${peer_id}');">${_PEER.sendVideo} Share audio/video</button></li>
<li><button class="btn-sm ml5" id='${peer_id}___pEject' onclick="rc.peerAction('me',this.id,'eject')">${peer_eject} Eject participant</button></li>
</ul>
</div>
<br/>
<div class="about-buttons mt5">
<button class="ml5" id='${peer_id}___pAudio' onclick="rc.peerAction('me',this.id,'mute')">${peer_audio}</button>
<button class="ml5" id='${peer_id}___pVideo' onclick="rc.peerAction('me',this.id,'hide')">${peer_video}</button>
<button class="ml5" id='${peer_id}___pScreen' onclick="rc.peerAction('me',this.id,'stop')">${peer_screen}</button>
`;
if (peer_info.peer_hand) {
li += `
<button class="ml5" >${peer_hand}</button>`;
}
li += `
</div>
</li>
`;
} else {
table += `
<tr id='${peer_id}'>
<td><img src="${avatarImg}"></td>
<td>${peer_name}</td>
<td><button id='${peer_id}___pAudio'>${peer_audio}</button></td>
<td><button id='${peer_id}___pVideo'>${peer_video}</button></td>
<td><button id='${peer_id}___pScreen'>${peer_screen}</button></td>
<td><button>${peer_hand}</button></td>
<td><button id='${peer_id}___shareFile' onclick="rc.selectFileToShare('${peer_id}', false)">${peer_sendFile}</button></td>
<td><button id="${peer_id}___sendMessageTo" onclick="rc.sendMessageTo('${peer_id}','${peer_name}')">${peer_sendMsg}</button></td>
<td><button id="${peer_id}___sendVideoTo" onclick="rc.shareVideo('${peer_id}');">${_PEER.sendVideo}</button></td>
<td></td>
</tr>
// GUEST USER
li += `
<li
id='${peer_id}'
data-to-id="${peer_id}"
data-to-name="${peer_name}"
class="clearfix"
onclick="rc.showPeerAboutAndMessages(this.id, '${peer_name}', event)"
>
<img
src="${avatarImg}"
alt="avatar"
/>
<div class="about">
<div class="name">${peer_name}</div>
<div class="status"> <i class="fa fa-circle online"></i> online <i id="${peer_id}-unread-msg" class="fas fa-comments hidden"></i> </div>
</div>
<div style="class="dropdown">
<button
class="dropdown-toggle"
type="button"
id="${peer_id}-chatDropDownMenu"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<!-- <i class="fas fa-bars"></i> -->
</button>
<ul class="dropdown-menu" aria-labelledby="${peer_id}-chatDropDownMenu">
<li><button class="btn-sm ml5" id='${peer_id}___shareFile' onclick="rc.selectFileToShare('${peer_id}', false)">${peer_sendFile} Share file</button></li>
<li><button class="btn-sm ml5" id="${peer_id}___sendVideoTo" onclick="rc.shareVideo('${peer_id}');">${_PEER.sendVideo} Share Audio/Video</button></li>
</ul>
</div>
<br/>
<div class="about-buttons mt5">
<button class="ml5" id='${peer_id}___pAudio'>${peer_audio}</button>
<button class="ml5" id='${peer_id}___pVideo'>${peer_video}</button>
<button class="ml5" id='${peer_id}___pScreen'>${peer_screen}</button>
`;
if (peer_info.peer_hand) {
li += `
<button class="ml5" >${peer_hand}</button>`;
}
li += `
</div>
</li>
`;
}
}
}
table += `</table>`;
return table;
return li;
}
function setParticipantsTippy(peers) {
@@ -3017,27 +3077,18 @@ function setParticipantsTippy(peers) {
setTippy('muteAllButton', 'Mute all participants', 'top');
setTippy('hideAllButton', 'Hide all participants', 'top');
setTippy('stopAllButton', 'Stop screen share to all participants', 'top');
setTippy('sendAllButton', 'Share file to all', 'top');
setTippy('sendMessageToAll', 'Send message to all', 'top');
setTippy('sendVideoToAll', 'Share video to all', 'top');
setTippy('ejectAllButton', 'Eject all participants', 'top');
//
for (let peer of Array.from(peers.keys())) {
let peer_info = peers.get(peer).peer_info;
let peer_id = peer_info.peer_id;
const peer_info = peers.get(peer).peer_info;
const peer_id = peer_info.peer_id;
setTippy(peer_id + '___pAudio', 'Mute', 'top');
setTippy(peer_id + '___pVideo', 'Hide', 'top');
setTippy(peer_id + '___pScreen', 'Stop', 'top');
setTippy(peer_id + '___shareFile', 'Share file', 'top');
setTippy(peer_id + '___sendMessageTo', 'Send private message', 'top');
setTippy(peer_id + '___sendVideoTo', 'Share video', 'top');
setTippy(peer_id + '___pEject', 'Eject', 'top');
}
}
}
function refreshParticipantsCount(count, adapt = true) {
participantsTitle.innerHTML = `<i class="fas fa-users"></i> Participants ( ${count} )`;
if (adapt) adaptAspectRatio(count);
}
@@ -3058,8 +3109,8 @@ function setTheme() {
document.documentElement.style.setProperty('--body-bg', 'radial-gradient(#393939, #000000)');
document.documentElement.style.setProperty('--transcription-bg', 'radial-gradient(#393939, #000000)');
document.documentElement.style.setProperty('--msger-bg', 'radial-gradient(#393939, #000000)');
document.documentElement.style.setProperty('--left-msg-bg', '#252d31');
document.documentElement.style.setProperty('--right-msg-bg', '#056162');
document.documentElement.style.setProperty('--left-msg-bg', '#056162');
document.documentElement.style.setProperty('--right-msg-bg', '#252d31');
document.documentElement.style.setProperty('--select-bg', '#2c2c2c');
document.documentElement.style.setProperty('--tab-btn-active', '#393939');
document.documentElement.style.setProperty('--settings-bg', 'radial-gradient(#393939, #000000)');
@@ -3073,8 +3124,8 @@ function setTheme() {
document.documentElement.style.setProperty('--body-bg', 'radial-gradient(#666, #333)');
document.documentElement.style.setProperty('--transcription-bg', 'radial-gradient(#666, #333)');
document.documentElement.style.setProperty('--msger-bg', 'radial-gradient(#666, #333)');
document.documentElement.style.setProperty('--left-msg-bg', '#252d31');
document.documentElement.style.setProperty('--right-msg-bg', '#056162');
document.documentElement.style.setProperty('--left-msg-bg', '#056162');
document.documentElement.style.setProperty('--right-msg-bg', '#252d31');
document.documentElement.style.setProperty('--select-bg', '#2c2c2c');
document.documentElement.style.setProperty('--tab-btn-active', '#666');
document.documentElement.style.setProperty('--settings-bg', 'radial-gradient(#666, #333)');
@@ -3088,8 +3139,8 @@ function setTheme() {
document.documentElement.style.setProperty('--body-bg', 'radial-gradient(#003934, #001E1A)');
document.documentElement.style.setProperty('--transcription-bg', 'radial-gradient(#003934, #001E1A)');
document.documentElement.style.setProperty('--msger-bg', 'radial-gradient(#003934, #001E1A)');
document.documentElement.style.setProperty('--left-msg-bg', '#003934');
document.documentElement.style.setProperty('--right-msg-bg', '#001E1A');
document.documentElement.style.setProperty('--left-msg-bg', '#001E1A');
document.documentElement.style.setProperty('--right-msg-bg', '#003934');
document.documentElement.style.setProperty('--select-bg', '#001E1A');
document.documentElement.style.setProperty('--tab-btn-active', '#003934');
document.documentElement.style.setProperty('--settings-bg', 'radial-gradient(#003934, #001E1A)');
@@ -3103,8 +3154,8 @@ function setTheme() {
document.documentElement.style.setProperty('--body-bg', 'radial-gradient(#306bac, #141B41)');
document.documentElement.style.setProperty('--transcription-bg', 'radial-gradient(#306bac, #141B41)');
document.documentElement.style.setProperty('--msger-bg', 'radial-gradient(#306bac, #141B41)');
document.documentElement.style.setProperty('--left-msg-bg', '#306bac');
document.documentElement.style.setProperty('--right-msg-bg', '#141B41');
document.documentElement.style.setProperty('--left-msg-bg', '#141B41');
document.documentElement.style.setProperty('--right-msg-bg', '#306bac');
document.documentElement.style.setProperty('--select-bg', '#141B41');
document.documentElement.style.setProperty('--tab-btn-active', '#306bac');
document.documentElement.style.setProperty('--settings-bg', 'radial-gradient(#306bac, #141B41)');
@@ -3118,8 +3169,8 @@ function setTheme() {
document.documentElement.style.setProperty('--body-bg', 'radial-gradient(#69140E, #3C1518)');
document.documentElement.style.setProperty('--transcription-bg', 'radial-gradient(#69140E, #3C1518)');
document.documentElement.style.setProperty('--msger-bg', 'radial-gradient(#69140E, #3C1518)');
document.documentElement.style.setProperty('--left-msg-bg', '#69140E');
document.documentElement.style.setProperty('--right-msg-bg', '#3C1518');
document.documentElement.style.setProperty('--left-msg-bg', '#3C1518');
document.documentElement.style.setProperty('--right-msg-bg', '#69140E');
document.documentElement.style.setProperty('--select-bg', '#3C1518');
document.documentElement.style.setProperty('--tab-btn-active', '#69140E');
document.documentElement.style.setProperty('--settings-bg', 'radial-gradient(#69140E, #3C1518)');

عرض الملف

@@ -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.1.4
* @version 1.2.0
*
*/
@@ -80,6 +80,8 @@ const image = {
feedback: '../images/feedback.png',
lobby: '../images/lobby.png',
email: '../images/email.png',
chatgpt: '../images/chatgpt.png',
all: '../images/all.png',
};
const mediaType = {
@@ -183,6 +185,8 @@ class RoomClient {
this.isChatBgTransparent = false;
this.isVideoPinned = false;
this.isChatPinned = false;
this.isChatMaximized = false;
this.isToggleUnreadMsg = false;
this.pinnedVideoPlayerId = null;
this.camVideo = false;
this.camera = 'user';
@@ -2919,22 +2923,43 @@ class RoomClient {
}
}
toggleChat() {
let chatRoom = this.getId('chatRoom');
if (this.isChatOpen == false) {
chatRoom.style.display = 'block';
async toggleChat() {
const chatRoom = this.getId('chatRoom');
chatRoom.classList.toggle('show');
if (!this.isChatOpen) {
await getRoomParticipants(true);
hide(chatMinButton);
if (!this.isMobileDevice) {
show(chatMaxButton);
}
this.chatCenter();
this.sound('open');
this.isChatOpen = true;
} else {
chatRoom.style.display = 'none';
this.isChatOpen = false;
this.showPeerAboutAndMessages('all', 'all');
}
isParticipantsListOpen = !isParticipantsListOpen;
this.isChatOpen = !this.isChatOpen;
if (this.isChatPinned) this.chatUnpin();
}
toggleShowParticipants() {
const plist = this.getId('plist');
const chat = this.getId('chat');
const chatHistory = this.getId('chatHistory');
plist.classList.toggle('hidden');
const isParticipantsListHidden = plist.classList.contains('hidden');
chat.style.marginLeft = isParticipantsListHidden ? 0 : '300px';
chat.style.borderLeft = isParticipantsListHidden ? 'none' : '1px solid rgb(255 255 255 / 32%)';
chatHistory.style.height = isParticipantsListHidden ? 'calc(100vh - 220px)' : '500px';
this.toggleChatHistorySize(isParticipantsListHidden && (this.isChatPinned || this.isChatMaximized));
plist.style.width = this.isChatPinned || this.isMobileDevice ? '100%' : '300px';
}
toggleChatHistorySize(max = true) {
const chatHistory = this.getId('chatHistory');
chatHistory.style.minHeight = max ? 'calc(100vh - 220px)' : '500px';
chatHistory.style.maxHeight = max ? 'calc(100vh - 220px)' : '500px';
}
toggleChatPin() {
if (transcription.isPin()) {
return userLog('info', 'Please unpin the transcription that appears to be currently pinned', 'top-end');
@@ -2944,22 +2969,26 @@ class RoomClient {
}
chatMaximize() {
this.isChatMaximized = true;
hide(chatMaxButton);
show(chatMinButton);
this.chatCenter();
document.documentElement.style.setProperty('--msger-width', '100%');
document.documentElement.style.setProperty('--msger-height', '100%');
this.toggleChatHistorySize(true);
}
chatMinimize() {
this.isChatMaximized = false;
hide(chatMinButton);
show(chatMaxButton);
if (this.isChatPinned) {
this.chatPin();
} else {
this.chatCenter();
document.documentElement.style.setProperty('--msger-width', '420px');
document.documentElement.style.setProperty('--msger-height', '680px');
document.documentElement.style.setProperty('--msger-width', '800px');
document.documentElement.style.setProperty('--msger-height', '700px');
this.toggleChatHistorySize(false);
}
}
@@ -2975,6 +3004,8 @@ class RoomClient {
resizeVideoMedia();
chatRoom.style.resize = 'none';
if (!this.isMobileDevice) this.makeUnDraggable(chatRoom, chatHeader);
if (!plist.classList.contains('hidden')) this.toggleShowParticipants();
if (chatRoom.classList.contains('container')) chatRoom.classList.remove('container');
}
chatUnpin() {
@@ -2984,16 +3015,17 @@ class RoomClient {
this.videoMediaContainer.style.width = '100%';
this.videoMediaContainer.style.height = '100%';
}
document.documentElement.style.setProperty('--msger-width', '420px');
document.documentElement.style.setProperty('--msger-height', '680px');
document.documentElement.style.setProperty('--msger-width', '800px');
document.documentElement.style.setProperty('--msger-height', '700px');
hide(chatMinButton);
show(chatMaxButton);
this.chatCenter();
this.isChatPinned = false;
setColor(chatTogglePin, 'white');
resizeVideoMedia();
chatRoom.style.resize = 'both';
if (!this.isMobileDevice) this.makeDraggable(chatRoom, chatHeader);
if (plist.classList.contains('hidden')) this.toggleShowParticipants();
if (!chatRoom.classList.contains('container')) chatRoom.classList.add('container');
}
chatCenter() {
@@ -3026,7 +3058,7 @@ class RoomClient {
cleanMessage() {
chatMessage.value = '';
chatMessage.style.height = '43px';
chatMessage.setAttribute('rows', '1');
}
pasteMessage() {
@@ -3049,12 +3081,35 @@ class RoomClient {
return this.userLog('info', 'No participants in the room', 'top-end');
}
chatMessage.value = filterXSS(chatMessage.value.trim());
let peer_msg = this.formatMsg(chatMessage.value);
const peer_msg = this.formatMsg(chatMessage.value);
if (!peer_msg) {
return this.cleanMessage();
}
this.peer_name = filterXSS(this.peer_name);
const data = {
peer_name: this.peer_name,
peer_id: this.peer_id,
to_peer_id: 'ChatGPT',
to_peer_name: 'ChatGPT',
peer_msg: peer_msg,
};
if (isChatGPTOn) {
console.log('Send message:', data);
this.socket.emit('message', data);
this.setMsgAvatar('left', this.peer_name);
this.appendMessage(
'left',
this.leftMsgAvatar,
this.peer_name,
this.peer_id,
peer_msg,
data.to_peer_id,
data.to_peer_name,
);
this.cleanMessage();
this.socket
.request('getChatGPT', {
time: getDataTimeString(),
@@ -3066,15 +3121,15 @@ class RoomClient {
function (completion) {
if (!completion) return;
console.log('Receive message:', completion);
this.setMsgAvatar('left', 'ChatGPT');
this.setMsgAvatar('right', 'ChatGPT');
this.appendMessage(
'left',
this.leftMsgAvatar,
'right',
image.chatgpt,
'ChatGPT',
this.peer_id,
completion,
this.peer_id,
this.peer_name,
'ChatGPT',
'ChatGPT',
);
this.cleanMessage();
this.speechInMessages ? this.speechMessage(true, 'ChatGPT', completion) : this.sound('message');
@@ -3084,19 +3139,30 @@ class RoomClient {
console.log('ChatGPT error:', err);
});
} else {
let data = {
peer_name: this.peer_name,
peer_id: this.peer_id,
to_peer_id: 'all',
peer_msg: peer_msg,
};
const participantsList = this.getId('participantsList');
const participantsListItems = participantsList.getElementsByTagName('li');
for (let i = 0; i < participantsListItems.length; i++) {
const li = participantsListItems[i];
if (li.classList.contains('active')) {
data.to_peer_id = li.getAttribute('data-to-id');
data.to_peer_name = li.getAttribute('data-to-name');
console.log('Send message:', data);
this.socket.emit('message', data);
}
this.setMsgAvatar('right', this.peer_name);
this.appendMessage('right', this.rightMsgAvatar, this.peer_name, this.peer_id, peer_msg, 'all', 'all');
this.setMsgAvatar('left', this.peer_name);
this.appendMessage(
'left',
this.leftMsgAvatar,
this.peer_name,
this.peer_id,
peer_msg,
data.to_peer_id,
data.to_peer_name,
);
this.cleanMessage();
}
}
}
}
sendMessageTo(to_peer_id, to_peer_name) {
if (!this.thereAreParticipants()) {
@@ -3132,10 +3198,10 @@ class RoomClient {
};
console.log('Send message:', data);
this.socket.emit('message', data);
this.setMsgAvatar('right', this.peer_name);
this.setMsgAvatar('left', this.peer_name);
this.appendMessage(
'right',
this.rightMsgAvatar,
'left',
this.leftMsgAvatar,
this.peer_name,
this.peer_id,
peer_msg,
@@ -3147,12 +3213,12 @@ class RoomClient {
});
}
showMessage(data) {
if (!this.isChatOpen && this.showChatOnMessage) this.toggleChat();
this.setMsgAvatar('left', data.peer_name);
async showMessage(data) {
if (!this.isChatOpen && this.showChatOnMessage) await this.toggleChat();
this.setMsgAvatar('right', data.peer_name);
this.appendMessage(
'left',
this.leftMsgAvatar,
'right',
this.rightMsgAvatar,
data.peer_name,
data.peer_id,
data.peer_msg,
@@ -3163,6 +3229,19 @@ class RoomClient {
this.userLog('info', `💬 New message from: ${data.peer_name}`, 'top-end');
}
this.speechInMessages ? this.speechMessage(true, data.peer_name, data.peer_msg) : this.sound('message');
const participantsList = this.getId('participantsList');
const participantsListItems = participantsList.getElementsByTagName('li');
for (let i = 0; i < participantsListItems.length; i++) {
const li = participantsListItems[i];
// INCOMING PRIVATE MESSAGE
if (li.id === data.peer_id && data.to_peer_id != 'all') {
li.classList.add('pulsate');
if (!['all', 'ChatGPT'].includes(data.to_peer_id)) {
this.getId(`${data.peer_id}-unread-msg`).classList.remove('hidden');
}
}
}
}
setMsgAvatar(avatar, peerName) {
@@ -3181,61 +3260,81 @@ class RoomClient {
const getToName = filterXSS(toName);
const time = this.getTimeNow();
const msgBubble = getToId == 'all' ? 'msg-bubble' : 'msg-bubble-private';
const replyMsg = getFromId === this.peer_id ? `<hr/>Private message to ${getToName}` : '';
const message = getToId == 'all' ? getMsg : getMsg + replyMsg;
const myMessage = getSide === 'left';
const messageClass = myMessage ? 'my-message' : 'other-message float-right';
const messageData = myMessage ? 'text-start' : 'text-end';
const timeAndName = myMessage
? `<span class="message-data-time">${time}, ${getFromName} ( me ) </span>`
: `<span class="message-data-time">${time}, ${getFromName} </span>`;
let msgHTML = `
<div id="msg-${chatMessagesId}" class="msg ${getSide}-msg">
<img class="msg-img" src="${getImg}" />
<div class=${msgBubble}>
<div class="msg-info">
<div class="msg-info-name">${getFromName}</div>
<div class="msg-info-time">${time}</div>
const speechButton = this.isSpeechSynthesisSupported
? `<button
id="msg-speech-${chatMessagesId}"
class="mr5"
onclick="rc.speechMessage(false, '${getFromName}', '${this.formatMsg(getMsg)}')">
<i class="fas fa-volume-high"></i>
</button>`
: '';
const positionFirst = myMessage
? `<img src="${getImg}" alt="avatar" />${timeAndName}`
: `${timeAndName}<img src="${getImg}" alt="avatar" />`;
const newMessageHTML = `
<li id="msg-${chatMessagesId}"
data-from-id="${getFromId}"
data-from-name="${getFromName}"
data-to-id="${getToId}"
data-to-name="${getToName}"
class="clearfix"
>
<div class="message-data ${messageData}">
${positionFirst}
</div>
<div id="${chatMessagesId}" class="msg-text">${message}
<hr/>`;
// add btn direct reply to private message
if (getFromId != this.peer_id) {
msgHTML += `
<button
class="fas fa-paper-plane"
id="msg-private-reply-${chatMessagesId}"
onclick="rc.sendMessageTo('${getFromId}','${getFromName}')"
></button>`;
}
msgHTML += `
<div class="message ${messageClass}">
<span class="text-start " id="${chatMessagesId}">${getMsg}</span>
<hr/>
<div class="about-buttons mt5">
<button
id="msg-delete-${chatMessagesId}"
class="fas fa-trash"
onclick="rc.deleteMessage('msg-${chatMessagesId}')"
></button>
class="mr5"
onclick="rc.deleteMessage('msg-${chatMessagesId}')">
<i class="fas fa-trash"></i>
</button>
<button
id="msg-copy-${chatMessagesId}"
class="fas fa-copy"
onclick="rc.copyToClipboard('${chatMessagesId}')"
></button>`;
if (this.isSpeechSynthesisSupported) {
msgHTML += `
<button
id="msg-speech-${chatMessagesId}"
class="fas fa-volume-high"
onclick="rc.speechMessage(false, '${getFromName}', '${this.formatMsg(getMsg)}')"
></button>
`;
}
msgHTML += `
</div>
</div>
</div>
class="mr5"
onclick="rc.copyToClipboard('${chatMessagesId}')">
<i class="fas fa-paste"></i>
</button>
${speechButton}
</div>
</div>
</li>
`;
this.collectMessages(time, getFromName, getMsg);
chatMsger.insertAdjacentHTML('beforeend', msgHTML);
chatMsger.scrollTop += 500;
console.log('Append message to:', { to_id: getToId, to_name: getToName });
switch (getToId) {
case 'ChatGPT':
chatGPTMessages.insertAdjacentHTML('beforeend', newMessageHTML);
break;
case 'all':
chatPublicMessages.insertAdjacentHTML('beforeend', newMessageHTML);
break;
default:
chatPrivateMessages.insertAdjacentHTML('beforeend', newMessageHTML);
break;
}
chatHistory.scrollTop += 500;
this.setTippy('msg-delete-' + chatMessagesId, 'Delete', 'top');
this.setTippy('msg-copy-' + chatMessagesId, 'Copy', 'top');
this.setTippy('msg-speech-' + chatMessagesId, 'Speech', 'top');
this.setTippy('msg-private-reply-' + chatMessagesId, 'Reply', 'top');
chatMessagesId++;
}
@@ -3389,7 +3488,7 @@ class RoomClient {
checkLineBreaks() {
chatMessage.style.height = '';
if (this.getLineBreaks(chatMessage.value) > 0 || chatMessage.value.length > 50) {
chatMessage.style.height = '200px';
chatMessage.setAttribute('rows', '2');
}
}
@@ -3422,7 +3521,7 @@ class RoomClient {
Swal.fire({
background: swalBackground,
position: 'center',
title: 'Clean up chat Messages?',
title: 'Clean up all chat Messages?',
imageUrl: image.delete,
showDenyButton: true,
confirmButtonText: `Yes`,
@@ -3431,11 +3530,15 @@ class RoomClient {
hideClass: { popup: 'animate__animated animate__fadeOutUp' },
}).then((result) => {
if (result.isConfirmed) {
let msgs = chatMsger.firstChild;
while (msgs) {
chatMsger.removeChild(msgs);
msgs = chatMsger.firstChild;
function removeAllChildNodes(parentNode) {
while (parentNode.firstChild) {
parentNode.removeChild(parentNode.firstChild);
}
}
// Remove child nodes from different message containers
removeAllChildNodes(chatGPTMessages);
removeAllChildNodes(chatPublicMessages);
removeAllChildNodes(chatPrivateMessages);
this.chatMessages = [];
this.sound('delete');
}
@@ -3870,10 +3973,10 @@ class RoomClient {
fileSize: this.fileToSend.size,
fileType: this.fileToSend.type,
};
this.setMsgAvatar('right', this.peer_name);
this.setMsgAvatar('left', this.peer_name);
this.appendMessage(
'right',
this.rightMsgAvatar,
'left',
this.leftMsgAvatar,
this.peer_name,
this.peer_id,
`${icons.fileSend} File send:
@@ -3912,10 +4015,10 @@ class RoomClient {
html.newline +
' File size: ' +
this.bytesToSize(this.incomingFileInfo.fileSize);
this.setMsgAvatar('left', this.incomingFileInfo.peer_name);
this.setMsgAvatar('right', this.incomingFileInfo.peer_name);
this.appendMessage(
'left',
this.leftMsgAvatar,
'right',
this.rightMsgAvatar,
this.incomingFileInfo.peer_name,
this.incomingFileInfo.peer_id,
`${icons.fileReceive} File receive:
@@ -5145,22 +5248,123 @@ class RoomClient {
// ####################################################
searchPeer() {
let input, filter, table, tr, td, i, txtValue;
input = this.getId('searchParticipants');
filter = input.value.toUpperCase();
table = this.getId('myTable');
tr = table.getElementsByTagName('tr');
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName('td')[1];
if (td) {
txtValue = td.textContent || td.innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = '';
} else {
tr[i].style.display = 'none';
const searchParticipantsFromList = this.getId('searchParticipantsFromList');
const searchFilter = searchParticipantsFromList.value.toUpperCase();
const participantsList = this.getId('participantsList');
const participantsListItems = participantsList.getElementsByTagName('li');
for (let i = 0; i < participantsListItems.length; i++) {
const li = participantsListItems[i];
const participantName = li.getAttribute('data-to-name').toUpperCase();
const shouldDisplay = participantName.includes(searchFilter);
li.style.display = shouldDisplay ? '' : 'none';
}
}
// ####################################################
// FILTER PEER WITH UNREAD MESSAGES
// ####################################################
toggleUnreadMsg() {
const participantsList = this.getId('participantsList');
const participantsListItems = participantsList.getElementsByTagName('li');
for (let i = 0; i < participantsListItems.length; i++) {
const li = participantsListItems[i];
const shouldDisplay =
(li.classList.contains('pulsate') && !this.isToggleUnreadMsg) || this.isToggleUnreadMsg;
li.style.display = shouldDisplay ? '' : 'none';
}
this.isToggleUnreadMsg = !this.isToggleUnreadMsg;
setColor(participantsUnreadMessagesBtn, this.isToggleUnreadMsg ? 'lime' : 'white');
}
// ####################################################
// SHOW PEER ABOUT AND MESSAGES
// ####################################################
showPeerAboutAndMessages(peer_id, peer_name, event = null) {
this.hidePeerMessages();
const chatAbout = this.getId('chatAbout');
const participant = this.getId(peer_id);
const participantsList = this.getId('participantsList');
const chatPrivateMessages = this.getId('chatPrivateMessages');
const messagePrivateListItems = chatPrivateMessages.getElementsByTagName('li');
const participantsListItems = participantsList.getElementsByTagName('li');
const avatarImg = getParticipantAvatar(peer_name);
const generateChatAboutHTML = (imgSrc, title, status = 'online', participants = '') => {
return `
<img
class="all-participants-img"
style="border: var(--border); width: 43px; margin-right: 5px; cursor: pointer;"
id="chatShowParticipantsList"
src="${image.users}"
alt="participants"
onclick="rc.toggleShowParticipants()"
/>
<a data-toggle="modal" data-target="#view_info">
<img src="${imgSrc}" alt="avatar" />
</a>
<div class="chat-about">
<h6 class="mb-0">${title}</h6>
<span class="status"> <i class="fa fa-circle ${status}"></i> ${status} ${participants}</span>
</div>
`;
};
// CURRENT SELECTED PEER
for (let i = 0; i < participantsListItems.length; i++) {
participantsListItems[i].classList.remove('active');
participantsListItems[i].classList.remove('pulsate'); // private new message to read
if (!['all', 'ChatGPT'].includes(peer_id)) {
// icon private new message to read
this.getId(`${peer_id}-unread-msg`).classList.add('hidden');
}
}
participant.classList.add('active');
isChatGPTOn = false;
console.log('Display messages', peer_id);
switch (peer_id) {
case 'ChatGPT':
isChatGPTOn = true;
chatAbout.innerHTML = generateChatAboutHTML(image.chatgpt, 'ChatGPT');
this.getId('chatGPTMessages').style.display = 'block';
break;
case 'all':
chatAbout.innerHTML = generateChatAboutHTML(image.all, 'Public chat', 'online', participantsCount);
this.getId('chatPublicMessages').style.display = 'block';
break;
default:
chatAbout.innerHTML = generateChatAboutHTML(avatarImg, peer_name);
chatPrivateMessages.style.display = 'block';
for (let i = 0; i < messagePrivateListItems.length; i++) {
const li = messagePrivateListItems[i];
const itemFromId = li.getAttribute('data-from-id');
const itemToId = li.getAttribute('data-to-id');
const shouldDisplay = itemFromId.includes(peer_id) || itemToId.includes(peer_id);
li.style.display = shouldDisplay ? '' : 'none';
}
break;
}
setTippy('chatShowParticipantsList', 'Toggle participants list', 'bottom');
const clickedElement = event ? event.target : null;
if (!event || (clickedElement.tagName === 'LI' || clickedElement.tagName === 'IMG')) {
if ((this.isMobileDevice || this.isChatPinned) && (!plist || !plist.classList.contains('hidden'))) {
this.toggleShowParticipants();
}
}
}
hidePeerMessages() {
elemDisplay('chatGPTMessages', false);
elemDisplay('chatPublicMessages', false);
elemDisplay('chatPrivateMessages', false);
}
// ####################################################

عرض الملف

@@ -18,7 +18,6 @@ const BUTTONS = {
swapCameraButton: true,
chatButton: true,
transcriptionButton: true,
participantsButton: true,
whiteboardButton: true,
emojiRoomButton: true,
settingsButton: true,
@@ -67,8 +66,6 @@ const BUTTONS = {
chatSaveButton: true,
chatEmojiButton: true,
chatMarkdownButton: true,
chatGPTButton: true,
chatShareFileButton: true,
chatSpeechStartButton: true,
},
participantsList: {
@@ -118,7 +115,7 @@ function handleRules(isPresenter) {
isLobbyEnabled = lsSettings.lobby;
switchLobby.checked = isLobbyEnabled;
rc.roomAction(isLobbyEnabled ? 'lobbyOn' : 'lobbyOff', true, false);
// ROom host-only-recording
// Room host-only-recording
hostOnlyRecording = lsSettings.host_only_recording;
switchHostOnlyRecording.checked = hostOnlyRecording;
rc.roomAction(hostOnlyRecording ? 'hostOnlyRecordingOn' : 'hostOnlyRecordingOff', true, false);

عرض الملف

@@ -29,8 +29,6 @@ const commands = {
chatSend: 'send',
chatOff: 'close the chat',
toggleTr: 'toggle transcription',
chatGPTOn: 'open chatGPT',
chatGPTOff: 'close chatGPT',
whiteboardOn: 'open the whiteboard',
whiteboardOff: 'close the whiteboard',
recordingOn: 'start the recording',
@@ -203,14 +201,6 @@ function execVoiceCommands(transcript) {
printCommand(commands.whiteboardOn);
whiteboardButton.click();
break;
case commands.chatGPTOn:
printCommand(commands.chatGPTOn);
chatGPTButton.click();
break;
case commands.chatGPTOff:
printCommand(commands.chatGPTOff);
chatGPTButton.click();
break;
case commands.whiteboardOff:
printCommand(commands.whiteboardOff);
whiteboardCloseBtn.click();
@@ -241,11 +231,11 @@ function execVoiceCommands(transcript) {
break;
case commands.participantsOn:
printCommand(commands.participantsOn);
participantsButton.click();
chatButton.click();
break;
case commands.participantsOff:
printCommand(commands.participantsOff);
participantsCloseBtn.click();
chatCloseButton.click();
break;
case commands.participantsVideoOff:
printCommand(commands.participantsVideoOff);

عرض الملف

@@ -35,6 +35,7 @@
<link rel="stylesheet" href="../css/Room.css" />
<link rel="stylesheet" href="../css/VideoGrid.css" />
<link rel="stylesheet" href="../css/GroupChat.css" />
<!-- https://cdnjs.com/libraries/font-awesome -->
@@ -108,7 +109,7 @@ access to use this app.
<section>
<div id="initUser" class="init-user hidden">
<!-- <p>Please allow the camera & microphone access to use this app.</p> -->
<div class="container">
<div class="init-container">
<video
id="initVideo"
playsinline="true"
@@ -118,15 +119,21 @@ access to use this app.
style="object-fit: contain"
></video>
</div>
<div>
<button id="initVideoButton" class="fas fa-video" onclick="handleVideo(event)"></button>
<button id="initAudioButton" class="fas fa-microphone" onclick="handleAudio(event)"></button>
<button id="initAudioVideoButton" class="fas fa-eye" onclick="handleAudioVideo(event)"></button>
<button id="initStartScreenButton" class="hidden" onclick="toggleScreenSharing()">
<i class="fas fa-desktop"></i>
</button>
<button id="initStopScreenButton" class="hidden" onclick="toggleScreenSharing()">
<i class="fas fa-stop-circle"></i>
</button>
<button
id="initStartScreenButton"
class="fas fa-desktop hidden"
onclick="toggleScreenSharing()"
></button>
<button
id="initStopScreenButton"
class="fas fa-stop-circle hidden"
onclick="toggleScreenSharing()"
></button>
</div>
<select id="initVideoSelect" class="form-select text-light bg-dark"></select>
<select id="initMicrophoneSelect" class="form-select text-light bg-dark"></select>
<select id="initSpeakerSelect" class="form-select text-light bg-dark"></select>
@@ -150,7 +157,6 @@ access to use this app.
<button id="roomEmojiPicker" class="hidden"><i class="fas fa-face-smile"></i></button>
<button id="chatButton" class="hidden"><i class="fas fa-comments"></i></button>
<button id="transcriptionButton" class="hidden"><i class="fas fa-closed-captioning"></i></button>
<button id="participantsButton" class="hidden"><i class="fas fa-users"></i></button>
<button id="whiteboardButton" class="hidden"><i class="fas fa-chalkboard-teacher"></i></button>
<button id="settingsButton" class="hidden"><i class="fas fa-cogs"></i></button>
<button id="aboutButton" class="hidden"><i class="fas fa-question"></i></button>
@@ -799,19 +805,6 @@ access to use this app.
<button id="receiveHideBtn"><i class="fas fa-eye-slash"></i>&nbsp; Hide</button>
</div>
<section id="participants" class="fadein center hidden">
<header id="participantsHeader" class="participants-header">
<div class="participants-header-options">
<button id="participantsCloseBtn" class="fas fa-times"></button>
<button id="participantsSaveBtn" class="hidden"><i class="fas fa-save"></i></button>
</div>
<div id="participantsTitle" class="participants-header-title"></div>
</header>
<main>
<div id="roomParticipants"></div>
</main>
</section>
<section id="whiteboard" class="fadein center hidden">
<header id="whiteboardHeader" class="whiteboard-header">
<div id="whiteboardTitle" class="whiteboard-header-title">
@@ -856,76 +849,201 @@ access to use this app.
</main>
</section>
<section id="chatRoom" class="chat-room fadein">
<section id="msger" class="msger">
<header id="chatHeader" class="chat-header">
<div class="chat-header-title">
<button id="chatCloseButton" class="fas fa-times"></button>
<button id="chatTogglePin" class="hidden"><i class="fas fa-map-pin"></i></button>
<button id="chatMaxButton" class="hidden"><i class="fas fa-expand"></i></button>
<button id="chatMinButton" class="hidden"><i class="fas fa-compress"></i></button>
<div style="margin-left: 10px" class="title">
<!-- CHAT ROOM CONTAINER -->
<section id="chatRoom" class="chat-container container fadein hidden">
<!-- CHAT APP -->
<div class="chat-app">
<!-- CHAT PEOPLE LIST -->
<div id="plist" class="people-list">
<!-- CHAT LIST OPTIONS -->
<div>
<button id="chatShareRoomBtn"><i class="fas fa-user-plus"></i> Invite user</button>
<button id="chatHideParticipantsList" class="float-right">
<i class="fa-solid fa-circle-arrow-left"></i>
</button>
<button id="participantsUnreadMessagesBtn" class="float-right">
<i class="fa-solid fa-comments"></i>
</button>
<button id="participantsSaveBtn" class="float-right hidden">
<i class="fas fa-save"></i>
</button>
</div>
<br />
<!-- CHAT SEARCH -->
<div class="input-group mt5">
<span class="input-group-text"><i class="fa fa-search"></i></span>
<input
id="searchParticipantsFromList"
type="text"
class="form-control"
placeholder="Search..."
maxlength="36"
/>
</div>
<!-- CHAT LIST -->
<ul id="participantsList" class="list-unstyled chat-list mt-2 mb-0"></ul>
</div>
<!-- CHAT -->
<div id="chat" class="chat">
<!-- CHAT HEADER -->
<div id="chatHeader" class="chat-header clearfix">
<div class="w-100">
<!-- CHAT ABOUT -->
<div id="chatAbout" class="text-start">
<img
class="all-participants-img"
id="chatShowParticipantsList"
src="../images/participants.png"
alt="participants"
/>
<a data-toggle="modal" data-target="#view_info">
<img src="../images/all.png" alt="avatar" />
</a>
<div class="chat-about">
<h6 class="mb-0">Public chat</h6>
</div>
</div>
<!-- CHAT OPTIONS -->
<div style="position: absolute; z-index: 5; right: 50px; top: 20px">
<button id="chatTogglePin" class="hidden">
<i class="fas fa-map-pin"></i>
</button>
<button id="chatMaxButton" class="hidden">
<i class="fas fa-expand"></i>
</button>
<button id="chatMinButton" class="hidden">
<i class="fas fa-compress"></i>
</button>
<button id="chatCloseButton">
<i class="fas fa-times"></i>
</button>
</div>
<!-- CHAT MORE OPTIONS -->
<div style="position: absolute; z-index: 5; right: 5px; top: 20px">
<div class="dropdown">
<button
class="dropdown-toggle"
type="button"
id="chatDropDownMenu"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<i class="fas fa-bars"></i>
</button>
<ul class="dropdown-menu text-start" aria-labelledby="chatDropDownMenu">
<li>
<hr class="hr" />
<div class="title ml10">
<i class="fa-solid fa-eye"></i>
<div style="margin-left: 5px" class="form-check form-switch form-switch-md">
<input class="form-check-input" type="checkbox" id="showChatOnMsg" checked />
<div class="form-check form-switch form-switch-md ml10">
<input
class="form-check-input"
type="checkbox"
id="showChatOnMsg"
checked
/>
</div>
</div>
<div id="speechMsgDiv" class="title">
</li>
<li>
<hr class="hr" />
<div id="speechMsgDiv" class="title ml10">
<i class="fas fa-volume-high"></i>
<div style="margin-left: 5px" class="form-check form-switch form-switch-md">
<input class="form-check-input" type="checkbox" id="speechIncomingMsg" />
<div class="form-check form-switch form-switch-md ml10">
<input
class="form-check-input"
type="checkbox"
id="speechIncomingMsg"
/>
</div>
</div>
<hr class="hr" />
</li>
<li>
<button id="chatGhostButton" class="btn-sm">
<i class="fas fa-circle-half-stroke"></i> Toggle bg
</button>
</li>
<li>
<button id="chatSaveButton" class="btn-sm">
<i class="fas fa-save"></i> Save messages
</button>
</li>
<li>
<button id="chatCleanButton" class="btn-sm">
<i class="fas fa-trash"></i> Clean messages
</button>
</li>
</ul>
</div>
</div>
</div>
<div class="chat-header-options">
<button id="chatGhostButton" class="fas fa-circle-half-stroke"></button>
<button id="chatSaveButton" class="fas fa-save"></button>
<button id="chatCleanButton" class="fas fa-trash"></button>
</div>
</header>
<main id="chatMsger" class="chat-msger">
<!-- chat messages -->
</main>
<div class="chat-msger-inputarea">
<!-- CHAT HISTORY -->
<div id="chatHistory" class="chat-history">
<!-- CHAT MESSAGES -->
<ul id="chatGPTMessages" class="mb-0"></ul>
<ul id="chatPublicMessages" class="mb-0"></ul>
<ul id="chatPrivateMessages" class="mb-0"></ul>
<!-- CHAT EMOJI -->
<div id="chatEmoji" class="hidden fadein chatEmojiPicker"></div>
</div>
<!-- CHAT MESSAGE -->
<div class="chat-message clearfix">
<!-- CHAT MESSAGE INPUT -->
<div class="input-group mb-0 w-100">
<div class="input-group mb-3">
<span class="input-group-text"><i class="fas fa-message"></i></span>
<textarea
rows="1"
cols="1"
id="chatMessage"
class="chat-msger-input"
placeholder="💬 Write a message..."
class="form-control"
placeholder="Enter text here..."
rows="1"
></textarea>
<!-- <input
id="chatMessage"
type="text"
class="form-control"
placeholder="Enter text here..."
> -->
</div>
<div class="chat-msger-buttons">
<button id="chatEmojiButton" class="hidden">
</div>
<!-- CHAT MESSAGE OPTIONS -->
<div class="mt10">
<button id="chatEmojiButton" class="float-left mr5 hidden">
<i class="fas fa-smile"></i>
</button>
<button id="chatMarkdownButton" class="hidden">
<button id="chatMarkdownButton" class="float-left mr5 hidden">
<i class="fab fa-markdown"></i>
</button>
<button id="chatGPTButton" class="hidden">
<i class="fas fa-robot"></i>
</button>
<button id="chatShareFileButton" class="hidden">
<i class="fas fa-paperclip"></i>
</button>
<button id="chatSpeechStartButton" class="hidden">
<button id="chatSpeechStartButton" class="float-left mr5 hidden">
<i class="fas fa-microphone-slash"></i>
</button>
<button id="chatSpeechStopButton" class="hidden">
<button id="chatSpeechStopButton" class="float-left mr5 hidden">
<i class="fas fa-microphone"></i>
</button>
<button id="chatCleanTextButton" class="hidden">
<i class="fas fa-trash"></i>
</button>
<button id="chatPasteButton" class="hidden">
<button id="chatPasteButton" class="float-left mr5 hidden">
<i class="fas fa-paste"></i>
</button>
<button id="chatSendButton" class="hidden">
<button id="chatCleanTextButton" class="float-left mr5 hidden">
<i class="fas fa-trash"></i>
</button>
<button id="chatSendButton" class="float-right hidden">
<i class="fas fa-paper-plane"></i>
</button>
</div>
<div id="chatEmoji" class="hidden fadein chatEmojiPicker"></div>
</section>
</div>
</div>
</div>
</section>
<section id="transcriptionRoom" class="transcription-room fadein">