Added partial file transfers in MeshMessenger
This commit is contained in:
parent
de76f812d5
commit
13fffd4f02
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Binary file not shown.
Before Width: | Height: | Size: 25 KiB |
|
@ -14,7 +14,7 @@
|
|||
<div id="fileButton" class="icon4 topButton" title="Share a file" style="display:none" onclick="fileButtonClick()"></div>
|
||||
<div id="camButton" class="icon2 topButton" title="Activate camera & microphone" style="display:none" onclick="camButtonClick()"></div>
|
||||
<div id="micButton" class="icon6 topButton" title="Activate microphone" style="display:none" onclick="micButtonClick()"></div>
|
||||
<div id="hangupButton" class="icon11 topButton" title="Hang up" style="display:none" onclick="hangUpButtonClick(1)"></div>
|
||||
<div id="hangupButton" class="icon11 topRedButton" title="Hang up" style="display:none" onclick="hangUpButtonClick(1)"></div>
|
||||
<div style="display:inline-block;width:2px"></div>
|
||||
<div style="padding-top:9px;padding-left:6px;font-size:20px;display:inline-block"><b>MeshMessenger<span id="xtitle"></span></b></div>
|
||||
</div>
|
||||
|
@ -36,6 +36,7 @@
|
|||
<div style="position:absolute;right:0;left:0;top:2.5px;text-align:center">Local</div>
|
||||
<video id="localVideoCanvas" autoplay muted style="position:absolute;top:20px;left:0;width:100%;height:calc(100% - 30px);background-color:black"></video>
|
||||
</div>
|
||||
<input id="uploadFileInput" type="file" style="display:none">
|
||||
<script type="text/javascript">
|
||||
var userInputFocus = 0;
|
||||
var args = parseUriArgs();
|
||||
|
@ -47,11 +48,18 @@
|
|||
var webrtcconfiguration = null; //{ "iceServers": [ { 'urls': 'stun:stun.services.mozilla.com' }, { 'urls': 'stun:stun.l.google.com:19302' } ] };
|
||||
var localStream = null;
|
||||
var remoteStream = null;
|
||||
var senders = null;
|
||||
var fileUploads = [];
|
||||
var fileDownloads = {};
|
||||
var currentFileUpload = null;
|
||||
var currentFileDownload = null;
|
||||
|
||||
// Set the title
|
||||
if (args.title) { QH('xtitle', ' - ' + args.title); document.title = document.title + ' - ' + args.title; }
|
||||
|
||||
document.addEventListener('dragover', haltEvent, false);
|
||||
document.addEventListener('dragleave', haltEvent, false);
|
||||
document.addEventListener('drop', fileDrop, false);
|
||||
|
||||
document.onkeyup = function ondockeypress(e) {
|
||||
if (state == 2) {
|
||||
if ((e.keyCode == 8) && (userInputFocus == 0)) {
|
||||
|
@ -119,6 +127,7 @@
|
|||
QE('sendButton', state == 2);
|
||||
QE('clearButton', state == 2);
|
||||
QE('xouttext', state == 2);
|
||||
QV('fileButton', state == 2);
|
||||
QV('camButton', webchannel && webchannel.ok && !localStream);
|
||||
QV('micButton', webchannel && webchannel.ok && !localStream);
|
||||
QV('hangupButton', webchannel && webchannel.ok && localStream);
|
||||
|
@ -195,6 +204,7 @@
|
|||
function disconnect() {
|
||||
if (state > 0) { displayControl('Connection closed.'); }
|
||||
if (state > 1) { setTimeout(start, 500); }
|
||||
cancelAllFileTransfers();
|
||||
hangUpButtonClick(0, true); // Data channel
|
||||
hangUpButtonClick(1, true); // Local audio/video
|
||||
hangUpButtonClick(2, true); // Remote audio/video
|
||||
|
@ -208,7 +218,7 @@
|
|||
if (state != 2) return; // If not in connected state, ignore this.
|
||||
if (typeof data == 'object') { data = JSON.stringify(data); } // If this is an object, convert it to a string.
|
||||
if (webchannel && webchannel.ok) { if (webchannel.xoutBuffer != null) { webchannel.xoutBuffer.push(data); } else { webchannel.send(data); } } // If WebRTC channel is possible, use it or hold until we can use it.
|
||||
else { if (socket != null) { socket.send(data); } } // If a websocket channel is present, use that.
|
||||
else { if (socket != null) { try { socket.send(data); } catch (ex) { } } } // If a websocket channel is present, use that.
|
||||
}
|
||||
|
||||
// Send data over the websocket transport (WebSocket only)
|
||||
|
@ -240,6 +250,39 @@
|
|||
}
|
||||
break;
|
||||
}
|
||||
case 'file': { startFileDownload(data); break; }
|
||||
case 'fileUploadCancel': { cancelFileTransfer(data.id); break; }
|
||||
case 'fileUploadStart': {
|
||||
if (fileDownloads[data.id]) {
|
||||
currentFileDownload = fileDownloads[data.id];
|
||||
currentFileDownload.data = '';
|
||||
changeFileInfo(data.id, 2, 0);
|
||||
continueFileDownload(data);
|
||||
send({ action: 'fileUploadAck', id: data.id });
|
||||
} break;
|
||||
}
|
||||
case 'fileUploadEnd': {
|
||||
if (currentFileDownload && (currentFileDownload.id == data.id)) {
|
||||
changeFileInfo(data.id, 3, 200);
|
||||
currentFileDownload.done = 1;
|
||||
currentFileDownload = null;
|
||||
send({ action: 'fileUploadAck', id: data.id });
|
||||
}
|
||||
currentFileDownload = null;
|
||||
break;
|
||||
}
|
||||
case 'fileUploadAck': {
|
||||
continueFileUpload();
|
||||
break;
|
||||
}
|
||||
case 'fileData': {
|
||||
if (currentFileDownload && (currentFileDownload.id == data.id)) {
|
||||
currentFileDownload.data += data.data;
|
||||
changeFileInfo(data.id, 2, (currentFileDownload.data.length * 200 / currentFileDownload.size));
|
||||
send({ action: 'fileUploadAck', id: data.id });
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: { console.log('Unhandled object data', data); break; }
|
||||
}
|
||||
} else {
|
||||
|
@ -249,7 +292,122 @@
|
|||
|
||||
// File sharing button
|
||||
function fileButtonClick() {
|
||||
var chooser = Q('uploadFileInput');
|
||||
if (chooser.getAttribute("eventset") != 1) {
|
||||
chooser.setAttribute("eventset", "1");
|
||||
chooser.addEventListener("change", fileSelect, false);
|
||||
}
|
||||
chooser.value = null;
|
||||
chooser.click();
|
||||
}
|
||||
|
||||
function fileSelect() {
|
||||
if (state != 2) return;
|
||||
var x = Q('uploadFileInput');
|
||||
if (x.files.length != 1) return;
|
||||
var reader = new FileReader();
|
||||
reader.onload = function (e) { this.xfile.data = e.target.result; startFileUpload(this.xfile); };
|
||||
reader.xfile = x.files[0];
|
||||
reader.readAsBinaryString(x.files[0]);
|
||||
}
|
||||
|
||||
function fileDrop(e) {
|
||||
haltEvent(e);
|
||||
if (state != 2) return;
|
||||
if (e.dataTransfer == null || e.dataTransfer.files.length != 1) return;
|
||||
var reader = new FileReader();
|
||||
reader.onload = function (e) { this.xfile.data = e.target.result; startFileUpload(this.xfile); };
|
||||
reader.xfile = e.dataTransfer.files[0];
|
||||
reader.readAsBinaryString(e.dataTransfer.files[0]);
|
||||
}
|
||||
|
||||
function startFileUpload(file) {
|
||||
if (state != 2) return;
|
||||
file.id = Math.random();
|
||||
fileUploads.push(file);
|
||||
//console.log('XXX', file);
|
||||
QA('xmsg', '<div style="clear:both"></div><div id="FILEUP-' + file.id + '" class="localBubble" style="width:240px;cursor:pointer" onclick="cancelFileTransfer(\'' + file.id + '\')"><div id="FILEUP-ICON-' + file.id + '" class="fileicon" style="float:left;width:32px;height:32px"></div><div><div id="FILEUP-NAME-' + file.id + '" style="height:16px;overflow:hidden">' + file.name + '</div><div style="width:200px;background-color:lightgray;margin-left:32px;border-radius:3px;margin-top:3px;height:11px"><div id="FILEUP-PROGRESS-' + file.id + '" style="width:0px;background-color:green;border-radius:3px;height:11px"> </div></div></div></div>');
|
||||
Q('xmsg').scrollTop = Q('xmsg').scrollHeight;
|
||||
send({ action: 'file', size: file.size, id: file.id, type: file.type, name: file.name });
|
||||
if (currentFileUpload == null) continueFileUpload();
|
||||
}
|
||||
|
||||
function startFileDownload(file) {
|
||||
if (state != 2) return;
|
||||
fileDownloads[file.id] = file;
|
||||
QA('xmsg', '<div style="clear:both"></div><div id="FILEUP-' + file.id + '" class="remoteBubble" style="width:240px;cursor:pointer"><div id="FILEUP-ICON-' + file.id + '" class="fileicon" style="float:left;width:32px;height:32px"></div><div><div id="FILEUP-NAME-' + file.id + '" style="height:16px;overflow:hidden">' + file.name + '</div><div style="width:200px;background-color:lightgray;margin-left:32px;border-radius:3px;margin-top:3px;height:11px"><div id="FILEUP-PROGRESS-' + file.id + '" style="width:0px;background-color:green;border-radius:3px;height:11px"> </div></div></div></div>');
|
||||
Q('xmsg').scrollTop = Q('xmsg').scrollHeight;
|
||||
}
|
||||
|
||||
// Change the file icon and progress
|
||||
function changeFileInfo(id, icon, progress, progressColor) {
|
||||
if (icon) {
|
||||
Q('FILEUP-ICON-' + id).classList.remove('fileicon');
|
||||
Q('FILEUP-ICON-' + id).classList.remove('fileiconx');
|
||||
Q('FILEUP-ICON-' + id).classList.remove('fileicontransfer');
|
||||
Q('FILEUP-ICON-' + id).classList.remove('fileicondone');
|
||||
Q('FILEUP-ICON-' + id).classList.add(['fileicon', 'fileiconx', 'fileicontransfer', 'fileicondone'][icon]);
|
||||
}
|
||||
if (progress) { QS('FILEUP-PROGRESS-' + id)['width'] = progress + 'px'; }
|
||||
if (progressColor) { QS('FILEUP-PROGRESS-' + id)['background-color'] = progressColor; }
|
||||
}
|
||||
|
||||
function cancelFileTransfer(id) {
|
||||
if ((currentFileUpload != null) && (currentFileUpload.id == id)) { currentFileUpload = null; }
|
||||
if ((currentFileDownload != null) && (currentFileDownload.id == id)) { currentFileDownload = null; }
|
||||
|
||||
var found = false;
|
||||
if (fileDownloads[id] && (fileDownloads[id].done != 1)) {
|
||||
delete fileDownloads[id];
|
||||
found = true;
|
||||
} else {
|
||||
for (var i in fileUploads) {
|
||||
if (fileUploads[i].id == id) {
|
||||
send({ action: 'fileUploadCancel', id: id });
|
||||
fileUploads.splice(i, 1);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found) { changeFileInfo(id, 1, 200, 'gray'); } // Only cancel a file if it was in the file queue.
|
||||
}
|
||||
|
||||
function cancelAllFileTransfers() {
|
||||
for (var i in fileDownloads) { cancelFileTransfer(fileDownloads[i].id); }
|
||||
for (var i in fileUploads) { cancelFileTransfer(fileUploads[i].id); }
|
||||
}
|
||||
|
||||
function continueFileUpload() {
|
||||
if (currentFileUpload == null) {
|
||||
// Select the next file to upload
|
||||
if (fileUploads.length == 0) { return; } // Nothing to do
|
||||
currentFileUpload = fileUploads[0];
|
||||
currentFileUpload.ptr = 0;
|
||||
|
||||
// Indicate that we are sending this file
|
||||
send({ action: 'fileUploadStart', size: currentFileUpload.size, id: currentFileUpload.id, type: currentFileUpload.type, name: currentFileUpload.name });
|
||||
} else {
|
||||
if (currentFileUpload.size <= currentFileUpload.ptr) {
|
||||
// If we are done, send the end marker
|
||||
send({ action: 'fileUploadEnd', size: currentFileUpload.size, id: currentFileUpload.id, type: currentFileUpload.type, name: currentFileUpload.name });
|
||||
changeFileInfo(currentFileUpload.id, 3, 200);
|
||||
fileUploads.splice(0, 1);
|
||||
currentFileUpload = null;
|
||||
continueFileUpload(); // Send the next file
|
||||
} else {
|
||||
// Send the next block
|
||||
var nextBlockLen = Math.min(4000, currentFileUpload.data.length - currentFileUpload.ptr);
|
||||
var data = currentFileUpload.data.substring(currentFileUpload.ptr, currentFileUpload.ptr + nextBlockLen);
|
||||
send({ action: 'fileData', id: currentFileUpload.id, data: data });
|
||||
currentFileUpload.ptr += nextBlockLen;
|
||||
changeFileInfo(currentFileUpload.id, 0, (currentFileUpload.ptr * 200 / currentFileUpload.size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function continueFileDownload(msg) {
|
||||
send({ action: 'fileUploadAck', id: msg.id });
|
||||
}
|
||||
|
||||
// Camera button
|
||||
|
@ -337,9 +495,9 @@
|
|||
socket.onclose = function () { disconnect(); }
|
||||
socket.onmessage = function (msg) {
|
||||
if ((state < 2) && (typeof msg.data == 'string') && (msg.data == 'c')) {
|
||||
hangUpButtonClick(0);
|
||||
hangUpButtonClick(1);
|
||||
hangUpButtonClick(2);
|
||||
hangUpButtonClick(0, true);
|
||||
hangUpButtonClick(1, true);
|
||||
hangUpButtonClick(2, true);
|
||||
displayControl('Connected.');
|
||||
state = 2;
|
||||
updateControls();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
.topButton {
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
margin: 3px;
|
||||
margin: 2px;
|
||||
float: right;
|
||||
border-radius: 3px;
|
||||
height: 32px;
|
||||
|
@ -12,6 +12,19 @@
|
|||
background-color: lightgray;
|
||||
}
|
||||
|
||||
.topRedButton {
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
margin: 2px;
|
||||
float: right;
|
||||
border-radius: 3px;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.topRedButton:hover {
|
||||
background-color: orangered;
|
||||
}
|
||||
|
||||
.remoteBubble {
|
||||
background-color: #00cc99;
|
||||
|
@ -87,5 +100,29 @@
|
|||
|
||||
.icon11 {
|
||||
background: url(../images/messenger32.png) -320px 0px;
|
||||
background-color: gray;
|
||||
background-color: orange;
|
||||
}
|
||||
|
||||
.fileicon {
|
||||
background: url(../images/messenger32.png) -96px 0px;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.fileiconx {
|
||||
background: url(../images/messenger32.png) -352px 0px;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.fileicontransfer {
|
||||
background: url(../images/messenger32.png) -288px 0px;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.fileicondone {
|
||||
background: url(../images/messenger32.png) -256px 0px;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
}
|
Loading…
Reference in New Issue