From 18cb507552fe781b5201e129ae2fed041dceec91 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Wed, 2 Dec 2020 04:53:58 +0530 Subject: [PATCH 1/7] Change to new cloud and group feature - Made required changes to migrate to new cloud support. - Added group feature Users can create groups and send/receive messages via groupID --- index.html | 9730 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 9571 insertions(+), 159 deletions(-) diff --git a/index.html b/index.html index 86496cc..a506ac9 100644 --- a/index.html +++ b/index.html @@ -23,11 +23,12 @@ }, //Required for Supernode operations - SNStorageID: "FEzk75EGMPEQMrCuPosGiwuK162hcEu49E", + SNStorageID: "FNaN9McoBAEFUjkRmNQRYLmBF8SpS7Tgfk", supernodes: {}, //each supnernode must be stored as floID : {uri:,pubKey:} //for app - application: "messenger", + application: "messenger", + adminID: "FMRsefPydWznGWneLqi4ABeQAJeFvtS3aQ", pubKeys: {}, contacts: {}, appendix: {} @@ -78,30 +79,33 @@ } }) }) - + //invoke the startup functions floDapps.launchStartUp().then(result => { console.log(result) document.getElementById("greet_tag").textContent = myFloID - //load messages from IDB and render them - reactor.dispatchEvent("startUpSuccessLog", `Loading Data! Please Wait...`) - messenger.loadDataFromIDB().then(data => { - floGlobals.pubKeys = data.pubKeys; - floGlobals.contacts = data.contacts; - floGlobals.settings = data.settings; + //load messages from IDB and render them + console.log(`Loading Data! Please Wait...`) + messenger.initUserDB().then(result => { + console.log(result) + messenger.loadDataFromIDB().then(data => { + console.log(data) floGlobals.appendix = data.appendix; renderContactList(floGlobals.contacts) renderMailContactList(floGlobals.contacts) renderMessages(data.messages, false) renderMailList(data.mails, false) renderMarked(data.marked) - reactor.dispatchEvent("startUpSuccessLog", `Load Successful!`) + messenger.refreshInbox(renderLiveUI) + .then(rqID => console.log("reqID:", rqID)) + .catch(e => console.error("request error:", e)) + console.log(`Load Successful!`) //hide loading screen loadingPage.classList.add("hide-completely") mainPage.classList.remove('hide-completely') chatSection.classList.add('hide-completely') mailSection.classList.add('hide-completely') - refreshAgain() + //refreshAgain() document.getElementById('chat_page_button').click() if(privKeyNotSecured){ notify("Private key is not secured with password. Remember to secure the private key in settings", "warn", '') @@ -111,9 +115,11 @@ document.getElementById("secure_key").textContent = 'Change password' } }).catch(error => { - reactor.dispatchEvent("startUpErrorLog", `Failed to load data`) + //console.error(`Failed to load data`) notify(error, "error") }) + }) + }).catch(error => notify(error, "error")) } @@ -776,6 +782,20 @@ let currentDate, currentFloID + function renderLiveUI(data) { + console.log(data) + renderMessages(data.messages); + renderMailList(data.mails) + if(Object.keys(data.messages).length && activePage.button !== chatPageButton){ + chatPageButton.setAttribute('data-notifications', Object.keys(data.messages).length) + notify(`${Object.keys(data.messages).length} new message(s)`) + } + if(Object.keys(data.mails).length && activePage.button !== mailPageButton){ + mailPageButton.setAttribute('data-notifications', Object.keys(data.mails).length) + notify(`${Object.keys(data.mails).length} new mail(s)`) + } + } + function randomHsl(saturation = 90, lightness = 50) { let hue = Math.random() * 360 let color = { @@ -1192,6 +1212,7 @@ let refreshing = false function refresh(){ + return if(refreshing) return; refreshing = true messenger.refreshInbox().then(data => { @@ -1405,7 +1426,6 @@ function renderMessages(data, markUnread = true) { let floID - console.log(data) for (m in data) { floID = data[m].floID render.messageBubble(data[m].floID, data[m].message, data[m].time, data[m].category) @@ -1549,85 +1569,9289 @@ } - + + + + + + + + From 24d00fa3447677474ad7aa13815f1eab84b1ff28 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Mon, 7 Dec 2020 03:26:48 +0530 Subject: [PATCH 2/7] additional group feature and bugfixes - Added: description feature to group - Added feature to change name and description of the group. - Added: rmGroupMembers to kick group members - fixed minor bugs --- index.html | 395 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 231 insertions(+), 164 deletions(-) diff --git a/index.html b/index.html index a506ac9..38116f3 100644 --- a/index.html +++ b/index.html @@ -35,93 +35,97 @@ } @@ -796,6 +800,10 @@ } } + function renderGroupLiveUI(data){ + console.log(data) + } + function randomHsl(saturation = 90, lightness = 50) { let hue = Math.random() * 360 let color = { @@ -1306,11 +1314,12 @@ typeMessage.value = '' if(message.trim() === '') return messenger.sendMessage(message, receiver).then(data => { - render.messageBubble(data.floID, data.message, data.time, data.category) - chatContainer.scrollTo({left: 0, top: chatContainer.scrollHeight, behavior: 'smooth'}) - refreshCount = 0; - refreshTimeout = 10000; - setTimeout(refreshAgain, refreshTimeout) + renderMessages(data, false) + //render.messageBubble(data.floID, data.message, data.time, data.category) + //chatContainer.scrollTo({left: 0, top: chatContainer.scrollHeight, behavior: 'smooth'}) + //refreshCount = 0; + //refreshTimeout = 10000; + //setTimeout(refreshAgain, refreshTimeout) }).catch(error => notify(error, "error")); } @@ -1426,7 +1435,7 @@ function renderMessages(data, markUnread = true) { let floID - for (m in data) { + for (let m in data) { floID = data[m].floID render.messageBubble(data[m].floID, data[m].message, data[m].time, data[m].category) if (markUnread) @@ -1445,7 +1454,7 @@ textColor = contact.getAttribute('text-color'), backgroundColor = contact.getAttribute('background-color') let currentConversation = document.getElementById("receiver_floID").textContent; - console.log(floID, floGlobals.contacts[floID], currentConversation) + console.log(floID, floGlobals.contacts[floID]) if (currentConversation != "") document.getElementById(currentConversation).classList.add('hide-completely') let currentChat = document.getElementById(floID) @@ -9597,7 +9606,7 @@ Bitcoin.Util = { }, liveRequest: function (floID, datareq, callback) { - request = { + let request = { ...datareq.request }; const checkFilter = (v, d, r) => @@ -10935,6 +10944,7 @@ Bitcoin.Util = { newgroups: [], keyrevoke: [] } + console.log(dataSet) for (let vc in dataSet) { try { //store the pubKey if not stored already @@ -10982,13 +10992,16 @@ Bitcoin.Util = { floCrypto.verifySign(h, groupInfo.hash, groupInfo.pubKey) && floCrypto.getFloID(groupInfo.pubKey) === groupInfo.groupID) { floGlobals.groups[groupInfo.groupID] = groupInfo - compactIDB.addData("groupInfo", groupInfo, groupInfo.groupID) + compactIDB.writeData("groupInfo", groupInfo, groupInfo.groupID) newInbox.newgroups.push(groupInfo.groupID) } } else if (dataSet[vc].type === "REVOKE_KEY") { let r = JSON.parse(dataSet[vc].message); let groupInfo = floGlobals.groups[r.groupID] if (dataSet[vc].senderID === groupInfo.admin) { + if(typeof floGlobals.expiredKeys[r.groupID] !== "object") + floGlobals.expiredKeys[r.groupID] = {} + floGlobals.expiredKeys[r.groupID][vc] = groupInfo.eKey groupInfo.eKey = r.newKey; compactIDB.writeData("groupInfo", groupInfo, groupInfo.groupID) newInbox.keyrevoke.push(groupInfo.groupID) @@ -11197,7 +11210,7 @@ Bitcoin.Util = { //group feature - createGroup(groupname) { + createGroup(groupname, description = '') { return new Promise((resolve, reject) => { if (!groupname) return reject("Invalid Group Name") let id = floCrypto.generateNewID(); @@ -11206,6 +11219,7 @@ Bitcoin.Util = { pubKey: id.pubKey, admin: myFloID, name: groupname, + description: description, created: Date.now(), members: [myFloID], eKey: floCrypto.randString(16, false) @@ -11222,7 +11236,31 @@ Bitcoin.Util = { }) }, - addGroupMembers(groupID, newMem = [], note = undefined) { + changeGroupName(groupID, description) { + return new Promise((resolve, reject) => { + let groupInfo = floGlobals.groups[groupID] + if (myFloID !== groupInfo.admin) + return reject("Access denied: Admin only!") + let message = this.util.encrypt(description, groupInfo.eKey) + this.util.sendRaw(message, groupID, "UP_NAME", false) + .then(result => resolve('Description updated')) + .catch(error => reject(error)) + }) + }, + + changeGroupDescription(groupID, name) { + return new Promise((resolve, reject) => { + let groupInfo = floGlobals.groups[groupID] + if (myFloID !== groupInfo.admin) + return reject("Access denied: Admin only!") + let message = this.util.encrypt(name, groupInfo.eKey) + this.util.sendRaw(message, groupID, "UP_DESCRIPTION", false) + .then(result => resolve('Description updated')) + .catch(error => reject(error)) + }) + }, + + addGroupMembers(groupID, newMem, note = undefined) { return new Promise((resolve, reject) => { if (!Array.isArray(newMem) && typeof newMem === "string") newMem = [newMem] @@ -11238,40 +11276,60 @@ Bitcoin.Util = { else if (imem2.length) return reject(`Invalid Members (pubKey not available): ${imem2}`) //send new newMem list to existing members - let message = this.util.encrypt(newMem.join("|"), key) - this.util.sendRaw(message, groupID, "ADD_MEMBERS", false, note).then(r => { - let groupInfo = floGlobals.groups[groupID] - groupInfo.members = [...new Set(groupInfo.members.concat(newMem))] - //send groupInfo to new newMem - groupInfo = JSON.stringify(groupInfo) - let promises = newMem.map(m => this.util.sendRaw(groupInfo, m, "CREATE_GROUP", - true)) - Promise.allSettled(promises).then(results => { - let success = [], - failed = []; - for (let i in results) - if (results[i].status === "fulfilled") - success.push(newMem[i]) - else if (results[i].status === "rejected") - failed.push(newMem[i]) - resolve({ - success, - failed - }) - }) - }).catch(e => reject(e)) + let groupInfo = floGlobals.groups[groupID] + if (myFloID !== groupInfo.admin) + return reject("Access denied: Admin only!") + //send groupInfo to new newMem + let k = groupInfo.eKey + groupInfo = JSON.stringify(groupInfo) + let promises = newMem.map(m => this.util.sendRaw(groupInfo, m, "CREATE_GROUP", true)) + Promise.allSettled(promises).then(results => { + let success = [], + failed = []; + for (let i in results) + if (results[i].status === "fulfilled") + success.push(newMem[i]) + else if (results[i].status === "rejected") + failed.push(newMem[i]) + console.log(success.join("|"), k) + let message = this.util.encrypt(success.join("|"), k) + this.util.sendRaw(message, groupID, "ADD_MEMBERS", false, note) + .then(r => resolve(`Members added: ${success}`)) + .catch(e => reject(e)) + }) + }) + }, + + rmGroupMembers(groupID, rmMem, note = undefined) { + return new Promise((resolve, reject) => { + if (!Array.isArray(rmMem) && typeof rmMem === "string") + rmMem = [rmMem] + let groupInfo = floGlobals.groups[groupID] + let imem = rmMem.filter(m => !groupInfo.members.includes(m)) + if (imem.length) + return reject(`Invalid members: ${imem}`) + if (myFloID !== groupInfo.admin) + return reject("Access denied: Admin only!") + let message = this.util.encrypt(rmMem.join("|"), groupInfo.eKey) + p1 = this.util.sendRaw(message, groupID, "RM_MEMBERS", false, note) + groupInfo.members = groupInfo.members.filter(m => !rmMem.includes(m)) + p2 = this.revokeKey(groupID) + Promise.all([p1, p2]) + .then(r => resolve(`Members removed: ${rmMem}`)) + .catch(e => reject(e)) }) }, revokeKey(groupID) { return new Promise((resolve, reject) => { let groupInfo = floGlobals.groups[groupID] + if (myFloID !== groupInfo.admin) + return reject("Access denied: Admin only!") let newKey = floCrypto.randString(16, false); Promise.all(groupInfo.members.map(m => this.util.sendRaw(JSON.stringify({ newKey, groupID }), m, "REVOKE_KEY", true))).then(result => { - floGlobals.groups[groupID].eKey = newKey; resolve("Group key revoked") }).catch(error => reject(error)) }) @@ -11279,41 +11337,28 @@ Bitcoin.Util = { sendGroupMessage(message, groupID) { return new Promise(async (resolve, reject) => { - let k = floGlobals.groups[receiverID].eKey + let k = floGlobals.groups[groupID].eKey message = this.util.encrypt(message, k) - this.util.sendRaw(message, groupID, "GROUP_MSG", false).then(result => { - let vc = Object.keys(result).pop() - let data = { - groupID: groupID, - sender: myFloID, - time: result[vc].time, - category: "sent", - message: this.util.encrypt(message) - } - compactIDB.addData("groupMsg", { - ...data - }, vc) - data.message = message; - resolve({ - [vc]: data - }); - }).catch(error => reject(error)) + this.util.sendRaw(message, groupID, "GROUP_MSG", false) + .then(result => resolve(`${groupID}: ${message}`)) + .catch(error => reject(error)) }) }, refreshGroupInbox(UIcallback) { return new Promise((resolve, reject) => { - let callback = function (dataSet, error) { - //<< ex[0]) ex.shift() + if(ex.length) + var k = floGlobals.expiredKeys[groupID][ex.shift()] + else + var k = floGlobals.groups[data.groupID].eKey + dataSet[vc].message = messenger.util.decrypt(dataSet[vc].message, k) //store the pubKey if not stored already floDapps.storePubKey(dataSet[vc].senderID, dataSet[vc].pubKey) - let k = floGlobals.groups[data.groupID].eKey - let msg = messenger.util.decrypt(dataSet[vc].message, k) - if (dataSet[vc].type === "GROUP_MSG") { + if (dataSet[vc].type === "GROUP_MSG") data.message = messenger.util.encrypt(dataSet[vc].message); - data.category = "received" - } else if (dataSet[vc].type === "ADD_MEMBERS") { - data.newMembers = dataSet[vc].message.split("|") - data.note = dataSet[vc].comment + else if (data.sender === floGlobals.groups[groupID].admin) { let groupInfo = floGlobals.groups[groupID] - groupInfo.members = [...new Set(groupInfo.members.concat(data - .newMembers))] - memberchange = true; + data.admin = true; + if (dataSet[vc].type === "ADD_MEMBERS") { + data.newMembers = dataSet[vc].message.split("|") + data.note = dataSet[vc].comment + groupInfo.members = [...new Set(groupInfo.members.concat(data + .newMembers))] + } else if (dataSet[vc].type === "RM_MEMBERS") { + data.rmMembers = dataSet[vc].message.split("|") + data.note = dataSet[vc].comment + groupInfo.members = groupInfo.members.filter(m => !data.rmMembers + .includes(m)) + if (data.rmMembers.includes(myFloID)) + groupInfo.status = false + } else if (dataSet[vc].type === "UP_DESCRIPTION") { + data.description = dataSet[vc].message + groupInfo.description = data.description + } else if (dataSet[vc].type === "UP_NAME") { + data.name = dataSet[vc].message + groupInfo.name = data.name + } + infoChange = true; } compactIDB.addData("groupMsg", { ...data @@ -11343,16 +11408,16 @@ Bitcoin.Util = { data.message = messenger.util.decrypt(data.message); newInbox.messages[vc] = data; messenger.addMark(data.groupID, "unread") + if (!floGlobals.appendix[`lastReceived_${groupID}`] || + floGlobals.appendix[`lastReceived_${groupID}`] < vc) + floGlobals.appendix[`lastReceived_${groupID}`] = vc; } catch (error) { console.log(error) - } finally { - if (floGlobals.appendix[`lastReceived_${groupID}`] < vc) - floGlobals.appendix[`lastReceived_${groupID}`] = vc; } } compactIDB.writeData("appendix", floGlobals.appendix[`lastReceived_${groupID}`], `lastReceived_${groupID}`); - if (memberchange) + if (infoChange) compactIDB.writeData("groupInfo", floGlobals.groups[groupID], groupID) UIcallback(newInbox) } @@ -11368,13 +11433,15 @@ Bitcoin.Util = { }) }) }) - for (let groupID in floGlobal.groups) { - var options = { - receiverID: groupID, - lowerVectorClock: floGlobals.appendix[`lastReceived_${groupID}`] + 1, - callback: callbackFn + for (let groupID in floGlobals.groups) { + if (floGlobals.groups[groupID].status !== false) { + var options = { + receiverID: groupID, + lowerVectorClock: floGlobals.appendix[`lastReceived_${groupID}`] + 1, + callback: callbackFn + } + promises.push(reqFn(options)) } - promises.push(reqFn(options)) } Promise.all(promises).then(result => { let success = {}, @@ -11384,7 +11451,7 @@ Bitcoin.Util = { success, failed }) - }).catch(error => reject(error)) + }) }) } } From 7bfc1d1842de6d9c5f923c353beddfee60647927 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Wed, 9 Dec 2020 03:02:00 +0530 Subject: [PATCH 3/7] Improvements - Improved group callback function - automatically adds a live request connection to newly created groups. --- index.html | 244 ++++++++++++++++++++++++++++------------------------- 1 file changed, 130 insertions(+), 114 deletions(-) diff --git a/index.html b/index.html index 38116f3..1916b60 100644 --- a/index.html +++ b/index.html @@ -97,12 +97,12 @@ renderMessages(data.messages, false) renderMailList(data.mails, false) renderMarked(data.marked) - messenger.refreshInbox(renderLiveUI) - .then(rqID => console.log("reqID:", rqID)) - .catch(e => console.error("request error:", e)) - messenger.refreshGroupInbox(renderGroupLiveUI) + messenger.setUIcallbacks(renderDirectUI, renderGroupUI) + messenger.refreshInbox() + .then(r => console.log("DirectConn:", r)) + .catch(e => console.error("Request error:", e)) + messenger.refreshGroupInbox() .then(r => console.log(r)) - .catch(e => console.error(e)) console.log(`Load Successful!`) //hide loading screen loadingPage.classList.add("hide-completely") @@ -786,7 +786,7 @@ let currentDate, currentFloID - function renderLiveUI(data) { + function renderDirectUI(data) { console.log(data) renderMessages(data.messages); renderMailList(data.mails) @@ -800,7 +800,7 @@ } } - function renderGroupLiveUI(data){ + function renderGroupUI(data){ console.log(data) } @@ -10821,9 +10821,103 @@ Bitcoin.Util = { decrypt(value, key = floGlobals.appendix.AESKey) { return Crypto.AES.decrypt(value, key) + }, + + UIcallback: { + group: (d, e) => console.log(d, e), + direct: (d, e) => console.log(d, e) + }, + + groupConn(groupID) { + let callbackFn = function (dataSet, error) { + if (error) + return console.error(error) + console.info(dataSet) + let newInbox = { + messages: {} + } + let infoChange = false; + for (let vc in dataSet) { + if(groupID !== dataSet[vc].receiverID || + !floGlobals.groups[groupID].members.includes(dataSet[vc].senderID)) + continue; + try { + let data = { + time: dataSet[vc].time, + sender: dataSet[vc].senderID, + groupID: dataSet[vc].receiverID + } + let k = floGlobals.groups[groupID].eKey; + if(floGlobals.expiredKeys[groupID]){ + var ex = Object.keys(floGlobals.expiredKeys[groupID]).sort() + while(ex.lenght && vc > ex[0]) ex.shift() + if(ex.length) + k = floGlobals.expiredKeys[groupID][ex.shift()] + } + dataSet[vc].message = messenger.util.decrypt(dataSet[vc].message, k) + //store the pubKey if not stored already + floDapps.storePubKey(dataSet[vc].senderID, dataSet[vc].pubKey) + if (dataSet[vc].type === "GROUP_MSG") + data.message = messenger.util.encrypt(dataSet[vc].message); + else if (data.sender === floGlobals.groups[groupID].admin) { + let groupInfo = floGlobals.groups[groupID] + data.admin = true; + if (dataSet[vc].type === "ADD_MEMBERS") { + data.newMembers = dataSet[vc].message.split("|") + data.note = dataSet[vc].comment + groupInfo.members = [...new Set(groupInfo.members.concat(data + .newMembers))] + } else if (dataSet[vc].type === "RM_MEMBERS") { + data.rmMembers = dataSet[vc].message.split("|") + data.note = dataSet[vc].comment + groupInfo.members = groupInfo.members.filter(m => !data.rmMembers + .includes(m)) + if (data.rmMembers.includes(myFloID)) + groupInfo.status = false + } else if (dataSet[vc].type === "UP_DESCRIPTION") { + data.description = dataSet[vc].message + groupInfo.description = data.description + } else if (dataSet[vc].type === "UP_NAME") { + data.name = dataSet[vc].message + groupInfo.name = data.name + } + infoChange = true; + } + compactIDB.addData("groupMsg", { + ...data + }, vc) + if (data.message) + data.message = messenger.util.decrypt(data.message); + newInbox.messages[vc] = data; + messenger.addMark(data.groupID, "unread") + if (!floGlobals.appendix[`lastReceived_${groupID}`] || + floGlobals.appendix[`lastReceived_${groupID}`] < vc) + floGlobals.appendix[`lastReceived_${groupID}`] = vc; + } catch (error) { + console.log(error) + } + } + compactIDB.writeData("appendix", floGlobals.appendix[`lastReceived_${groupID}`], + `lastReceived_${groupID}`); + if (infoChange) + compactIDB.writeData("groupInfo", floGlobals.groups[groupID], groupID) + this.UIcallback["group"](newInbox) + } + return floCloudAPI.requestApplicationData(null, { + receiverID: groupID, + lowerVectorClock: floGlobals.appendix[`lastReceived_${groupID}`] + 1, + callback: callbackFn + }) } }, + setUIcallbacks(directUI = null, groupUI = null){ + if(direct instanceof Function) + this.util.UIcallback["direct"] = directUI + if(groupUI instanceof Function) + this.util.UIcallback["group"] = groupUI + }, + initUserDB() { return new Promise((resolve, reject) => { var obj = { @@ -10933,7 +11027,7 @@ Bitcoin.Util = { }) }, - refreshInbox(UIcallback) { + refreshInbox() { return new Promise((resolve, reject) => { let callbackFn = function (dataSet, error) { if (error) @@ -10983,7 +11077,7 @@ Bitcoin.Util = { compactIDB.addData("mailRef", vc, data.ref) data.content = dataSet[vc].message.content; newInbox.mails[vc] = data; - this.addMark(data.ref, "unread") + messenger.addMark(data.ref, "unread") } else if (dataSet[vc].type === "CREATE_GROUP") { //process create group let groupInfo = JSON.parse(dataSet[vc].message); @@ -10993,6 +11087,7 @@ Bitcoin.Util = { floCrypto.getFloID(groupInfo.pubKey) === groupInfo.groupID) { floGlobals.groups[groupInfo.groupID] = groupInfo compactIDB.writeData("groupInfo", groupInfo, groupInfo.groupID) + messenger.util.groupConn(groupInfo.groupID) newInbox.newgroups.push(groupInfo.groupID) } } else if (dataSet[vc].type === "REVOKE_KEY") { @@ -11015,7 +11110,7 @@ Bitcoin.Util = { } } compactIDB.writeData("appendix", floGlobals.appendix.lastReceived, "lastReceived"); - UIcallback(newInbox) + this.util.UIcallback["direct"](newInbox) } var options = { @@ -11207,7 +11302,6 @@ Bitcoin.Util = { }) }, - //group feature createGroup(groupname, description = '') { @@ -11231,6 +11325,7 @@ Bitcoin.Util = { p2 = compactIDB.addData("groupKey", this.util.encrypt(id.privKey), id.floID) Promise.all([p1, p2]).then(r => { floGlobals.groups[id.floID] = groupInfo; + this.util.groupConn(id.floID) resolve(groupInfo) }).catch(e => reject(e)) }) @@ -11345,115 +11440,36 @@ Bitcoin.Util = { }) }, - refreshGroupInbox(UIcallback) { - return new Promise((resolve, reject) => { - let callbackFn = function (dataSet, error) { - if (error) - return console.error(error) - console.info(dataSet) - let newInbox = { - messages: {} - } - let groupID = null, - infoChange = false; - for (let vc in dataSet) { - if(!floGlobals.groups[dataSet[vc].receiverID].members.includes(dataSet[vc].senderID)) - continue; - try { - let data = { - time: dataSet[vc].time, - sender: dataSet[vc].senderID, - groupID: dataSet[vc].receiverID - } - groupID = data.groupID - let ex = Object.keys(floGlobals.expiredKeys[groupID]).sort() - while(ex.lenght && vc > ex[0]) ex.shift() - if(ex.length) - var k = floGlobals.expiredKeys[groupID][ex.shift()] - else - var k = floGlobals.groups[data.groupID].eKey - dataSet[vc].message = messenger.util.decrypt(dataSet[vc].message, k) - //store the pubKey if not stored already - floDapps.storePubKey(dataSet[vc].senderID, dataSet[vc].pubKey) - if (dataSet[vc].type === "GROUP_MSG") - data.message = messenger.util.encrypt(dataSet[vc].message); - else if (data.sender === floGlobals.groups[groupID].admin) { - let groupInfo = floGlobals.groups[groupID] - data.admin = true; - if (dataSet[vc].type === "ADD_MEMBERS") { - data.newMembers = dataSet[vc].message.split("|") - data.note = dataSet[vc].comment - groupInfo.members = [...new Set(groupInfo.members.concat(data - .newMembers))] - } else if (dataSet[vc].type === "RM_MEMBERS") { - data.rmMembers = dataSet[vc].message.split("|") - data.note = dataSet[vc].comment - groupInfo.members = groupInfo.members.filter(m => !data.rmMembers - .includes(m)) - if (data.rmMembers.includes(myFloID)) - groupInfo.status = false - } else if (dataSet[vc].type === "UP_DESCRIPTION") { - data.description = dataSet[vc].message - groupInfo.description = data.description - } else if (dataSet[vc].type === "UP_NAME") { - data.name = dataSet[vc].message - groupInfo.name = data.name - } - infoChange = true; - } - compactIDB.addData("groupMsg", { - ...data - }, vc) - if (data.message) - data.message = messenger.util.decrypt(data.message); - newInbox.messages[vc] = data; - messenger.addMark(data.groupID, "unread") - if (!floGlobals.appendix[`lastReceived_${groupID}`] || - floGlobals.appendix[`lastReceived_${groupID}`] < vc) - floGlobals.appendix[`lastReceived_${groupID}`] = vc; - } catch (error) { - console.log(error) - } - } - compactIDB.writeData("appendix", floGlobals.appendix[`lastReceived_${groupID}`], - `lastReceived_${groupID}`); - if (infoChange) - compactIDB.writeData("groupInfo", floGlobals.groups[groupID], groupID) - UIcallback(newInbox) - } + refreshGroupInbox() { + return new Promise((resolve) => { let promises = [] - let reqFn = (op) => new Promise(res => { - let s = f = null; - floCloudAPI.requestApplicationData(null, op) - .then(r => s = r).catch(e => f = e).finally(_ => { - res({ - groupID: op.receiverID, - status: s ? true : false, - feedback: s || f - }) - }) + let reqFn = (g) => new Promise((res, rej) => { + this.util.groupConn(g) + .then(r => res([g, r])) + .catch(e => rej([g, e])) }) - for (let groupID in floGlobals.groups) { - if (floGlobals.groups[groupID].status !== false) { - var options = { - receiverID: groupID, - lowerVectorClock: floGlobals.appendix[`lastReceived_${groupID}`] + 1, - callback: callbackFn - } - promises.push(reqFn(options)) - } - } - Promise.all(promises).then(result => { - let success = {}, - failed = {} - result.forEach(r => (r.status ? success : failed)[r.groupID] = r.feedback) - resolve({ - success, - failed + for (let g in floGlobals.groups) + if (floGlobals.groups[g].status !== false) + promises.push(reqFn(g)) + Promise.allSettled(promises).then(result => { + let ret = {}; + result.forEach(r => { + if(r.status === 'fulfilled') + ret[r.value[0]] = { + status = r.status, + value = r.value[1] + } + else if(r.status === "rejected") + ret[r.reason[0]] = { + status = r.status, + reason = r.reason[1] + } }) + resolve(ret) }) }) } + } From e12711bb3afdc5ced91ee8bbeb499d0107017a55 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Wed, 9 Dec 2020 03:59:46 +0530 Subject: [PATCH 4/7] bug fix --- index.html | 58 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/index.html b/index.html index 1916b60..8d36bbb 100644 --- a/index.html +++ b/index.html @@ -9628,15 +9628,18 @@ Bitcoin.Util = { else if (evt.data.startsWith(floID, 1)) var i = 36 else return; + let d = e = null; try { let data = JSON.parse(evt.data.substring(i)) let filter = {} for (let v in data) if (checkFilter(v, data[v], request)) filter[v] = data[v] - callback(filter, null) + d = filter } catch (error) { - callback(null, evt.data.substring(i)) + e = evt.data.substring(i) + } finally { + callback(d, e) } } this.liveRequest[randID] = node; @@ -10838,8 +10841,8 @@ Bitcoin.Util = { } let infoChange = false; for (let vc in dataSet) { - if(groupID !== dataSet[vc].receiverID || - !floGlobals.groups[groupID].members.includes(dataSet[vc].senderID)) + if (groupID !== dataSet[vc].receiverID || + !floGlobals.groups[groupID].members.includes(dataSet[vc].senderID)) continue; try { let data = { @@ -10848,10 +10851,10 @@ Bitcoin.Util = { groupID: dataSet[vc].receiverID } let k = floGlobals.groups[groupID].eKey; - if(floGlobals.expiredKeys[groupID]){ + if (floGlobals.expiredKeys[groupID]) { var ex = Object.keys(floGlobals.expiredKeys[groupID]).sort() - while(ex.lenght && vc > ex[0]) ex.shift() - if(ex.length) + while (ex.lenght && vc > ex[0]) ex.shift() + if (ex.length) k = floGlobals.expiredKeys[groupID][ex.shift()] } dataSet[vc].message = messenger.util.decrypt(dataSet[vc].message, k) @@ -10890,8 +10893,8 @@ Bitcoin.Util = { data.message = messenger.util.decrypt(data.message); newInbox.messages[vc] = data; messenger.addMark(data.groupID, "unread") - if (!floGlobals.appendix[`lastReceived_${groupID}`] || - floGlobals.appendix[`lastReceived_${groupID}`] < vc) + if (!floGlobals.appendix[`lastReceived_${groupID}`] || + floGlobals.appendix[`lastReceived_${groupID}`] < vc) floGlobals.appendix[`lastReceived_${groupID}`] = vc; } catch (error) { console.log(error) @@ -10901,20 +10904,20 @@ Bitcoin.Util = { `lastReceived_${groupID}`); if (infoChange) compactIDB.writeData("groupInfo", floGlobals.groups[groupID], groupID) - this.UIcallback["group"](newInbox) + messenger.util.UIcallback["group"](newInbox) } return floCloudAPI.requestApplicationData(null, { - receiverID: groupID, - lowerVectorClock: floGlobals.appendix[`lastReceived_${groupID}`] + 1, - callback: callbackFn - }) + receiverID: groupID, + lowerVectorClock: floGlobals.appendix[`lastReceived_${groupID}`] + 1, + callback: callbackFn + }) } }, - setUIcallbacks(directUI = null, groupUI = null){ - if(direct instanceof Function) + setUIcallbacks(directUI = null, groupUI = null) { + if (directUI instanceof Function) this.util.UIcallback["direct"] = directUI - if(groupUI instanceof Function) + if (groupUI instanceof Function) this.util.UIcallback["group"] = groupUI }, @@ -11094,7 +11097,7 @@ Bitcoin.Util = { let r = JSON.parse(dataSet[vc].message); let groupInfo = floGlobals.groups[r.groupID] if (dataSet[vc].senderID === groupInfo.admin) { - if(typeof floGlobals.expiredKeys[r.groupID] !== "object") + if (typeof floGlobals.expiredKeys[r.groupID] !== "object") floGlobals.expiredKeys[r.groupID] = {} floGlobals.expiredKeys[r.groupID][vc] = groupInfo.eKey groupInfo.eKey = r.newKey; @@ -11110,7 +11113,7 @@ Bitcoin.Util = { } } compactIDB.writeData("appendix", floGlobals.appendix.lastReceived, "lastReceived"); - this.util.UIcallback["direct"](newInbox) + messenger.util.UIcallback["direct"](newInbox) } var options = { @@ -11448,28 +11451,27 @@ Bitcoin.Util = { .then(r => res([g, r])) .catch(e => rej([g, e])) }) - for (let g in floGlobals.groups) - if (floGlobals.groups[g].status !== false) + for (let g in floGlobals.groups) + if (floGlobals.groups[g].status !== false) promises.push(reqFn(g)) Promise.allSettled(promises).then(result => { let ret = {}; result.forEach(r => { - if(r.status === 'fulfilled') + if (r.status === 'fulfilled') ret[r.value[0]] = { - status = r.status, - value = r.value[1] + status: r.status, + value: r.value[1] } - else if(r.status === "rejected") + else if (r.status === "rejected") ret[r.reason[0]] = { - status = r.status, - reason = r.reason[1] + status: r.status, + reason: r.reason[1] } }) resolve(ret) }) }) } - } From ef9c8eba0f2572a76e3351528365b1e31c8360ee Mon Sep 17 00:00:00 2001 From: sairajzero Date: Mon, 14 Dec 2020 03:59:09 +0530 Subject: [PATCH 5/7] Fixed Mail and backup issues --- index.html | 260 +++++++++++++++++++++++------------------------------ 1 file changed, 113 insertions(+), 147 deletions(-) diff --git a/index.html b/index.html index 8d36bbb..46b3e34 100644 --- a/index.html +++ b/index.html @@ -1569,10 +1569,10 @@ let subject = subjectOfReplyMail.value; let content = replyMailContent.value; let prevRef = replyMailPopup.dataset.prevRef; - messenger.replyMail(prevRef, subject, content, recipient).then(data => { + messenger.sendMail(subject, content, recipient, prevRef).then(data => { notify(`Mail replied!`); replyMailPopup.hide() - let {ref, subject, time, category, floID, content} = data + let {ref, subject, time, from, to, prev, content} = data sentMailContainer.prepend(render.mailCard(ref, subject, time, category, floID, content)); }).catch(error => notify("Failed to send mail!", "error", error)) } @@ -10926,7 +10926,6 @@ Bitcoin.Util = { var obj = { messages: {}, mails: {}, - mailRef: {}, marked: {}, groupInfo: {}, groupMsg: {}, @@ -10961,72 +10960,38 @@ Bitcoin.Util = { }) }, - sendMail(subject, content, recipients) { + sendMail(subject, content, recipients, prev = null) { return new Promise(async (resolve, reject) => { - let result = { - success: {}, - error: {} - } + if (!Array.isArray(recipients)) + recipients = [recipients] let mail = { subject: subject, content: content, - ref: floCrypto.randString(32, true) - } - for (let i = 0; i < recipients.length; i++) { - try { - if (recipients[i] in floGlobals.pubKeys) - result.success[recipients[i]] = await this.util.sendEncrypted(mail, - recipients[i]); - else - result.success[recipients[i]] = await this.util.sendEncoded(mail, - recipients[i]); - } catch (error) { - result.error[recipients[i]] = error; - } - } - let data = { - time: Date.now(), - floID: Object.keys(result.success), - category: "sentTo", - subject: subject, - ref: mail.ref - } - compactIDB.addData("mails", data, `${data.time}`) - compactIDB.addData("mailContent", this.util.encrypt(content), `${data.time}`) - compactIDB.addData("mailRef", `${data.time}`, mail.ref) - result.data = data - resolve(result) - }) - }, - - replyMail(prevRef, subject, content, recipient) { - return new Promise(async (resolve, reject) => { - let mail = { - prevRef: prevRef, - subject: subject, - content: content, - ref: floCrypto.randString(32, true) - } - try { - if (recipient in floGlobals.pubKeys) - await this.util.sendEncrypted(mail, recipient); - else - await this.util.sendEncoded(mail, recipient); - let data = { - time: Date.now(), - floID: recipient, - category: "replyTo", - prevRef: prevRef, - subject: subject, - ref: mail.ref - } - compactIDB.addData("mails", data, `${data.time}`) - compactIDB.addData("mailContent", this.util.encrypt(content), `${data.time}`) - compactIDB.addData("mailRef", `${data.time}`, mail.ref) - resolve(data) - } catch (error) { - reject(error) + ref: Date.now() + floCrypto.randString(8, true), + prev: prev } + let promises = recipients.map(r => this.util.sendRaw(JSON.stringify(mail), r, "MAIL")) + Promise.allSettled(promises).then(results => { + mail.time = Date.now(); + mail.from = myFloID + mail.to = [] + results.forEach(r => { + if (r.status === "fulfilled") { + let vc = Object.keys(r.value).pop() + mail.to.push(r.value[vc].receiverID) + } + }) + if (mail.to.length === 0) + return reject(results) + mail.content = this.util.encrypt(content) + compactIDB.addData("mails", { + ...mail + }, mail.ref) + mail.content = content + resolve({ + [mail.ref]: mail + }); + }) }) }, @@ -11046,41 +11011,42 @@ Bitcoin.Util = { try { //store the pubKey if not stored already floDapps.storePubKey(dataSet[vc].senderID, dataSet[vc].pubKey) - let data = { - time: dataSet[vc].time, - floID: dataSet[vc].senderID, - } if (dataSet[vc].message instanceof Object && "secret" in dataSet[vc] .message) dataSet[vc].message = floCrypto.decryptData(dataSet[vc].message, myPrivKey) if (dataSet[vc].type === "MESSAGE") { //process as message - data.category = "received" - data.message = messenger.util.encrypt(dataSet[vc].message); + let dm = { + time: dataSet[vc].time, + floID: dataSet[vc].senderID, + category: "received", + message: messenger.util.encrypt(dataSet[vc].message) + } compactIDB.addData("messages", { - ...data + ...dm }, vc) - data.message = dataSet[vc].message; - newInbox.messages[vc] = data; - messenger.addMark(data.floID, "unread") + dm.message = dataSet[vc].message; + newInbox.messages[vc] = dm; + messenger.addMark(dm.floID, "unread") } else if (dataSet[vc].type === "MAIL") { //process as mail - data.subject = dataSet[vc].message.subject; - data.ref = dataSet[vc].message.ref; - if (dataSet[vc].message.prevRef) { - data.prevRef = dataSet[vc].message.prevRef; - data.category = "replyFrom"; - } else - data.category = "receivedFrom" - data.content = messenger.util.encrypt(dataSet[vc].message.content) + let data = JSON.parse(dataSet[vc].message); + let mail = { + time: dataSet[vc].time, + from: dataSet[vc].senderID, + to: [myFloID], + subject: data.subject, + content: messenger.util.encrypt(data.content), + ref: data.ref, + prev: data.prev + } compactIDB.addData("mails", { - ...data - }, vc); - compactIDB.addData("mailRef", vc, data.ref) - data.content = dataSet[vc].message.content; - newInbox.mails[vc] = data; - messenger.addMark(data.ref, "unread") + ...mail + }, mail.ref); + mail.content = dataSet[vc].message.content; + newInbox.mails[mail.ref] = mail; + messenger.addMark(mail.ref, "unread") } else if (dataSet[vc].type === "CREATE_GROUP") { //process create group let groupInfo = JSON.parse(dataSet[vc].message); @@ -11129,13 +11095,9 @@ Bitcoin.Util = { getMail(mailRef) { return new Promise((resolve, reject) => { - compactIDB.readData("mailRef", mailRef).then(result => { - let promises = [compactIDB.readData("mails", result), compactIDB.readData( - "mailContent", result)] - Promise.all(promises).then(results => { - results[0].content = Crypto.AES.decrypt(results[1]); - resolve(results[0]); - }).catch(error => reject(error)) + compactIDB.readData("mails", mailRef).then(mail => { + mail.content = this.util.decrypt(mail.content) + resolve(mail) }).catch(error => reject(error)) }); }, @@ -11176,32 +11138,48 @@ Bitcoin.Util = { return floDapps.storeContact(floID, name) }, - loadDataFromIDB() { + loadDataFromIDB(dataList = 'default') { return new Promise((resolve, reject) => { - let loadData = ["messages", "mails", "marked", "groupMsg", "groupInfo", "appendix"] + if (dataList === 'default') + dataList = ["messages", "mails", "marked", "groupMsg", "groupInfo", "appendix"] + else if (dataList === 'all') + dataList = ["messages", "mails", "marked", "groupMsg", "groupInfo", "groupKey", + "appendix" + ] let promises = [] - for (var i = 0; i < loadData.length; i++) - promises[i] = compactIDB.readAllData(loadData[i]) + for (var i = 0; i < dataList.length; i++) + promises[i] = compactIDB.readAllData(dataList[i]) Promise.all(promises).then(results => { let data = {} - for (var i = 0; i < loadData.length; i++) - data[loadData[i]] = results[i] + for (var i = 0; i < dataList.length; i++) + data[dataList[i]] = results[i] data.appendix.lastReceived = data.appendix.lastReceived || '0' if (data.appendix.AESKey) { try { let AESKey = floCrypto.decryptData(data.appendix.AESKey, myPrivKey); data.appendix.AESKey = AESKey; - for (let m in data.messages) { - let decrypted = Crypto.AES.decrypt(data.messages[m].message, - AESKey) - data.messages[m].message = decrypted; - } + if ("messages" in dataList) + for (let m in data.messages) + data.messages[m].message = this.util.decrypt(data.messages[m] + .message, AESKey) + if ("mails" in dataList) + for (let m in data.mails) + data.mails[m].content = this.util.decrypt(data.mails[m].content, + AESKey) + if ("groupKey" in dataList) + for (let k in data.groupKeys) + data.groupKeys[k] = this.util.decrypt(data.groupKeys[k], AESKey) + if ("groupMsg" in dataList) + for (let m in data.groupMsg) + if (datdata.groupMsg[m].message) + data.groupMsg[m].message = this.util.decrypt(data.groupMsg[ + m].message, AESKey) resolve(data) } catch (error) { reject("Corrupted AES Key"); } } else { - if (Object.keys(data.messages).length) + if (Object.keys(data.messages).length || Object.keys(data.mails).length) return reject("AES Key not Found") let AESKey = floCrypto.randString(32); let encryptedKey = floCrypto.encryptData(AESKey, myPubKey); @@ -11216,24 +11194,16 @@ Bitcoin.Util = { backupData() { return new Promise((resolve, reject) => { - let obs = ["contacts", "pubKeys", "messages", "mails", "mailContent", "mailRef", "marked", - "appendix" - ] - let promises = []; - obs.forEach(o => promises.push(compactIDB.readAllData(o))) - Promise.all(promises).then(results => { - let data = {} - for (let i = 0; i < obs.length; i++) - data[obs[i]] = results[i] - results = undefined; + this.loadDataFromIDB("all").then(data => { + delete data.appendix.AESKey; data = btoa(unescape(encodeURIComponent(JSON.stringify(data)))) - data = { + let blobData = { floID: myFloID, pubKey: myPubKey, - data: floCrypto.encryptData(data, myPubKey), + data: this.util.encrypt(data, myPrivKey), } - data.sign = floCrypto.signData(JSON.stringify(data.data), myPrivKey); - resolve(new Blob([JSON.stringify(data)], { + blobData.sign = floCrypto.signData(blobData.data, myPrivKey); + resolve(new Blob([JSON.stringify(blobData)], { type: 'application/json' })); }).catch(error => reject(error)) @@ -11245,14 +11215,14 @@ Bitcoin.Util = { if (blob instanceof Blob || blob instanceof File) { let reader = new FileReader(); reader.onload = evt => { - var data = JSON.parse(evt.target.result); - if (!floCrypto.verifySign(JSON.stringify(data.data), data.sign, data.pubKey)) + var blobData = JSON.parse(evt.target.result); + if (!floCrypto.verifySign(blobData.data, blobData.sign, blobData.pubKey)) reject("Corrupted Backup file: Signature verification failed"); - else if (myFloID !== data.floID || myPubKey !== data.pubKey) + else if (myFloID !== blobData.floID || myPubKey !== blobData.pubKey) reject("Invalid Backup file: Incorrect floID"); else { try { - data = floCrypto.decryptData(data.data, myPrivKey); + let data = this.util.decrypt(blobData.data, myPrivKey) try { data = JSON.parse(decodeURIComponent(escape(atob(data)))); resolve(data) @@ -11272,32 +11242,28 @@ Bitcoin.Util = { restoreData(arg) { return new Promise((resolve, reject) => { - let parseData; if (arg instanceof Blob || arg instanceof File) - parseData = this.parseBackup + var parseData = this.parseBackup else - parseData = data => new Promise((res, rej) => res(data)) + var parseData = data => new Promise((res, rej) => res(data)) parseData(arg).then(data => { - if (data.appendix.lastReceived < floGlobals.appendix.lastReceived) - data.appendix.lastReceived = floGlobals.appendix.lastReceived; - let AESKey = floCrypto.decryptData(data.appendix.AESKey, myPrivKey); - if (AESKey !== floGlobals.appendix.AESKey) { - for (let m in data.messages) { - let decrypted = this.util.decrypt(data.messages[m].message, AESKey) - let encrypted = this.util.encrypt(decrypted) - data.messages[m].message = encrypted; - } - for (let m in data.mailContent) { - let decrypted = this.util.decrypt(data.mailContent[m], AESKey) - let encrypted = this.util.encrypt(decrypted) - data.mailContent[m] = encrypted; - } - } - delete data.appendix.AESKey; + for (let m in data.messages) + data.messages[m].message = this.util.encrypt(data.messages[m].message) + for (let m in data.mail) + data.mails[m].content = this.util.encrypt(data.mails[m].content) + for (let k in data.groupKeys) + data.groupKeys[k] = this.util.encrypt(data.groupKeys[k]) + for (let m in data.groupMsg) + if (datdata.groupMsg[m].message) + data.groupMsg[m].message = this.util.encrypt(data.groupMsg[m].message) + for (let l in data.appendix) + if (l.startsWith('lastReceived') && data.appendix[l] < floGlobals.appendix[ + l]) + data.appendix[l] = floGlobals.appendix[l] let promises = []; - for (obs in data) - for (value in data[obs]) - promises.push(compactIDB.writeData(obs, data[obs][value], value)); + for (let obs in data) + for (let k in data[obs]) + promises.push(compactIDB.writeData(obs, data[obs][k], k)); Promise.all(promises) .then(results => resolve("Restore Successful")) .catch(error => reject("Restore Failed: Unable to write to IDB")) From 5e0e58145743ed81f0bd09f166207f2662534144 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Fri, 18 Dec 2020 04:17:30 +0530 Subject: [PATCH 6/7] UI and bug fixes - Fixed: Mail UI - Fixed minor bugs --- index.html | 255 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 145 insertions(+), 110 deletions(-) diff --git a/index.html b/index.html index 46b3e34..c511b16 100644 --- a/index.html +++ b/index.html @@ -98,10 +98,10 @@ renderMailList(data.mails, false) renderMarked(data.marked) messenger.setUIcallbacks(renderDirectUI, renderGroupUI) - messenger.refreshInbox() + messenger.requestDirectInbox() .then(r => console.log("DirectConn:", r)) .catch(e => console.error("Request error:", e)) - messenger.refreshGroupInbox() + messenger.requestGroupInbox() .then(r => console.log(r)) console.log(`Load Successful!`) //hide loading screen @@ -685,7 +685,7 @@ messageTemplate = document.getElementById('message_template'), mailTemplate = document.getElementById('mail_template') const render = { - mailCard(mailRef, subject, timestamp, category, floID, content, markUnread){ + mailCard(floID, ref, subject, timestamp, content, markUnread){ let card = mailCardTemplate.content.cloneNode(true), cardContainer = card.querySelector('.mail-card'), time = new Date(timestamp).toString(), @@ -694,7 +694,6 @@ dateTime minutes = minutes.length === 1 ? `0${minutes}` : minutes let finalHours = hours - 12 > 0 ? `${hours - 12}:${minutes} pm` : `${hours}:${minutes} am` - if(new Date().getDate() === new Date(timestamp).getDate()) dateTime = finalHours else @@ -702,18 +701,30 @@ let mailSummery = content.split(' ') mailSummery.splice(16) mailSummery = mailSummery.join(' ') - cardContainer.setAttribute("name", mailRef); - if (Array.isArray(floID)) - floID = floID.join(","); + cardContainer.setAttribute("name", ref); + let contact; + if(Array.isArray(floID)){ + for(let f of floID) + if(floGlobals.contacts[f]){ + contact = floGlobals.contacts[f] + break; + } + contact = contact || `${floID[0].substring(12)}...`; + if(floID.length > 1) + contact = `${contact} & ${floID.length - 1} others(s)` + floID = floID.join(', ') + } else + contact = floGlobals.contacts[floID] || floID if(markUnread) cardContainer.classList.add('unread') - card.querySelector('.sender').textContent = floGlobals.contacts[floID] || floID + card.querySelector('.sender').textContent = contact card.querySelector('.subject').textContent = subject card.querySelector('.date').textContent = dateTime card.querySelector('.description').textContent = mailSummery return card }, - mail(senderName, floID, timestamp, category, subject, content){ + + mail(from, to, subject, timestamp, content){ let card = mailTemplate.content.cloneNode(true), cardContainer = card.querySelector('.mail'), time = new Date(timestamp).toString(), @@ -722,17 +733,27 @@ dateTime minutes = minutes.length === 1 ? `0${minutes}` : minutes let finalHours = hours - 12 > 0 ? `${hours - 12}:${minutes} pm` : `${hours}:${minutes} am` - if(new Date().getDate() === new Date(timestamp).getDate()) dateTime = finalHours else dateTime = time.slice(4, 10) - if (Array.isArray(floID)) - floID = floID.join(","); - if(category === 'receivedFrom') - card.querySelector('.sender-name').textContent = `From : ${senderName}` - if(category === 'sentTo') - card.querySelector('.sender-name').textContent = `To : ${senderName}` + let senderName, floID + if(from === myFloID){ + let count = 0, list = []; + to.forEach(f => floGlobals.contacts[f] ? list.push(floGlobals.contacts[f]): count++) + senderName = `To : ${list.join(', ')}` + if(count){ + if(list.length) + senderName = `${senderName} & ${count} other(s)` + else + senderName = `${senderName} ${count} Unknown people` + } + floID = to.join(', ') + } else { + senderName = `From : ${floGlobals.contacts[from] || ''}`; + floID = from + } + card.querySelector('.sender-name').textContent = senderName card.querySelector('.flo-id').textContent = floID card.querySelector('.mail-subject').textContent = subject card.querySelector('.date').textContent = dateTime; @@ -1153,7 +1174,7 @@ 'Are you sure you want to Sign out?', "Sign Out", "Stay Signed In").then( result => { - floDapps.logout().then(result => { + floDapps.clearCredentials().then(result => { notify("Successfully Signed out", 'success') setTimeout(onLoadStartUp, 2000) }).catch(error => notify("Signout Unsuccessful", "error", error)) @@ -1164,7 +1185,7 @@ getConfirmation('Remove Account?', '**Remember to store your PRIVATE-KEY**\n*Private-Key will be needed to signIn again*\nAre you sure you want to remove account?', "Remove").then(result => { - floDapps.logout().then(result => { + floDapps.clearCredentials().then(result => { notify("Removed Account") setTimeout(onLoadStartUp, 2000) }).catch(error => notify("Remove Unsuccessful", "error", error)) @@ -1223,7 +1244,7 @@ return if(refreshing) return; refreshing = true - messenger.refreshInbox().then(data => { + messenger.requestDirectInbox().then(data => { renderMessages(data.messages) renderMailList(data.mails) if(Object.keys(data.messages).length && activePage.button !== chatPageButton){ @@ -1431,8 +1452,6 @@ return element; } - let frag = document.createDocumentFragment() - function renderMessages(data, markUnread = true) { let floID for (let m in data) { @@ -1471,18 +1490,19 @@ messenger.removeMark(floID, "unread"); } - let sentMailFrag = document.createDocumentFragment() function renderMailList(mails, markUnread = true) { - for (m in mails) { - let { ref, subject, time, category, floID, content} = mails[m] - if(category === 'receivedFrom' || category === 'replyFrom') - frag.prepend(render.mailCard(ref, subject, time, category, floID, content, markUnread)); - if(category === 'sentTo' || category === 'replyTo') - sentMailFrag.prepend(render.mailCard(ref, subject, time, category, floID, content, markUnread)); + let inboxMailFrag = document.createDocumentFragment() + let sentMailFrag = document.createDocumentFragment() + for (let m in mails) { + let { from, to, prev, ref, subject, time, content } = mails[m] + if(from === myFloID) + sentMailFrag.prepend(render.mailCard(to, ref, subject, time, content, markUnread)) + else if(to.includes(myFloID)) + inboxMailFrag.prepend(render.mailCard(from, ref, subject, time, content, markUnread)) } - inboxMailContainer.prepend(frag) + inboxMailContainer.prepend(inboxMailFrag) sentMailContainer.prepend(sentMailFrag) - if(inboxMailContainer.children.length){ + if(inboxMailContainer.children.length || sentMailContainer.children.length){ document.getElementById('no_mails').classList.add('hide-completely') document.getElementById('all_mail_container').classList.remove('hide-completely') } @@ -1498,26 +1518,19 @@ clearElement(document.getElementById("mail_container")) messenger.getMail(mailRef).then(result => { - let {floID, time, category, subject, content, prevRef} = result - //add name (if available) - let senderName = floGlobals.contacts[floID] || ' '; + let { from, to, prev, ref, subject, time, content } = result //append the contents to mail container - document.getElementById("mail_container").append(render.mail(senderName, floID, time, category, subject, content)); + document.getElementById("mail_container").append(render.mail(from, to, subject, time, content)); //add prop for previous mail (if available) let prevMail = document.getElementById("prev_mail"); - prevMail.dataset["value"] = prevRef; - prevMail.style.display = prevRef ? 'block' : 'none'; + prevMail.dataset["value"] = prev; + prevMail.style.display = prev ? 'block' : 'none'; //set values for reply mail form if new view if (newView) { - if (floID.includes(',')) - document.getElementById("show_reply_popup").classList.add("hide-completely"); - else { - replyMailPopup.dataset["to"] = floID; - replyMailPopup.dataset["prevRef"] = mailRef; - subjectOfReplyMail.value = subject.startsWith("Re: ") ? result - .subject : `Re: ${subject}`; - document.getElementById("show_reply_popup").classList.remove("hide-completely"); - } + replyMailPopup.dataset["to"] = (from === myFloID ? to.join(',') : from) + replyMailPopup.dataset["prev"] = mailRef; + subjectOfReplyMail.value = subject.startsWith("Re: ") ? subject : `Re: ${subject}`; + document.getElementById("show_reply_popup").classList.remove("hide-completely"); } messenger.removeMark(mailRef, "unread"); }).catch(error => notify("Unable to read mail", "error", error)) @@ -1539,25 +1552,9 @@ recipients.push(tmp); }) messenger.sendMail(subject, content, recipients).then(result => { - console.log(result); - let sucessCount = Object.keys(result.success).length; - let errorCount = Object.keys(result.error).length - if (!sucessCount) - notify("Failed to send mail!", "error", result) - else { - if (errorCount) - notify( - `${Object.keys(result.success).length} Mail(s) sent! (${Object.keys(result.error).length} failed)`, - "warn", { - sent: result.success, - failed: result.error - }); - else - notify(`${Object.keys(result.success).length} Mail(s) sent!`) - let {ref, subject, time, category, floID, content} = result.data - sentMailContainer.prepend(render.mailCard(ref, subject, time, category, floID, content)); - composeMailPopup.hide() - } + notify(`Mail(s) sent!`) + renderMailList(result) + composeMailPopup.hide() }).catch(error => notify("Failed to send mail!", "error", error)) } catch (error) { notify(error, "error") @@ -1566,15 +1563,16 @@ function replyMail() { let recipient = replyMailPopup.dataset.to; + if(recipient.includes(',')) + recipient = recipient.split(',') let subject = subjectOfReplyMail.value; let content = replyMailContent.value; - let prevRef = replyMailPopup.dataset.prevRef; - messenger.sendMail(subject, content, recipient, prevRef).then(data => { + let prev = replyMailPopup.dataset.prev; + messenger.sendMail(subject, content, recipient, prev).then(result => { notify(`Mail replied!`); + renderMailList(result) replyMailPopup.hide() - let {ref, subject, time, from, to, prev, content} = data - sentMailContainer.prepend(render.mailCard(ref, subject, time, category, floID, content)); - }).catch(error => notify("Failed to send mail!", "error", error)) + }).catch(error => notify("Failed to reply mail!", "error", error)) } @@ -9205,7 +9203,7 @@ Bitcoin.Util = { } } - - - -