[mirotalk] - refactor(ui): redesign file transfer card and progress

This commit is contained in:
Miroslav Pejic
2026-04-30 21:29:40 +02:00
parent 6ef5db9f66
commit a838a70ad5
9 changed files with 350 additions and 55 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
# ====================================================
# MiroTalk P2P v.1.8.27 - Environment Configuration
# MiroTalk P2P v.1.8.28 - Environment Configuration
# ====================================================
# App environment
+1 -1
View File
@@ -2,7 +2,7 @@
/**
* ==============================================
* MiroTalk P2P v.1.8.27 - Configuration File
* MiroTalk P2P v.1.8.28 - Configuration File
* ==============================================
*
* This file is the central configuration source.
+1 -1
View File
@@ -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.27
* @version 1.8.28
*
*/
+6 -6
View File
@@ -1,12 +1,12 @@
{
"name": "mirotalk",
"version": "1.8.27",
"version": "1.8.28",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mirotalk",
"version": "1.8.27",
"version": "1.8.28",
"license": "AGPL-3.0",
"dependencies": {
"@mattermost/client": "11.6.0",
@@ -18,7 +18,7 @@
"compression": "^1.8.1",
"cors": "^2.8.6",
"crypto-js": "^4.2.0",
"dompurify": "^3.4.1",
"dompurify": "^3.4.2",
"dotenv": "^17.4.2",
"express": "^5.2.1",
"express-openid-connect": "^2.20.2",
@@ -2501,9 +2501,9 @@
}
},
"node_modules/dompurify": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.1.tgz",
"integrity": "sha512-JahakDAIg1gyOm7dlgWSDjV4n7Ip2PKR55NIT6jrMfIgLFgWo81vdr1/QGqWtFNRqXP9UV71oVePtjqS2ebnPw==",
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.2.tgz",
"integrity": "sha512-lHeS9SA/IKeIFFyYciHBr2n0v1VMPlSj843HdLOwjb2OxNwdq9Xykxqhk+FE42MzAdHvInbAolSE4mhahPpjXA==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "mirotalk",
"version": "1.8.27",
"version": "1.8.28",
"description": "A free WebRTC browser-based video call",
"main": "server.js",
"scripts": {
@@ -53,7 +53,7 @@
"compression": "^1.8.1",
"cors": "^2.8.6",
"crypto-js": "^4.2.0",
"dompurify": "^3.4.1",
"dompurify": "^3.4.2",
"dotenv": "^17.4.2",
"express": "^5.2.1",
"express-openid-connect": "^2.20.2",
+256 -20
View File
@@ -4126,46 +4126,282 @@ input:checked + .slider:before {
#receiveFileDiv {
z-index: 17;
display: none;
width: min(92vw, 440px);
min-width: 320px;
border-radius: 12px;
margin: auto;
padding: 10px;
color: #fff;
font-size: 1rem;
background: var(--body-bg);
box-shadow: var(--box-shadow);
background: transparent;
box-shadow: none;
overflow: hidden;
}
#sendFileDiv {
--file-transfer-accent: #6fb4ff;
--file-transfer-accent-soft: rgba(111, 180, 255, 0.18);
}
#receiveFileDiv {
--file-transfer-accent: #59d7a5;
--file-transfer-accent-soft: rgba(89, 215, 165, 0.18);
}
.file-transfer-shell {
position: relative;
display: flex;
flex-direction: column;
gap: 18px;
padding: 22px;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 24px;
background: var(--body-bg);
backdrop-filter: blur(18px);
box-shadow: 0 28px 65px rgba(0, 0, 0, 0.35);
}
.file-transfer-hero {
display: flex;
align-items: center;
gap: 16px;
}
.file-transfer-drag-handle {
cursor: move;
}
.file-transfer-drag-handle * {
cursor: inherit;
}
.file-transfer-icon-wrap {
display: flex;
align-items: center;
justify-content: center;
flex: 0 0 72px;
width: 72px;
height: 72px;
border-radius: 22px;
background: linear-gradient(145deg, rgba(255, 255, 255, 0.12), var(--file-transfer-accent-soft));
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
.file-transfer-image {
width: 44px;
height: 44px;
object-fit: contain;
filter: drop-shadow(0 10px 18px rgba(0, 0, 0, 0.2));
}
.file-transfer-copy {
min-width: 0;
}
.file-transfer-kicker {
display: inline-flex;
align-items: center;
gap: 6px;
margin-bottom: 6px;
padding: 4px 10px;
border-radius: 999px;
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--file-transfer-accent);
background: rgba(0, 0, 0, 0.2);
}
.file-transfer-title {
margin: 0;
font-size: 1.35rem;
font-weight: 700;
line-height: 1.1;
}
.file-transfer-subtitle {
margin: 8px 0 0;
font-size: 0.95rem;
line-height: 1.5;
color: rgba(255, 255, 255, 0.76);
}
.file-transfer-meta {
display: flex;
flex-direction: column;
gap: 10px;
padding: 16px 18px;
border-radius: 18px;
background: rgba(0, 0, 0, 0.18);
border: 1px solid rgba(255, 255, 255, 0.06);
}
.file-transfer-info {
white-space: pre-line;
line-height: 1.55;
color: rgba(255, 255, 255, 0.88);
word-break: break-word;
}
.file-transfer-percentage {
font-size: 0.92rem;
font-weight: 600;
color: var(--file-transfer-accent);
}
.file-transfer-progress-wrap {
display: flex;
}
#sendProgress,
#receiveProgress {
width: 100%;
min-width: 100%;
height: 12px;
overflow: hidden;
border: 0;
border-radius: 999px;
background: rgba(255, 255, 255, 0.08);
}
#sendProgress::-webkit-progress-bar,
#receiveProgress::-webkit-progress-bar {
background: rgba(255, 255, 255, 0.08);
border-radius: 999px;
}
#sendProgress::-webkit-progress-value,
#receiveProgress::-webkit-progress-value {
border-radius: 999px;
background: linear-gradient(
90deg,
var(--file-transfer-accent),
color-mix(in srgb, var(--file-transfer-accent) 55%, white)
);
}
#sendProgress::-moz-progress-bar,
#receiveProgress::-moz-progress-bar {
border-radius: 999px;
background: linear-gradient(
90deg,
var(--file-transfer-accent),
color-mix(in srgb, var(--file-transfer-accent) 55%, white)
);
}
.file-transfer-actions {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
#sendAbortBtn,
#receiveAbortBtn,
#receiveHideBtn {
margin-top: 10px;
padding: 5px;
border: var(--border);
border-radius: 8px;
font-size: 1rem;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 10px;
min-height: 46px;
margin-top: 0;
padding: 0 18px;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 14px;
font-size: 0.96rem;
font-weight: 600;
color: #fff;
background-color: transparent;
transition: background 0.23s;
background-color: rgba(255, 255, 255, 0.04);
transition:
transform 0.23s ease,
background 0.23s ease,
border-color 0.23s ease,
color 0.23s ease,
box-shadow 0.23s ease;
}
#sendAbortBtn,
#receiveAbortBtn {
background: rgba(255, 89, 89, 0.12);
border-color: rgba(255, 89, 89, 0.2);
}
#receiveHideBtn {
background: rgba(255, 255, 255, 0.04);
}
#sendAbortBtn i,
#receiveAbortBtn i,
#receiveHideBtn i {
font-size: 1rem;
}
#sendAbortBtn:hover,
#receiveAbortBtn:hover,
#receiveHideBtn:hover {
color: rgb(255, 0, 0);
background: var(--body-bg);
transform: translateY(-3px);
transform: translateY(-2px);
box-shadow: 0 16px 30px rgba(0, 0, 0, 0.22);
}
progress {
width: 0;
min-width: 100%;
#sendAbortBtn:hover,
#receiveAbortBtn:hover {
color: #ffb4b4;
background: rgba(255, 89, 89, 0.18);
border-color: rgba(255, 89, 89, 0.35);
}
#imgShareSend:hover,
#imgShareReceive:hover {
cursor: move;
#receiveHideBtn:hover {
color: var(--file-transfer-accent);
background: rgba(255, 255, 255, 0.08);
border-color: rgba(255, 255, 255, 0.12);
}
@media (max-width: 640px) {
#sendFileDiv,
#receiveFileDiv {
width: min(94vw, 420px);
min-width: 0;
}
.file-transfer-shell {
gap: 16px;
padding: 18px;
border-radius: 22px;
}
.file-transfer-hero {
align-items: flex-start;
}
.file-transfer-icon-wrap {
flex-basis: 62px;
width: 62px;
height: 62px;
border-radius: 18px;
}
.file-transfer-image {
width: 36px;
height: 36px;
}
.file-transfer-title {
font-size: 1.18rem;
}
.file-transfer-subtitle {
font-size: 0.88rem;
}
.file-transfer-actions {
flex-direction: column;
}
#sendAbortBtn,
#receiveAbortBtn,
#receiveHideBtn {
width: 100%;
}
}
#dropArea {
+1 -1
View File
@@ -109,7 +109,7 @@ let brand = {
},
about: {
imageUrl: '../images/mirotalk-logo.gif',
title: 'WebRTC P2P v1.8.27',
title: 'WebRTC P2P v1.8.28',
html: `
<button
id="support-button"
+6 -10
View File
@@ -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.27
* @version 1.8.28
*
*/
@@ -478,7 +478,7 @@ const unlockRoomBtn = getId('unlockRoomBtn');
// File send progress
const sendFileDiv = getId('sendFileDiv');
const imgShareSend = getId('imgShareSend');
const sendFileDragHandle = getId('sendFileDragHandle');
const sendFilePercentage = getId('sendFilePercentage');
const sendFileInfo = getId('sendFileInfo');
const sendProgress = getId('sendProgress');
@@ -486,7 +486,7 @@ const sendAbortBtn = getId('sendAbortBtn');
// File receive progress
const receiveFileDiv = getId('receiveFileDiv');
const imgShareReceive = getId('imgShareReceive');
const receiveFileDragHandle = getId('receiveFileDragHandle');
const receiveFilePercentage = getId('receiveFilePercentage');
const receiveFileInfo = getId('receiveFileInfo');
const receiveProgress = getId('receiveProgress');
@@ -921,10 +921,6 @@ function setButtonsToolTip() {
setTippy(whiteboardEraserBtn, 'Eraser mode', 'bottom');
setTippy(whiteboardUndoBtn, 'Undo', 'bottom');
setTippy(whiteboardRedoBtn, 'Redo', 'bottom');
// Suspend/Hide File transfer buttons
setTippy(sendAbortBtn, 'Abort file transfer', 'bottom');
setTippy(receiveAbortBtn, 'Abort file transfer', 'bottom');
setTippy(receiveHideBtn, 'Hide file transfer', 'bottom');
// Video/audio URL player
setTippy(videoUrlCloseBtn, 'Close the video player', 'bottom');
setTippy(videoAudioCloseBtn, 'Close the video player', 'bottom');
@@ -6975,8 +6971,8 @@ function setMyWhiteboardBtn() {
function setMyFileShareBtn() {
// make send-receive file div draggable
if (!isMobileDevice) {
dragElement(sendFileDiv, imgShareSend);
dragElement(receiveFileDiv, imgShareReceive);
dragElement(sendFileDiv, sendFileDragHandle);
dragElement(receiveFileDiv, receiveFileDragHandle);
}
fileShareBtn.addEventListener('click', (e) => {
@@ -15783,7 +15779,7 @@ function showAbout() {
Swal.fire({
background: swBg,
position: 'center',
title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.8.27',
title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.8.28',
imageUrl: brand.about?.imageUrl && brand.about.imageUrl.trim() !== '' ? brand.about.imageUrl : images.about,
customClass: { image: 'img-about' },
html: `
+76 -13
View File
@@ -1434,25 +1434,88 @@ access to use this app.
<!-- Start File Send -->
<div id="sendFileDiv" class="center fadein">
<img id="imgShareSend" src="../images/share.png" alt="mirotalk-share-send" class="center-img" /><br />
<div id="sendFileInfo"></div>
<div id="sendFilePercentage"></div>
<progress id="sendProgress" max="0" value="0"></progress>
<button id="sendAbortBtn"><i class="fas fa-stop-circle"></i>&nbsp; Abort</button>
<div
id="sendFileDiv"
class="center fadein file-transfer-card file-transfer-card-send"
role="status"
aria-live="polite"
>
<div class="file-transfer-shell">
<div id="sendFileDragHandle" class="file-transfer-hero file-transfer-drag-handle">
<div class="file-transfer-icon-wrap">
<img
id="imgShareSend"
src="../images/share.png"
alt="mirotalk-share-send"
class="file-transfer-image"
/>
</div>
<div class="file-transfer-copy">
<span class="file-transfer-kicker">Outgoing transfer</span>
<h3 class="file-transfer-title">Sending file</h3>
<p class="file-transfer-subtitle">Keep this window open while the transfer completes.</p>
</div>
</div>
<div class="file-transfer-meta">
<div id="sendFileInfo" class="file-transfer-info"></div>
<div id="sendFilePercentage" class="file-transfer-percentage">Preparing transfer...</div>
</div>
<div class="file-transfer-progress-wrap">
<progress id="sendProgress" max="0" value="0"></progress>
</div>
<div class="file-transfer-actions">
<button id="sendAbortBtn" class="file-transfer-btn file-transfer-btn-danger">
<i class="fas fa-stop-circle"></i>
<span>Abort</span>
</button>
</div>
</div>
</div>
<!-- End File Send -->
<!-- Start File Receive -->
<div id="receiveFileDiv" class="center fadein">
<img id="imgShareReceive" src="../images/share.png" alt="mirotalk-share-receive" class="center-img" /><br />
<div id="receiveFileInfo"></div>
<div id="receiveFilePercentage"></div>
<progress id="receiveProgress" max="0" value="0"></progress>
<button id="receiveAbortBtn"><i class="fas fa-stop-circle"></i>&nbsp; Abort</button>
<button id="receiveHideBtn"><i class="fas fa-eye-slash"></i>&nbsp; Hide</button>
<div
id="receiveFileDiv"
class="center fadein file-transfer-card file-transfer-card-receive"
role="status"
aria-live="polite"
>
<div class="file-transfer-shell">
<div id="receiveFileDragHandle" class="file-transfer-hero file-transfer-drag-handle">
<div class="file-transfer-icon-wrap">
<img
id="imgShareReceive"
src="../images/share.png"
alt="mirotalk-share-receive"
class="file-transfer-image"
/>
</div>
<div class="file-transfer-copy">
<span class="file-transfer-kicker">Incoming transfer</span>
<h3 class="file-transfer-title">Receiving file</h3>
<p class="file-transfer-subtitle">You can hide this panel and let the download continue.</p>
</div>
</div>
<div class="file-transfer-meta">
<div id="receiveFileInfo" class="file-transfer-info"></div>
<div id="receiveFilePercentage" class="file-transfer-percentage">Waiting for data...</div>
</div>
<div class="file-transfer-progress-wrap">
<progress id="receiveProgress" max="0" value="0"></progress>
</div>
<div class="file-transfer-actions">
<button id="receiveAbortBtn" class="file-transfer-btn file-transfer-btn-danger">
<i class="fas fa-stop-circle"></i>
<span>Abort</span>
</button>
<button id="receiveHideBtn" class="file-transfer-btn file-transfer-btn-secondary">
<i class="fas fa-eye-slash"></i>
<span>Hide</span>
</button>
</div>
</div>
</div>
<!-- Stop File Receive -->