From 2a642f5c293cf2366db51fa0f90b9073819af8b0 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Mon, 16 Sep 2019 19:34:21 +0530 Subject: [PATCH] Added File-Share feature Users can now share files Click on the file-share button next to message input to attach file Files are encrypted and sign along with messages Note: file size is not restricted, yet its advised to share only files with size <1.5MB --- app/app.js | 178 ++++++++++++++++++++++++++++++++++++++----------- app/index.html | 43 +++++++----- app/styles.css | 153 ++++++++++-------------------------------- 3 files changed, 200 insertions(+), 174 deletions(-) diff --git a/app/app.js b/app/app.js index f79f89f..1d0a5cd 100644 --- a/app/app.js +++ b/app/app.js @@ -161,13 +161,13 @@ var floOpt = { var floID = key.getBitcoinAddress(); return floID; }, - signData: function (msg, privateKeyHex) { + signData: function (data, privateKeyHex) { var key = new Bitcoin.ECKey(privateKeyHex); key.setCompressed(true); var privateKeyArr = key.getBitcoinPrivateKeyByteArray(); privateKey = BigInteger.fromByteArrayUnsigned(privateKeyArr); - var messageHash = Crypto.SHA256(msg); + var messageHash = Crypto.SHA256(data); var messageHashBigInteger = new BigInteger(messageHash); var messageSign = Bitcoin.ECDSA.sign(messageHashBigInteger, key.priv); @@ -175,8 +175,8 @@ var floOpt = { var sighex = Crypto.util.bytesToHex(messageSign); return sighex; }, - verifyData: function (msg, signatureHex, publicKeyHex) { - var msgHash = Crypto.SHA256(msg); + verifyData: function (data, signatureHex, publicKeyHex) { + var msgHash = Crypto.SHA256(data); var messageHashBigInteger = new BigInteger(msgHash); var sigBytes = Crypto.util.hexToBytes(signatureHex); @@ -326,10 +326,19 @@ function broadcastTx(signedTxHash) { return result; } +function resetForm(formID) { + var formEl = document.getElementById(formID); + formEl.reset() + var labelSpans = formEl.querySelectorAll('span'); + for (var i = 0; i < labelSpans.length; i++) + labelSpans[i].textContent = ''; +} + function userDataStartUp() { console.log("StartUp"); - - document.getElementById("sendMsgInput").addEventListener("keydown", (event) => { + resetForm("replyForm"); + //Initiate Event Handling + document.getElementById("msgInput").addEventListener("keydown", (event) => { if (event.keyCode === 13 && !event.shiftKey) { event.preventDefault(); sendMsg(); @@ -337,6 +346,12 @@ function userDataStartUp() { }); document.getElementById("searchContact").addEventListener("input", searchContact, true); document.getElementById("searchList").addEventListener("input", searchChecklist, true); + document.getElementById('fileInput').onchange = function () { + var fileName = this.value.split("\\").pop(); + this.nextSibling.textContent = fileName; + }; + + //Start Program getDatafromAPI().then(result => { console.log(result); getContactsfromIDB().then(result => { @@ -453,7 +468,7 @@ function getDatafromAPI() { var objectStore3 = db.createObjectStore("messages", { keyPath: 'time' }); - objectStore3.createIndex('text', 'text', { + objectStore3.createIndex('msgData', 'msgData', { unique: false }); objectStore3.createIndex('floID', 'floID', { @@ -813,13 +828,13 @@ function initselfWebSocket() { function processIncomingData(data) { if (data.directMsg !== undefined) { - var msg = floOpt.decryptData(data.directMsg.msgCipher.secret, data.directMsg.msgCipher.pubVal, privKey) - if (!floOpt.verifyData(msg, data.directMsg.sign, contacts[data.from].pubKey)) + var msgData = floOpt.decryptData(data.directMsg.msgCipher.secret, data.directMsg.msgCipher.pubVal, privKey) + if (!floOpt.verifyData(msgData, data.directMsg.sign, contacts[data.from].pubKey)) return var msgInfo = { time: Date.now(), floID: data.from, - text: msg, + msgData: msgData, type: "R" } createMsgElement(msgInfo); @@ -827,14 +842,14 @@ function processIncomingData(data) { } else if (data.groupMsg !== undefined && data.groupMsg.group in groups) { if (!(groups[data.groupMsg.group].members.includes(data.from))) return - var msg = floOpt.decryptData(data.groupMsg.msgCipher.secret, data.groupMsg.msgCipher.pubVal, groups[data.groupMsg.group].privKey); - if (!floOpt.verifyData(msg, data.groupMsg.sign, contacts[data.from].pubKey)) + var msgData = floOpt.decryptData(data.groupMsg.msgCipher.secret, data.groupMsg.msgCipher.pubVal, groups[data.groupMsg.group].privKey); + if (!floOpt.verifyData(msgData, data.groupMsg.sign, contacts[data.from].pubKey)) return var msgInfo = { time: Date.now(), groupID: data.groupMsg.group, sender: data.from, - text: msg, + msgData: msgData, type: "R" } createMsgElement(msgInfo); @@ -898,28 +913,36 @@ function createMsgElement(msgInfo) { R: 'receiver' }; if (!msgInfo.groupID) { - var msgEl = document.getElementById(msgInfo.floID); + var msgCon = document.getElementById(msgInfo.floID); var msghd = ''; } else { - var msgEl = document.getElementById(msgInfo.groupID); - var msghd = `${msgInfo.sender}
`; + var msgCon = document.getElementById(msgInfo.groupID); + var msghd = `${msgInfo.sender}`; } - if (!msgEl) + if (!msgCon) return; - var msgdiv = document.createElement('div'); - msgdiv.setAttribute("class", "row message-body"); - msgdiv.innerHTML = `
+ var msgData = JSON.parse(msgInfo.msgData); + var msgEl = document.createElement('div'); + msgEl.setAttribute("class", "row message-body"); + msgEl.innerHTML = `
- - ${msghd}

+                      ${msghd}
+                        
+                        

                       
${getTime(msgInfo.time)}
`; - msgdiv.querySelector("pre").textContent = msgInfo.text; - msgEl.appendChild(msgdiv); + msgEl.querySelector("pre").textContent = msgData.text; + if (msgData.file) { + var fileEl = msgEl.querySelector("i"); + fileEl.textContent = ` ${msgData.file.name} (${getFileSize(msgData.file.size)})`; + fileEl.setAttribute("onclick", `downloadFile(${msgInfo.time})`); + fileEl.setAttribute("class", "fa fa-arrow-circle-down"); + } + msgCon.appendChild(msgEl); } catch (e) { console.log(e); } @@ -1057,30 +1080,83 @@ function getTime(time) { return tmp; } +function getFileSize(size){ + var filesizeUnits = ['B','kB','MB','GB','TB']; + for(var i = 0;i { + console.log(msgData); + var time = Date.now(); + var sign = floOpt.signData(msgData, privKey); + if (msgType === 'direct') + sendDirectMsg(msgData, time, sign); + else if (msgType === 'group') + sendGroupMsg(msgData, time, sign); + }).catch(error => { + console.log(error); + }) } -function sendDirectMsg(msg, time, sign) { +function getfileData(fileInput) { + return new Promise((resolve, reject) => { + try { + var files = document.getElementById(fileInput).files; + if (files.length == 0) + resolve(null); + else { + var reader = new FileReader(); + reader.onload = (event) => { + var fileBytes = Crypto.charenc.Binary.stringToBytes(event.target.result); + resolve({ + name: files[0].name, + size: files[0].size, + content: Crypto.util.bytesToBase64(fileBytes) + }); + }; + reader.onerror = (event) => { + reject("File could not be read! Code " + event.target.error.code); + }; + reader.readAsBinaryString(files[0]); + } + } catch (e) { + reject(e); + } + }); +} + +function getReplyInputs() { + return new Promise((resolve, reject) => { + getfileData('fileInput').then(fileData => { + var msgData = { + text: document.getElementById('msgInput').value, + file: fileData + }; + resetForm('replyForm'); + resolve(JSON.stringify(msgData)); + }).catch(error => { + reject(error); + }); + }); +} + +function sendDirectMsg(msgData, time, sign) { var data = JSON.stringify({ from: selfID, to: receiverID, directMsg: { time: time, - msgCipher: floOpt.encryptData(msg, contacts[receiverID].pubKey), + msgCipher: floOpt.encryptData(msgData, contacts[receiverID].pubKey), sign: sign } }); @@ -1092,20 +1168,20 @@ function sendDirectMsg(msg, time, sign) { var msgInfo = { time: time, floID: receiverID, - text: msg, + msgData: msgData, type: "S" } createMsgElement(msgInfo); storeMsg(msgInfo); } -function sendGroupMsg(msg, time, sign) { +function sendGroupMsg(msgData, time, sign) { var data = { from: selfID, groupMsg: { group: receiverID, time: time, - msgCipher: floOpt.encryptData(msg, groups[receiverID].pubKey), + msgCipher: floOpt.encryptData(msgData, groups[receiverID].pubKey), sign: sign } }; @@ -1121,7 +1197,7 @@ function sendGroupMsg(msg, time, sign) { time: time, sender: selfID, groupID: receiverID, - text: msg, + msgData: msgData, type: "S" } createMsgElement(msgInfo); @@ -1468,6 +1544,7 @@ function customCheckList(userList, ignoreList, okBtnVal = "Ok", okBtnType = "suc grpNameInput.style.display = (okBtnVal === "Create Group" ? "block" : "none"); grpNameInput.value = ''; var userChecklist = document.getElementById('userChecklist'); + userChecklist.innerHTML = ''; for (var i = 0; i < userList.length; i++) { if (ignoreList.includes(userList[i])) continue; @@ -1528,4 +1605,25 @@ function searchChecklist() { } catch (e) { console.log(e); } +} + +function downloadFile(msgID) { + var idb = indexedDB.open("FLO_Chat"); + idb.onerror = (event) => { + console.log("Error in opening IndexedDB!"); + }; + idb.onsuccess = (event) => { + var db = event.target.result; + var msgReq = db.transaction('messages', "readwrite").objectStore('messages').get(msgID); + msgReq.onsuccess = (event) => { + var file = JSON.parse(event.target.result.msgData).file; + var tmpEl = document.createElement('a'); + tmpEl.setAttribute('href', 'data:application/octet-stream;charset=utf-8;base64,' + file.content); + tmpEl.setAttribute('download', file.name); + tmpEl.style.display = 'none'; + document.body.appendChild(tmpEl); + tmpEl.click(); + document.body.removeChild(tmpEl); + } + } } \ No newline at end of file diff --git a/app/index.html b/app/index.html index 4312e62..92b984e 100644 --- a/app/index.html +++ b/app/index.html @@ -105,23 +105,32 @@
- -
- -
- -
- -
+
+ + +
+ +
+
+ +
+ +
+ +
+
diff --git a/app/styles.css b/app/styles.css index 7564d93..6d529ad 100644 --- a/app/styles.css +++ b/app/styles.css @@ -89,7 +89,8 @@ body { height: 60px; width: 100%; background-color: #eee; - z-index: 1000; + z-index: 1; + overflow: visible; } .heading-name { @@ -174,17 +175,6 @@ body { background-color: #f2f2f2; } -.sideBar-avatar { - text-align: center; - padding: 0 !important; -} - -.avatar-icon img { - border-radius: 50%; - height: 49px; - width: 49px; -} - .sideBar-main { padding: 0 !important; } @@ -321,7 +311,7 @@ body { } .message-body { - margin: 0 !important; + margin: 5px 0px !important; padding: 0 !important; width: auto; height: auto; @@ -349,6 +339,15 @@ body { word-break: break-word; } +.message-text i { + border: 0.5px solid rgba(0, 0, 0, 0.3); + border-radius: 5px; + background-color: rgba(0, 0, 0, 0.1); + padding: 5px; + display: block; + cursor: pointer; +} + .message-time { margin: 0 !important; font-size: 12px; @@ -360,7 +359,7 @@ body { .receiver { float: left; width: auto !important; - padding: 4px 10px 7px !important; + padding: 5px 10px 2px !important; border-radius: 10px 10px 10px 0; background: #ffffff; font-size: 12px; @@ -373,7 +372,7 @@ body { height: auto; background: #dcf8c6; border-radius: 10px 10px 0 10px; - padding: 4px 10px 7px !important; + padding: 5px 10px 2px !important; font-size: 12px; } @@ -390,6 +389,7 @@ body { .reply-icon { padding: 5px !important; + overflow: visible; } .reply-icon i { @@ -414,7 +414,6 @@ body { box-shadow: none; height: 100%; font-size: 16px; - overflow: hidden; } .reply-main textarea:focus { @@ -424,103 +423,6 @@ body { box-shadow: none; } -@media screen and (max-width: 700px) { - .app { - top: 0; - height: 100%; - } - - .heading { - height: 70px; - background-color: #009688; - } - - .fa-2x { - font-size: 2.3em !important; - } - - .heading-icon { - padding: 5px !important; - } - - .heading-icon i { - color: #fff; - cursor: pointer; - } - - .sideBar { - height: calc(100% - 130px); - } - - .sideBar-body { - height: 80px; - } - - .sideBar-main { - padding: 0 !important; - } - - .sideBar-main .row { - padding: 0 !important; - margin: 0 !important; - } - - .sideBar-name { - padding: 10px 5px !important; - } - - .name-meta { - font-size: 16px; - padding: 5% !important; - } - - .sideBar-time { - padding: 10px !important; - } - - .time-meta { - text-align: right; - font-size: 14px; - padding: 4% !important; - color: rgba(0, 0, 0, .4); - vertical-align: baseline; - } - - /*Conversation*/ - .conversation { - padding: 0 !important; - margin: 0 !important; - height: 100%; - border-left: 1px solid rgba(0, 0, 0, .08); - } - - .message { - height: calc(100% - 140px); - } - - .reply { - height: 70px; - } - - .reply-icon { - padding: 5px 0 !important; - } - - .reply-icon i { - padding: 5px 2px !important; - font-size: 1.8em !important; - } - - .reply-main { - padding: 2px 8px !important; - } - - .reply-main input { - padding: 8px !important; - font-size: 18px; - } -} - .badgebox { opacity: 0; } @@ -562,15 +464,17 @@ body { .userChecklist-container { margin-top: 25px; - width: 300px; - height: 400px; + padding: 5px; + width: 325px; + height: auto; background-color: #eee; display: inline-block; + } #userChecklist { height: 275px; - overflow-y: auto; + overflow-y: scroll; } .dropdown-content { @@ -579,7 +483,8 @@ body { min-width: 150px; z-index: 1; cursor: pointer; - margin-left: -75px; + margin-left: -100px; + overflow: inherit; } .dropdown-content li { @@ -601,4 +506,18 @@ body { #groupOptions { display: none; + overflow: visible; +} + +input.attach-file { + display: none; +} + +input.attach-file+span { + color: #93918f; + font-size: 10px; +} + +.hidden { + display: none; } \ No newline at end of file