[mirotalk] - improve widget
This commit is contained in:
@@ -17,6 +17,13 @@ module.exports = {
|
||||
expertImages: [
|
||||
'https://photo.cloudron.pocketsolution.net/uploads/original/95/7d/a5f7f7a2c89a5fee7affda5f013c.jpeg',
|
||||
],
|
||||
buttons: {
|
||||
audio: true,
|
||||
video: true,
|
||||
screen: true,
|
||||
chat: true,
|
||||
join: true,
|
||||
},
|
||||
checkOnlineStatus: false,
|
||||
isOnline: true,
|
||||
customMessages: {
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ dependencies: {
|
||||
* @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon
|
||||
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
|
||||
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
||||
* @version 1.5.55
|
||||
* @version 1.5.56
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mirotalk",
|
||||
"version": "1.5.55",
|
||||
"version": "1.5.56",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mirotalk",
|
||||
"version": "1.5.55",
|
||||
"version": "1.5.56",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@mattermost/client": "10.9.0",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mirotalk",
|
||||
"version": "1.5.55",
|
||||
"version": "1.5.56",
|
||||
"description": "A free WebRTC browser-based video call",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
|
||||
+8
-1
@@ -48,6 +48,13 @@ let brand = {
|
||||
expertImages: [
|
||||
'https://photo.cloudron.pocketsolution.net/uploads/original/95/7d/a5f7f7a2c89a5fee7affda5f013c.jpeg',
|
||||
],
|
||||
buttons: {
|
||||
audio: true,
|
||||
video: true,
|
||||
screen: true,
|
||||
chat: true,
|
||||
join: true,
|
||||
},
|
||||
checkOnlineStatus: false,
|
||||
isOnline: true,
|
||||
customMessages: {
|
||||
@@ -96,7 +103,7 @@ let brand = {
|
||||
},
|
||||
about: {
|
||||
imageUrl: '../images/mirotalk-logo.gif',
|
||||
title: 'WebRTC P2P v1.5.55',
|
||||
title: 'WebRTC P2P v1.5.56',
|
||||
html: `
|
||||
<button
|
||||
id="support-button"
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@
|
||||
* @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon
|
||||
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
|
||||
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
||||
* @version 1.5.55
|
||||
* @version 1.5.56
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -11273,7 +11273,7 @@ function showAbout() {
|
||||
Swal.fire({
|
||||
background: swBg,
|
||||
position: 'center',
|
||||
title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.5.55',
|
||||
title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.5.56',
|
||||
imageUrl: brand.about?.imageUrl && brand.about.imageUrl.trim() !== '' ? brand.about.imageUrl : images.about,
|
||||
customClass: { image: 'img-about' },
|
||||
html: `
|
||||
|
||||
+42
-13
@@ -16,6 +16,13 @@ class MiroTalkWidget {
|
||||
'https://i.pravatar.cc/40?img=2',
|
||||
'https://i.pravatar.cc/40?img=3',
|
||||
],
|
||||
buttons: {
|
||||
audio: true,
|
||||
video: true,
|
||||
screen: true,
|
||||
chat: true,
|
||||
join: true,
|
||||
},
|
||||
checkOnlineStatus: false,
|
||||
isOnline: true,
|
||||
customMessages: {
|
||||
@@ -223,21 +230,28 @@ class MiroTalkWidget {
|
||||
}
|
||||
|
||||
createActionButtons() {
|
||||
const buttons = [
|
||||
{ action: 'startAudioCall', icon: this.getAudioIcon(), text: 'Start Audio Call' },
|
||||
{ action: 'startVideoCall', icon: this.getVideoIcon(), text: 'Start Video Call' },
|
||||
];
|
||||
const flags = this.options.supportWidget.buttons || {};
|
||||
const buttons = [];
|
||||
|
||||
// Only show "Start Screen Share" if displayMedia is supported
|
||||
if (navigator.mediaDevices && typeof navigator.mediaDevices.getDisplayMedia === 'function') {
|
||||
if (flags.audio) {
|
||||
buttons.push({ action: 'startAudioCall', icon: this.getAudioIcon(), text: 'Start Audio Call' });
|
||||
}
|
||||
if (flags.video) {
|
||||
buttons.push({ action: 'startVideoCall', icon: this.getVideoIcon(), text: 'Start Video Call' });
|
||||
}
|
||||
if (flags.screen && navigator.mediaDevices && typeof navigator.mediaDevices.getDisplayMedia === 'function') {
|
||||
buttons.push({ action: 'startScreenShare', icon: this.getScreenIcon(), text: 'Start Screen Share' });
|
||||
}
|
||||
if (flags.chat) {
|
||||
buttons.push({ action: 'startChat', icon: this.getChatIcon(), text: 'Start Chat' });
|
||||
}
|
||||
if (flags.join) {
|
||||
buttons.push({ action: 'joinRoom', icon: this.getJoinIcon(), text: 'Join Room' });
|
||||
}
|
||||
|
||||
// Add chat button
|
||||
buttons.push({ action: 'startChat', icon: this.getChatIcon(), text: 'Start Chat' });
|
||||
|
||||
// Insert "Start Screen Share" before "Join Room" if present
|
||||
buttons.push({ action: 'joinRoom', icon: this.getJoinIcon(), text: 'Join Room' });
|
||||
if (!buttons.length) {
|
||||
return `<div class="no-actions">No actions available</div>`;
|
||||
}
|
||||
|
||||
return buttons
|
||||
.map(
|
||||
@@ -245,8 +259,7 @@ class MiroTalkWidget {
|
||||
<button class="btn" onclick="miroTalkWidgetAction('${btn.action}', this)">
|
||||
<div class="btn-icon">${btn.icon}</div>
|
||||
<span class="btn-text">${btn.text}</span>
|
||||
</button>
|
||||
`
|
||||
</button>`
|
||||
)
|
||||
.join('');
|
||||
}
|
||||
@@ -709,6 +722,21 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
if (!autoInit) return;
|
||||
|
||||
try {
|
||||
const buttonsAttr = autoInit.getAttribute('data-buttons');
|
||||
let buttonsConfig = { ...MiroTalkWidget.DEFAULT_OPTIONS.supportWidget.buttons };
|
||||
if (buttonsAttr) {
|
||||
// Normalize and map
|
||||
const requested = buttonsAttr
|
||||
.split(',')
|
||||
.map((b) => b.trim().toLowerCase())
|
||||
.filter(Boolean);
|
||||
// Start all false then enable requested valid keys
|
||||
buttonsConfig = { audio: false, video: false, screen: false, chat: false, join: false };
|
||||
requested.forEach((key) => {
|
||||
if (key in buttonsConfig) buttonsConfig[key] = true;
|
||||
});
|
||||
}
|
||||
|
||||
const config = {
|
||||
domain: autoInit.getAttribute('data-domain') || window.location.host,
|
||||
roomId: autoInit.getAttribute('data-room') || 'support-room',
|
||||
@@ -756,6 +784,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
expertImages: config.expertImages,
|
||||
checkOnlineStatus: config.checkOnline,
|
||||
customMessages: config.customMessages,
|
||||
buttons: buttonsConfig,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
data-position="bottom-right"
|
||||
data-check-online="false"
|
||||
data-expert-images="https://i.pravatar.cc/40?img=1,https://i.pravatar.cc/40?img=2,https://i.pravatar.cc/40?img=3"
|
||||
data-buttons="audio,video,screen,chat,join"
|
||||
data-heading="Need Help?"
|
||||
data-subheading="Get instant support from our expert team!"
|
||||
data-connect-text="connect in < 5 seconds"
|
||||
|
||||
+184
-42
@@ -346,6 +346,63 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.button-toggle-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
|
||||
gap: 10px 14px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
background: #eef1f4;
|
||||
border: 1px solid #d5dade;
|
||||
padding: 8px 10px 8px 40px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition:
|
||||
background 0.25s,
|
||||
border-color 0.25s;
|
||||
}
|
||||
|
||||
.toggle input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.toggle::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 6px;
|
||||
background: linear-gradient(135deg, #9aa0a6, #7e858c);
|
||||
box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.5);
|
||||
transition: background 0.25s;
|
||||
}
|
||||
|
||||
.toggle.checked {
|
||||
background: #e5faf0;
|
||||
border-color: #00c853;
|
||||
color: #116d3d;
|
||||
}
|
||||
|
||||
.toggle.checked::before {
|
||||
background: linear-gradient(135deg, #00e676, #00c853);
|
||||
}
|
||||
|
||||
.toggle:active {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.content {
|
||||
grid-template-columns: 1fr;
|
||||
@@ -453,6 +510,30 @@
|
||||
<label for="check-online">Check Online Status <span class="feature-badge">PRO</span></label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="form-group">
|
||||
<label class="tooltip" data-tooltip="Select which action buttons to include">
|
||||
Action Buttons
|
||||
</label>
|
||||
<div id="action-buttons" class="button-toggle-grid">
|
||||
<label class="toggle" data-btn="audio"
|
||||
><input type="checkbox" id="btn-audio" checked />Audio</label
|
||||
>
|
||||
<label class="toggle" data-btn="video"
|
||||
><input type="checkbox" id="btn-video" checked />Video</label
|
||||
>
|
||||
<label class="toggle" data-btn="screen"
|
||||
><input type="checkbox" id="btn-screen" checked />Screen</label
|
||||
>
|
||||
<label class="toggle" data-btn="chat"
|
||||
><input type="checkbox" id="btn-chat" checked />Chat</label
|
||||
>
|
||||
<label class="toggle" data-btn="join"
|
||||
><input type="checkbox" id="btn-join" checked />Join</label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Customization Form -->
|
||||
@@ -555,6 +636,11 @@ https://i.pravatar.cc/40?img=1,https://i.pravatar.cc/40?img=2,https://i.pravatar
|
||||
offlineText: document.getElementById('offline-text'),
|
||||
poweredBy: document.getElementById('powered-by'),
|
||||
expertImages: document.getElementById('expert-images'),
|
||||
btnAudio: document.getElementById('btn-audio'),
|
||||
btnVideo: document.getElementById('btn-video'),
|
||||
btnScreen: document.getElementById('btn-screen'),
|
||||
btnChat: document.getElementById('btn-chat'),
|
||||
btnJoin: document.getElementById('btn-join'),
|
||||
};
|
||||
|
||||
const generatedCode = document.getElementById('generated-code');
|
||||
@@ -579,36 +665,45 @@ https://i.pravatar.cc/40?img=1,https://i.pravatar.cc/40?img=2,https://i.pravatar
|
||||
'https://i.pravatar.cc/40?img=1,https://i.pravatar.cc/40?img=2,https://i.pravatar.cc/40?img=3',
|
||||
};
|
||||
|
||||
const buttonsSelected = [];
|
||||
if (form.btnAudio.checked) buttonsSelected.push('audio');
|
||||
if (form.btnVideo.checked) buttonsSelected.push('video');
|
||||
if (form.btnScreen.checked) buttonsSelected.push('screen');
|
||||
if (form.btnChat.checked) buttonsSelected.push('chat');
|
||||
if (form.btnJoin.checked) buttonsSelected.push('join');
|
||||
|
||||
const buttonsAttr = buttonsSelected.join(',');
|
||||
|
||||
const html = `<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>MiroTalk Widget</title>
|
||||
<script src="https://${config.domain}/js/Widget.js"><\/script>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>MiroTalk Widget</title>
|
||||
<script src="https://${config.domain}/js/Widget.js"><\/script>
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
id="mirotalk-widget"
|
||||
data-mirotalk-auto
|
||||
data-domain="${config.domain}"
|
||||
data-room="${config.room}"
|
||||
data-theme="${config.theme}"
|
||||
data-widget-type="support"
|
||||
data-position="${config.position}"
|
||||
data-widget-state="${config.widgetState}"
|
||||
data-check-online="${config.checkOnline}"
|
||||
data-expert-images="${escapeHtml(config.expertImages)}"
|
||||
data-heading="${escapeHtml(config.heading)}"
|
||||
data-subheading="${escapeHtml(config.subheading)}"
|
||||
data-connect-text="${escapeHtml(config.connectText)}"
|
||||
data-online-text="${escapeHtml(config.onlineText)}"
|
||||
data-offline-text="${escapeHtml(config.offlineText)}"
|
||||
data-powered-by="${escapeHtml(config.poweredBy)}"
|
||||
></div>
|
||||
<div
|
||||
id="mirotalk-widget"
|
||||
data-mirotalk-auto
|
||||
data-domain="${config.domain}"
|
||||
data-room="${config.room}"
|
||||
data-theme="${config.theme}"
|
||||
data-widget-type="support"
|
||||
data-position="${config.position}"
|
||||
data-widget-state="${config.widgetState}"
|
||||
data-buttons="${buttonsAttr}"
|
||||
data-check-online="${config.checkOnline}"
|
||||
data-expert-images="${escapeHtml(config.expertImages)}"
|
||||
data-heading="${escapeHtml(config.heading)}"
|
||||
data-subheading="${escapeHtml(config.subheading)}"
|
||||
data-connect-text="${escapeHtml(config.connectText)}"
|
||||
data-online-text="${escapeHtml(config.onlineText)}"
|
||||
data-offline-text="${escapeHtml(config.offlineText)}"
|
||||
data-powered-by="${escapeHtml(config.poweredBy)}"
|
||||
></div>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
generatedCode.value = html;
|
||||
}
|
||||
|
||||
@@ -668,19 +763,25 @@ https://i.pravatar.cc/40?img=1,https://i.pravatar.cc/40?img=2,https://i.pravatar
|
||||
form.poweredBy.value = 'Powered by MiroTalk';
|
||||
form.expertImages.value =
|
||||
'https://i.pravatar.cc/40?img=1,https://i.pravatar.cc/40?img=2,https://i.pravatar.cc/40?img=3';
|
||||
form.btnAudio.checked =
|
||||
form.btnVideo.checked =
|
||||
form.btnScreen.checked =
|
||||
form.btnChat.checked =
|
||||
form.btnJoin.checked =
|
||||
true;
|
||||
|
||||
toggleLabels.forEach(syncToggleVisual);
|
||||
generateCode();
|
||||
saveFormData();
|
||||
}
|
||||
|
||||
// Auto-save form data to localStorage
|
||||
function saveFormData() {
|
||||
const data = {};
|
||||
Object.keys(form).forEach((key) => {
|
||||
if (form[key].type === 'checkbox') {
|
||||
data[key] = form[key].checked;
|
||||
} else {
|
||||
data[key] = form[key].value;
|
||||
}
|
||||
if (!form[key]) return;
|
||||
if (form[key].type === 'checkbox') data[key] = form[key].checked;
|
||||
else data[key] = form[key].value;
|
||||
});
|
||||
localStorage.setItem('mirotalk-widget-maker', JSON.stringify(data));
|
||||
}
|
||||
@@ -691,27 +792,68 @@ https://i.pravatar.cc/40?img=1,https://i.pravatar.cc/40?img=2,https://i.pravatar
|
||||
const data = JSON.parse(localStorage.getItem('mirotalk-widget-maker'));
|
||||
if (data) {
|
||||
Object.keys(data).forEach((key) => {
|
||||
if (form[key]) {
|
||||
if (form[key].type === 'checkbox') {
|
||||
form[key].checked = data[key];
|
||||
} else {
|
||||
form[key].value = data[key];
|
||||
}
|
||||
}
|
||||
if (!form[key]) return;
|
||||
if (form[key].type === 'checkbox') form[key].checked = data[key];
|
||||
else form[key].value = data[key];
|
||||
});
|
||||
generateCode();
|
||||
} else {
|
||||
// No saved data -> default all action buttons checked
|
||||
setAllActionButtons(true);
|
||||
}
|
||||
} catch (err) {
|
||||
// If somehow all buttons ended up unchecked, enforce default all on first load
|
||||
if (
|
||||
![form.btnAudio, form.btnVideo, form.btnScreen, form.btnChat, form.btnJoin].some(
|
||||
(b) => b.checked
|
||||
)
|
||||
) {
|
||||
setAllActionButtons(true);
|
||||
}
|
||||
toggleLabels.forEach(syncToggleVisual);
|
||||
generateCode();
|
||||
} catch (e) {
|
||||
console.warn('Failed to load saved form data');
|
||||
setAllActionButtons(true);
|
||||
toggleLabels.forEach(syncToggleVisual);
|
||||
generateCode();
|
||||
}
|
||||
}
|
||||
|
||||
function setAllActionButtons(state) {
|
||||
form.btnAudio.checked =
|
||||
form.btnVideo.checked =
|
||||
form.btnScreen.checked =
|
||||
form.btnChat.checked =
|
||||
form.btnJoin.checked =
|
||||
state;
|
||||
}
|
||||
|
||||
// toggle handling (must be before loadFormData())
|
||||
const toggleLabels = Array.from(document.querySelectorAll('#action-buttons .toggle'));
|
||||
|
||||
function syncToggleVisual(label) {
|
||||
const cb = label.querySelector('input[type=checkbox]');
|
||||
label.classList.toggle('checked', cb.checked);
|
||||
}
|
||||
|
||||
// Attach handlers
|
||||
toggleLabels.forEach((label) => {
|
||||
const cb = label.querySelector('input[type=checkbox]');
|
||||
const onChange = () => {
|
||||
syncToggleVisual(label);
|
||||
generateCode();
|
||||
saveFormData();
|
||||
};
|
||||
cb.addEventListener('change', onChange);
|
||||
// Initial state
|
||||
syncToggleVisual(label);
|
||||
});
|
||||
|
||||
// Add event listeners to all form elements
|
||||
Object.values(form).forEach((element) => {
|
||||
element.addEventListener('input', generateCode);
|
||||
element.addEventListener('change', generateCode);
|
||||
element.addEventListener('input', saveFormData);
|
||||
element.addEventListener('change', saveFormData);
|
||||
Object.values(form).forEach((el) => {
|
||||
el.addEventListener('input', generateCode);
|
||||
el.addEventListener('change', generateCode);
|
||||
el.addEventListener('input', saveFormData);
|
||||
el.addEventListener('change', saveFormData);
|
||||
});
|
||||
|
||||
// Add event listeners for buttons
|
||||
|
||||
Reference in New Issue
Block a user