[mirotalk] - feat: add animated disconnect banner for lost signaling connection
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
||||
# ====================================================
|
||||
# MiroTalk P2P v.1.8.17 - Environment Configuration
|
||||
# MiroTalk P2P v.1.8.18 - Environment Configuration
|
||||
# ====================================================
|
||||
|
||||
# App environment
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/**
|
||||
* ==============================================
|
||||
* MiroTalk P2P v.1.8.17 - Configuration File
|
||||
* MiroTalk P2P v.1.8.18 - Configuration File
|
||||
* ==============================================
|
||||
*
|
||||
* This file is the central configuration source.
|
||||
|
||||
+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.8.17
|
||||
* @version 1.8.18
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mirotalk",
|
||||
"version": "1.8.17",
|
||||
"version": "1.8.18",
|
||||
"description": "A free WebRTC browser-based video call",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -91,6 +91,132 @@ body {
|
||||
/* background: url('../images/bg.svg'); */
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------
|
||||
# Disconnect banner
|
||||
--------------------------------------------------------------*/
|
||||
|
||||
.disconnect-banner {
|
||||
position: fixed;
|
||||
top: 18px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(calc(-100% - 30px));
|
||||
z-index: 99999;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition:
|
||||
transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1),
|
||||
opacity 0.35s ease;
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
|
||||
.disconnect-banner.visible {
|
||||
transform: translateX(-50%) translateY(0);
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.disconnect-banner-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
padding: 11px 22px 11px 16px;
|
||||
background: linear-gradient(135deg, rgba(28, 28, 32, 0.82), rgba(18, 18, 20, 0.9));
|
||||
backdrop-filter: blur(18px) saturate(160%);
|
||||
-webkit-backdrop-filter: blur(18px) saturate(160%);
|
||||
border: 1px solid rgba(255, 80, 80, 0.4);
|
||||
border-radius: 50px;
|
||||
box-shadow:
|
||||
0 4px 24px rgba(0, 0, 0, 0.55),
|
||||
0 0 0 1px rgba(255, 80, 80, 0.12) inset,
|
||||
0 0 28px rgba(220, 50, 50, 0.18);
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
transition:
|
||||
border-color 0.4s ease,
|
||||
box-shadow 0.4s ease;
|
||||
}
|
||||
|
||||
.disconnect-banner.reconnected .disconnect-banner-inner {
|
||||
border-color: rgba(52, 211, 100, 0.45);
|
||||
box-shadow:
|
||||
0 4px 24px rgba(0, 0, 0, 0.55),
|
||||
0 0 0 1px rgba(52, 211, 100, 0.12) inset,
|
||||
0 0 28px rgba(30, 180, 80, 0.22);
|
||||
}
|
||||
|
||||
.disconnect-banner-icon-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 50%;
|
||||
background: rgba(220, 50, 50, 0.22);
|
||||
border: 1px solid rgba(255, 80, 80, 0.3);
|
||||
font-size: 15px;
|
||||
color: #ff6b6b;
|
||||
flex-shrink: 0;
|
||||
transition:
|
||||
background 0.4s ease,
|
||||
border-color 0.4s ease,
|
||||
color 0.4s ease;
|
||||
animation: bannerIconPulse 1.8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.disconnect-banner.reconnected .disconnect-banner-icon-wrap {
|
||||
background: rgba(30, 180, 80, 0.22);
|
||||
border-color: rgba(52, 211, 100, 0.35);
|
||||
color: #4ade80;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.disconnect-banner-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
#disconnectBannerTitle {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.02em;
|
||||
color: #ff6b6b;
|
||||
transition: color 0.4s ease;
|
||||
}
|
||||
|
||||
.disconnect-banner.reconnected #disconnectBannerTitle {
|
||||
color: #4ade80;
|
||||
}
|
||||
|
||||
#disconnectBannerMsg {
|
||||
font-size: 11.5px;
|
||||
font-weight: 500;
|
||||
color: rgba(220, 220, 230, 0.75);
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.disconnect-banner-spinner {
|
||||
font-size: 15px;
|
||||
color: rgba(255, 255, 255, 0.45);
|
||||
flex-shrink: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.disconnect-banner.reconnected .disconnect-banner-spinner {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@keyframes bannerIconPulse {
|
||||
0%,
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgba(220, 50, 50, 0.35);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 0 6px rgba(220, 50, 50, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------
|
||||
# Google Translate
|
||||
--------------------------------------------------------------*/
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ let brand = {
|
||||
},
|
||||
about: {
|
||||
imageUrl: '../images/mirotalk-logo.gif',
|
||||
title: 'WebRTC P2P v1.8.17',
|
||||
title: 'WebRTC P2P v1.8.18',
|
||||
html: `
|
||||
<button
|
||||
id="support-button"
|
||||
|
||||
+40
-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.8.17
|
||||
* @version 1.8.18
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -504,6 +504,13 @@ const speechRecognitionStop = getId('speechRecognitionStop');
|
||||
// Media
|
||||
const sinkId = 'sinkId' in HTMLMediaElement.prototype;
|
||||
|
||||
// Disconnect banner
|
||||
const banner = getId('disconnectBanner');
|
||||
const icon = getId('disconnectBannerIcon');
|
||||
const title = getId('disconnectBannerTitle');
|
||||
const msg = getId('disconnectBannerMsg');
|
||||
const spinner = getId('disconnectBannerSpinner');
|
||||
|
||||
//....
|
||||
|
||||
const isRulesActive = true; // Presenter can do anything, guest is slightly moderate, if false no Rules for the room.
|
||||
@@ -1483,6 +1490,7 @@ async function sendToDataChannel(config) {
|
||||
async function handleConnect() {
|
||||
console.log('03. Connected to signaling server');
|
||||
|
||||
hideDisconnectBanner();
|
||||
myPeerId = signalingSocket.id;
|
||||
console.log('04. My peer id [ ' + myPeerId + ' ]');
|
||||
|
||||
@@ -3148,6 +3156,7 @@ function handleIceCandidate(config) {
|
||||
function handleDisconnect(reason) {
|
||||
console.log('Disconnected from signaling server', { reason: reason });
|
||||
|
||||
showDisconnectBanner();
|
||||
checkRecording();
|
||||
|
||||
for (const peer_id in peerConnections) {
|
||||
@@ -15532,7 +15541,7 @@ function showAbout() {
|
||||
Swal.fire({
|
||||
background: swBg,
|
||||
position: 'center',
|
||||
title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.8.17',
|
||||
title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.8.18',
|
||||
imageUrl: brand.about?.imageUrl && brand.about.imageUrl.trim() !== '' ? brand.about.imageUrl : images.about,
|
||||
customClass: { image: 'img-about' },
|
||||
html: `
|
||||
@@ -15818,6 +15827,35 @@ function applyBoxShadowEffect(element, color, delay = 200) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a persistent banner indicating the signaling server connection was lost.
|
||||
*/
|
||||
function showDisconnectBanner() {
|
||||
if (!banner) return;
|
||||
banner.classList.remove('reconnected');
|
||||
icon.className = 'fa-solid fa-wifi-exclamation';
|
||||
title.textContent = 'Connection lost';
|
||||
msg.innerHTML = 'Reconnecting to signaling server\u2026';
|
||||
spinner.style.opacity = '1';
|
||||
// Trigger transition
|
||||
requestAnimationFrame(() => banner.classList.add('visible'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the disconnect banner (or briefly show a reconnected confirmation).
|
||||
*/
|
||||
function hideDisconnectBanner() {
|
||||
if (!banner || !banner.classList.contains('visible')) return;
|
||||
banner.classList.add('reconnected');
|
||||
icon.className = 'fa-solid fa-circle-check';
|
||||
title.textContent = 'Back online';
|
||||
msg.textContent = 'Connection restored successfully';
|
||||
setTimeout(() => {
|
||||
banner.classList.remove('visible');
|
||||
setTimeout(() => banner.classList.remove('reconnected'), 420);
|
||||
}, 2800);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic user logging using https://sweetalert2.github.io & https://animate.style/
|
||||
* @param {string} type of popup
|
||||
|
||||
@@ -73,6 +73,23 @@
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
||||
<!-- Disconnect banner -->
|
||||
|
||||
<div id="disconnectBanner" class="disconnect-banner" role="status" aria-live="assertive" aria-atomic="true">
|
||||
<div class="disconnect-banner-inner">
|
||||
<span class="disconnect-banner-icon-wrap">
|
||||
<i id="disconnectBannerIcon" class="fa-solid fa-wifi-exclamation"></i>
|
||||
</span>
|
||||
<div class="disconnect-banner-text">
|
||||
<span id="disconnectBannerTitle">Connection lost</span>
|
||||
<span id="disconnectBannerMsg">Reconnecting to signaling server…</span>
|
||||
</div>
|
||||
<span class="disconnect-banner-spinner" id="disconnectBannerSpinner">
|
||||
<i class="fa-solid fa-circle-notch fa-spin"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- just 4SEO Optimization -->
|
||||
|
||||
<div id="webRTCSeo">
|
||||
|
||||
Reference in New Issue
Block a user