[mirotalksfu] - #127 add Group Chat feature
هذا الالتزام موجود في:
@@ -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
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
10
package.json
10
package.json
@@ -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
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
ثنائية
public/images/all.png
Normal file
ملف ثنائي غير معروض.
|
بعد العرض: | الارتفاع: | الحجم: 9.3 KiB |
ثنائية
public/images/chatgpt.png
Normal file
ثنائية
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> 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> <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> 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">
|
||||
|
||||
المرجع في مشكلة جديدة
حظر مستخدم