blockUser and disableGroup features

- block/unblock users (stop receiving messages)
- disable group (stop receiving messages)
- groups, chats, marked are now warped in messenger module. (can be accessed by messenger properties)
- Improved requestGroupInbox and requestDirectInbox
- Improved assigning renderUI functions
- messenger.init now starts the messenger-startups (no longer needed to call each messenger-startup functions individually)
This commit is contained in:
sairajzero 2022-06-02 02:12:17 +05:30
parent c9fde067f2
commit 60d2447e8c
2 changed files with 345 additions and 275 deletions

View File

@ -61,35 +61,21 @@
//load messages from IDB and render them
console.log(`Loading Data! Please Wait...`)
messenger.initUserDB().then(result => {
console.log(result)
//Check for availble bg image
setBgImage()
messenger.loadDataFromIDB().then(data => {
console.log(data)
floGlobals.appendix = data.appendix;
floGlobals.groups = data.groups;
floGlobals.chats = data.chats
floGlobals['marked'] = data.marked
renderChatList()
renderMailList(data.mails, false)
renderMarked(data.marked)
messenger.setUIcallbacks(renderDirectUI, renderGroupUI)
messenger.requestDirectInbox()
.then(r => console.log("DirectConn:", r))
.catch(e => console.error("Request error:", e))
messenger.requestGroupInbox()
.then(r => console.log(r))
console.log(`Load Successful!`)
if(isPinSet){
loadPage()
}
}).catch(error => {
//console.error(`Failed to load data`)
notify(error, "error")
})
})
//Check for availble bg image
setBgImage();
//Set UI render functions
messenger.renderUI.chats = renderChatList;
messenger.renderUI.directChat = renderDirectUI;
messenger.renderUI.groupChat = renderGroupUI;
messenger.renderUI.mails = m => renderMailList(m, false);
messenger.renderUI.marked = renderMarked;
//init messenger
messenger.init().then(result => {
console.log(result);
if(isPinSet){
loadPage()
}
}).catch(error => notify(error, "error"));
}).catch(error => notify(error, "error"))
}
</script>
@ -1143,7 +1129,7 @@
getRef('add_as_contact_option').classList.remove('hide-completely')
}
const markUnread = floGlobals.marked[clickedContact.floID]?.includes('unread')
const markUnread = messenger.marked[clickedContact.floID]?.includes('unread')
if (markUnread) {
getRef('mark_read_option').classList.remove('hide-completely')
getRef('mark_unread_option').classList.add('hide-completely')
@ -1162,7 +1148,7 @@
getRef("contact_initial").innerHTML = `
<svg class="icon group-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path d="M13.61,28.09c-1.63,0-4.72-2.35-5.33-3.58a21.65,21.65,0,0,1-1.35-7.32s-.26-6.07,6.68-6.07a6.38,6.38,0,0,1,6.69,6.07A21.65,21.65,0,0,1,19,24.51c-.62,1.23-3.7,3.58-5.34,3.58"/><path d="M50.39,28.09c-1.64,0-4.72-2.35-5.34-3.58a21.9,21.9,0,0,1-1.35-7.32s-.26-6.07,6.69-6.07a6.37,6.37,0,0,1,6.68,6.07,21.65,21.65,0,0,1-1.35,7.32c-.61,1.23-3.7,3.58-5.33,3.58"/><path d="M32,31.74c-2.21,0-6.37-3.17-7.2-4.83A29.3,29.3,0,0,1,23,17s-.35-8.21,9-8.21c8.68,0,9,8.21,9,8.21a29.3,29.3,0,0,1-1.83,9.88c-.82,1.66-5,4.83-7.2,4.83"/><path d="M48.29,38.58c-4.16-1.83-8.57-3.08-10.34-6.4a12,12,0,0,1-6,3.73,12,12,0,0,1-5.95-3.73c-1.77,3.32-6.18,4.57-10.34,6.4-1.7.71-3.11,9.88-1.13,9.88A33.06,33.06,0,0,0,31.23,53h1.54a33.06,33.06,0,0,0,16.65-4.53C51.4,48.46,50,39.29,48.29,38.58Z"/><path d="M14.82,36.57c.76-.33,1.54-.65,2.3-1,2.49-1,4.85-2,6.22-3.44C21.07,31.23,19,30.25,18,28.41a8.83,8.83,0,0,1-4.41,2.76,8.83,8.83,0,0,1-4.4-2.76c-1.31,2.46-4.58,3.38-7.66,4.74-1.26.52-2.3,7.31-.84,7.31a24.55,24.55,0,0,0,10.86,3.31C11.89,40.81,12.86,37.39,14.82,36.57Z"/><path d="M62.45,33.15c-3.08-1.36-6.35-2.28-7.66-4.74a8.83,8.83,0,0,1-4.4,2.76A8.83,8.83,0,0,1,46,28.41c-1,1.84-3,2.82-5.32,3.76,1.37,1.43,3.73,2.41,6.22,3.44.76.31,1.54.63,2.26,1,2,.83,3,4.25,3.29,7.21a24.55,24.55,0,0,0,10.86-3.31C64.75,40.46,63.71,33.67,62.45,33.15Z"/></svg>
`
if (floGlobals.groups[clickedContact['floID']].admin === myFloID)
if (messenger.groups[clickedContact['floID']].admin === myFloID)
getRef('contact_name').disabled = false
else
getRef('contact_name').disabled = true
@ -1180,7 +1166,7 @@
case 'contacts_popup':
const contacts = []
for (contact in floGlobals.contacts) {
if (!floGlobals.groups[activeChat.floID].members.includes(contact) && contact in floGlobals.pubKeys) {
if (!messenger.groups[activeChat.floID].members.includes(contact) && contact in floGlobals.pubKeys) {
contacts.push(contact)
}
}
@ -1382,8 +1368,8 @@
let name
if (floGlobals.contacts[floID])
name = floGlobals.contacts[floID]
else if (floGlobals.groups[floID])
name = floGlobals.groups[floID].name
else if (messenger.groups[floID])
name = messenger.groups[floID].name
else if (floID === myFloID)
name = 'You'
else
@ -1678,7 +1664,7 @@
initial.innerHTML = `
<svg class="icon group-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path d="M13.61,28.09c-1.63,0-4.72-2.35-5.33-3.58a21.65,21.65,0,0,1-1.35-7.32s-.26-6.07,6.68-6.07a6.38,6.38,0,0,1,6.69,6.07A21.65,21.65,0,0,1,19,24.51c-.62,1.23-3.7,3.58-5.34,3.58"/><path d="M50.39,28.09c-1.64,0-4.72-2.35-5.34-3.58a21.9,21.9,0,0,1-1.35-7.32s-.26-6.07,6.69-6.07a6.37,6.37,0,0,1,6.68,6.07,21.65,21.65,0,0,1-1.35,7.32c-.61,1.23-3.7,3.58-5.33,3.58"/><path d="M32,31.74c-2.21,0-6.37-3.17-7.2-4.83A29.3,29.3,0,0,1,23,17s-.35-8.21,9-8.21c8.68,0,9,8.21,9,8.21a29.3,29.3,0,0,1-1.83,9.88c-.82,1.66-5,4.83-7.2,4.83"/><path d="M48.29,38.58c-4.16-1.83-8.57-3.08-10.34-6.4a12,12,0,0,1-6,3.73,12,12,0,0,1-5.95-3.73c-1.77,3.32-6.18,4.57-10.34,6.4-1.7.71-3.11,9.88-1.13,9.88A33.06,33.06,0,0,0,31.23,53h1.54a33.06,33.06,0,0,0,16.65-4.53C51.4,48.46,50,39.29,48.29,38.58Z"/><path d="M14.82,36.57c.76-.33,1.54-.65,2.3-1,2.49-1,4.85-2,6.22-3.44C21.07,31.23,19,30.25,18,28.41a8.83,8.83,0,0,1-4.41,2.76,8.83,8.83,0,0,1-4.4-2.76c-1.31,2.46-4.58,3.38-7.66,4.74-1.26.52-2.3,7.31-.84,7.31a24.55,24.55,0,0,0,10.86,3.31C11.89,40.81,12.86,37.39,14.82,36.57Z"/><path d="M62.45,33.15c-3.08-1.36-6.35-2.28-7.66-4.74a8.83,8.83,0,0,1-4.4,2.76A8.83,8.83,0,0,1,46,28.41c-1,1.84-3,2.82-5.32,3.76,1.37,1.43,3.73,2.41,6.22,3.44.76.31,1.54.63,2.26,1,2,.83,3,4.25,3.29,7.21a24.55,24.55,0,0,0,10.86-3.31C64.75,40.46,63.71,33.67,62.45,33.15Z"/></svg>
`
cardContainer.querySelector('.name').textContent = floGlobals.groups[floID].name
cardContainer.querySelector('.name').textContent = messenger.groups[floID].name
}
else {
cardContainer.querySelector('.name').textContent = name !== 'Unknown' ? name : floID
@ -1713,7 +1699,7 @@
}
}
if (type === 'group' && lastMessage.time === 0) {
lastMessage.time = floGlobals.groups[floID].created
lastMessage.time = messenger.groups[floID].created
}
let lastText = document.createElement('p')
if ((type === 'chat' && lastMessage.category === 'sent') || (type === 'group' && lastMessage.sender === myFloID)) {
@ -1856,7 +1842,7 @@
else if (admin) {
if (newMembers.length) {
const cards = document.createDocumentFragment()
const { admin } = floGlobals.groups[groupID]
const { admin } = messenger.groups[groupID]
newMembers.forEach(member => {
let eventCard = document.createElement('p')
eventCard.classList.add('group-event-card')
@ -1872,7 +1858,7 @@
}
else if (rmMembers.length) {
const cards = document.createDocumentFragment()
const { admin } = floGlobals.groups[groupID]
const { admin } = messenger.groups[groupID]
rmMembers.forEach(member => {
let eventCard = document.createElement('p')
eventCard.classList.add('group-event-card')
@ -2134,7 +2120,7 @@
clickedContact['chatCard'] = contact
clickedContact['floID'] = contact.getAttribute("flo-id")
clickedContact['name'] = contact.getAttribute("name")
clickedContact['isGroup'] = floGlobals.groups.hasOwnProperty(clickedContact['floID'])
clickedContact['isGroup'] = messenger.groups.hasOwnProperty(clickedContact['floID'])
showPopup('contact_details_popup')
}, 500)
getRef('chat_page').addEventListener('touchmove', handleTouchMove)
@ -2171,7 +2157,7 @@
clickedContact['chatCard'] = contact
clickedContact['floID'] = contact.getAttribute("flo-id")
clickedContact['name'] = contact.getAttribute("name")
clickedContact['isGroup'] = floGlobals.groups.hasOwnProperty(clickedContact['floID'])
clickedContact['isGroup'] = messenger.groups.hasOwnProperty(clickedContact['floID'])
if (clickedContact['floID'] === myFloID) return
@ -2744,15 +2730,15 @@
getRef('contacts_container').append(frag)
}
async function renderChatList() {
async function renderChatList(chatOrder) {
const frag = document.createDocumentFragment()
getRef('chat_container').innerHTML = ''
for (floID of messenger.getChatOrder().mixed) {
const markUnread = floGlobals.marked[floID]?.includes('unread')
for (floID of chatOrder) {
const markUnread = messenger.marked[floID]?.includes('unread')
let type
if (floGlobals.chats[floID])
if (messenger.chats[floID])
type = 'chat'
else if (floGlobals.groups[floID])
else if (messenger.groups[floID])
type = 'group'
frag.append(render.contactCard(floID, { type, markUnread }))
}
@ -2927,7 +2913,7 @@
backgroundColor = contact.getAttribute('background-color')
activeChat['floID'] = floID
activeChat['isGroup'] = floGlobals.groups[floID] ? true : false
activeChat['isGroup'] = messenger.groups[floID] ? true : false
getRef("chat_dp").setAttribute('style', `color: ${textColor}; background-color: ${backgroundColor};`)
getRef("receiver_name").textContent = getContactName(floID);
if (activeChat.isGroup) {
@ -3235,12 +3221,12 @@
getRef("chat_dp").innerHTML = `
<svg class="icon group-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path d="M13.61,28.09c-1.63,0-4.72-2.35-5.33-3.58a21.65,21.65,0,0,1-1.35-7.32s-.26-6.07,6.68-6.07a6.38,6.38,0,0,1,6.69,6.07A21.65,21.65,0,0,1,19,24.51c-.62,1.23-3.7,3.58-5.34,3.58"/><path d="M50.39,28.09c-1.64,0-4.72-2.35-5.34-3.58a21.9,21.9,0,0,1-1.35-7.32s-.26-6.07,6.69-6.07a6.37,6.37,0,0,1,6.68,6.07,21.65,21.65,0,0,1-1.35,7.32c-.61,1.23-3.7,3.58-5.33,3.58"/><path d="M32,31.74c-2.21,0-6.37-3.17-7.2-4.83A29.3,29.3,0,0,1,23,17s-.35-8.21,9-8.21c8.68,0,9,8.21,9,8.21a29.3,29.3,0,0,1-1.83,9.88c-.82,1.66-5,4.83-7.2,4.83"/><path d="M48.29,38.58c-4.16-1.83-8.57-3.08-10.34-6.4a12,12,0,0,1-6,3.73,12,12,0,0,1-5.95-3.73c-1.77,3.32-6.18,4.57-10.34,6.4-1.7.71-3.11,9.88-1.13,9.88A33.06,33.06,0,0,0,31.23,53h1.54a33.06,33.06,0,0,0,16.65-4.53C51.4,48.46,50,39.29,48.29,38.58Z"/><path d="M14.82,36.57c.76-.33,1.54-.65,2.3-1,2.49-1,4.85-2,6.22-3.44C21.07,31.23,19,30.25,18,28.41a8.83,8.83,0,0,1-4.41,2.76,8.83,8.83,0,0,1-4.4-2.76c-1.31,2.46-4.58,3.38-7.66,4.74-1.26.52-2.3,7.31-.84,7.31a24.55,24.55,0,0,0,10.86,3.31C11.89,40.81,12.86,37.39,14.82,36.57Z"/><path d="M62.45,33.15c-3.08-1.36-6.35-2.28-7.66-4.74a8.83,8.83,0,0,1-4.4,2.76A8.83,8.83,0,0,1,46,28.41c-1,1.84-3,2.82-5.32,3.76,1.37,1.43,3.73,2.41,6.22,3.44.76.31,1.54.63,2.26,1,2,.83,3,4.25,3.29,7.21a24.55,24.55,0,0,0,10.86-3.31C64.75,40.46,63.71,33.67,62.45,33.15Z"/></svg>
`
getRef("last_interaction_time").textContent = `Created ${getFormatedTime(floGlobals.groups[floID].created)}`;
getRef("last_interaction_time").textContent = `Created ${getFormatedTime(messenger.groups[floID].created)}`;
getRef("chat_type").textContent = `Group FLO ID`;
getRef('group_members_list').innerHTML = ''
floGlobals.groups[floID].members.forEach(member => {
let isAdmin = floGlobals.groups[floID].admin === member ? true : false
messenger.groups[floID].members.forEach(member => {
let isAdmin = messenger.groups[floID].admin === member ? true : false
frag.append(render.contactCard(member, { type: 'contact', contactOnly: true, isAdmin }))
})
getRef('group_members_list').append(frag)
@ -3250,9 +3236,9 @@
getRef('delete_chat_button').classList.add('hide-completely')
getRef("group_description").value = floGlobals.groups[floID].description === '' ? 'Add group description' : floGlobals.groups[floID].description;
getRef("group_description").value = messenger.groups[floID].description === '' ? 'Add group description' : messenger.groups[floID].description;
if (floGlobals.groups[activeChat['floID']].admin === myFloID) {
if (messenger.groups[activeChat['floID']].admin === myFloID) {
getRef("chat_name").disabled = false
getRef('group_description').disabled = false
getRef('edit_group_button').classList.remove('hide-completely')

View File

@ -1,6 +1,62 @@
(function() {
const messenger = window.messenger = {};
const expiredKeys = {};
const UI = {
group: (d, e) => console.log(d, e),
direct: (d, e) => console.log(d, e),
chats: (c) => console.log(c),
mails: (m) => console.log(m),
marked: (r) => console.log(r)
};
messenger.renderUI = {};
Object.defineProperties(messenger.renderUI, {
chats: {
set: ui_fn => UI.chats = ui_fn
},
directChat: {
set: ui_fn => UI.direct = ui_fn
},
groupChat: {
set: ui_fn => UI.group = ui_fn
},
mails: {
set: ui_fn => UI.mails = ui_fn
},
marked: {
set: ui_fn => UI.marked = ui_fn
},
});
const _loaded = {};
Object.defineProperties(messenger, {
chats: {
get: _loaded.chats
},
groups: {
get: _loaded.groups
},
blocked: {
get: _loaded.blocked
},
marked: {
get: _loaded.marked
}
});
var directConnID, groupConnID = {};
messenger.conn = {};
Object.defineProperties(messenger.conn, {
direct: {
get: directConnID
},
group: {
get: Object.assign({}, groupConnID),
value: g_id => groupConnID[g_id]
}
});
function sendRaw(message, recipient, type, encrypt = null, comment = undefined) {
return new Promise((resolve, reject) => {
if (!floCrypto.validateAddr(recipient))
@ -21,11 +77,11 @@
})
}
function encrypt(value, key = floGlobals.appendix.AESKey) {
function encrypt(value, key = _loaded.appendix.AESKey) {
return Crypto.AES.encrypt(value, key)
}
function decrypt(value, key = floGlobals.appendix.AESKey) {
function decrypt(value, key = _loaded.appendix.AESKey) {
return Crypto.AES.decrypt(value, key)
}
@ -51,8 +107,7 @@
if (!result || !result.includes(mark))
return resolve("Mark doesnot exist")
else {
result.splice(result.indexOf(mark),
1); //remove the mark from the list of marks
result.splice(result.indexOf(mark), 1); //remove the mark from the list of marks
compactIDB.writeData("marked", result, key)
.then(result => resolve("Mark removed"))
.catch(error => reject(error))
@ -61,13 +116,12 @@
})
}
const UIcallback = {
group: (d, e) => console.log(d, e),
direct: (d, e) => console.log(d, e)
}
function groupConn(groupID) {
console.debug(UIcallback);
function requestGroupInbox(groupID) {
console.debug(UI);
if (groupConnID[groupID]) { //close existing request connection (if any)
floCloudAPI.closeRequest(groupConnID[groupID]);
delete groupConnID[groupID];
}
let callbackFn = function(dataSet, error) {
if (error)
return console.error(error)
@ -78,7 +132,7 @@
let infoChange = false;
for (let vc in dataSet) {
if (groupID !== dataSet[vc].receiverID ||
!floGlobals.groups[groupID].members.includes(dataSet[vc].senderID))
!_loaded.groups[groupID].members.includes(dataSet[vc].senderID))
continue;
try {
let data = {
@ -86,33 +140,33 @@
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()
let k = _loaded.groups[groupID].eKey;
if (expiredKeys[groupID]) {
var ex = Object.keys(expiredKeys[groupID]).sort()
while (ex.lenght && vc > ex[0]) ex.shift()
if (ex.length)
k = floGlobals.expiredKeys[groupID][ex.shift()]
k = expiredKeys[groupID][ex.shift()]
}
dataSet[vc].message = 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 = encrypt(dataSet[vc].message);
else if (data.sender === floGlobals.groups[groupID].admin) {
let groupInfo = floGlobals.groups[groupID]
else if (data.sender === _loaded.groups[groupID].admin) {
let groupInfo = _loaded.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))]
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
groupInfo.members = groupInfo.members.filter(m => !data.rmMembers.includes(m))
if (data.rmMembers.includes(myFloID)) {
disableGroup(groupID);
return;
}
} else if (dataSet[vc].type === "UP_DESCRIPTION") {
data.description = dataSet[vc].message
groupInfo.description = data.description
@ -131,40 +185,35 @@
console.log(data)
if (data.sender !== myFloID)
addMark(data.groupID, "unread")
if (!floGlobals.appendix[`lastReceived_${groupID}`] ||
floGlobals.appendix[`lastReceived_${groupID}`] < vc)
floGlobals.appendix[`lastReceived_${groupID}`] = vc;
if (!_loaded.appendix[`lastReceived_${groupID}`] ||
_loaded.appendix[`lastReceived_${groupID}`] < vc)
_loaded.appendix[`lastReceived_${groupID}`] = vc;
} catch (error) {
console.log(error)
}
}
compactIDB.writeData("appendix", floGlobals.appendix[`lastReceived_${groupID}`],
compactIDB.writeData("appendix", _loaded.appendix[`lastReceived_${groupID}`],
`lastReceived_${groupID}`);
if (infoChange) {
let newInfo = {
...floGlobals.groups[groupID]
..._loaded.groups[groupID]
}
newInfo.eKey = encrypt(newInfo.eKey)
compactIDB.writeData("groups", newInfo, groupID)
}
console.debug(newInbox);
UIcallback.group(newInbox);
UI.group(newInbox);
}
return floCloudAPI.requestApplicationData(null, {
receiverID: groupID,
lowerVectorClock: floGlobals.appendix[`lastReceived_${groupID}`] + 1,
callback: callbackFn
})
floCloudAPI.requestApplicationData(null, {
receiverID: groupID,
lowerVectorClock: _loaded.appendix[`lastReceived_${groupID}`] + 1,
callback: callbackFn
}).then(conn_id => groupConnID[groupID] = conn_id)
.catch(error => console.error(`request-group(${groupID}):`, error))
}
messenger.setUIcallbacks = function(directUI = null, groupUI = null) {
if (directUI instanceof Function)
UIcallback["direct"] = directUI
if (groupUI instanceof Function)
UIcallback["group"] = groupUI
}
messenger.initUserDB = function() {
const initUserDB = function() {
return new Promise((resolve, reject) => {
var obj = {
messages: {},
@ -173,6 +222,7 @@
chats: {},
groups: {},
gkeys: {},
blocked: {},
appendix: {},
userSettings: {}
}
@ -184,8 +234,30 @@
})
}
messenger.blockUser = function(floID) {
return new Promise((resolve, reject) => {
if (_loaded.blocked.has(floID))
return resolve("User is already blocked");
compactIDB.addData("blocked", true, floID).then(result => {
_loaded.blocked.add(floID);
resolve("Blocked User: " + floID);
}).catch(error => reject(error))
})
}
messenger.unblockUser = function(floID) {
return new Promise((resolve, reject) => {
if (!_loaded.blocked.has(floID))
return resolve("User is not blocked");
compactIDB.removeData("blocked", floID).then(result => {
_loaded.blocked.delete(floID);
resolve("Unblocked User: " + floID);
}).catch(error => reject(error))
})
}
messenger.sendMessage = function(message, receiver) {
return new Promise(async (resolve, reject) => {
return new Promise((resolve, reject) => {
sendRaw(message, receiver, "MESSAGE").then(result => {
let vc = result.vectorClock;
let data = {
@ -194,7 +266,7 @@
category: 'sent',
message: encrypt(message)
}
floGlobals.chats[receiver] = parseInt(vc)
_loaded.chats[receiver] = parseInt(vc)
compactIDB.writeData("chats", parseInt(vc), receiver)
compactIDB.addData("messages", {
...data
@ -208,7 +280,7 @@
}
messenger.sendMail = function(subject, content, recipients, prev = null) {
return new Promise(async (resolve, reject) => {
return new Promise((resolve, reject) => {
if (!Array.isArray(recipients))
recipients = [recipients]
let mail = {
@ -240,112 +312,114 @@
})
}
messenger.requestDirectInbox = function() {
return new Promise((resolve, reject) => {
let callbackFn = function(dataSet, error) {
if (error)
return console.error(error)
let newInbox = {
messages: {},
mails: {},
newgroups: [],
keyrevoke: []
}
console.log(dataSet)
for (let vc in dataSet) {
try {
//store the pubKey if not stored already
floDapps.storePubKey(dataSet[vc].senderID, dataSet[vc].pubKey)
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
let dm = {
time: dataSet[vc].time,
floID: dataSet[vc].senderID,
category: "received",
message: encrypt(dataSet[vc].message)
}
compactIDB.addData("messages", {
...dm
}, `${dm.floID}|${vc}`)
floGlobals.chats[dm.floID] = parseInt(vc)
compactIDB.writeData("chats", parseInt(vc), dm.floID)
dm.message = dataSet[vc].message;
newInbox.messages[vc] = dm;
addMark(dm.floID, "unread")
} else if (dataSet[vc].type === "MAIL") {
//process as mail
let data = JSON.parse(dataSet[vc].message);
let mail = {
time: dataSet[vc].time,
from: dataSet[vc].senderID,
to: [myFloID],
subject: data.subject,
content: encrypt(data.content),
ref: data.ref,
prev: data.prev
}
compactIDB.addData("mails", {
...mail
}, mail.ref);
mail.content = data.content;
newInbox.mails[mail.ref] = mail;
addMark(mail.ref, "unread")
} else if (dataSet[vc].type === "CREATE_GROUP") {
//process create group
let groupInfo = JSON.parse(dataSet[vc].message);
let h = ["groupID", "created", "admin"].map(x => groupInfo[x]).join('|')
if (groupInfo.admin === dataSet[vc].senderID &&
floCrypto.verifySign(h, groupInfo.hash, groupInfo.pubKey) &&
floCrypto.getFloID(groupInfo.pubKey) === groupInfo.groupID) {
let eKey = groupInfo.eKey
groupInfo.eKey = encrypt(eKey)
compactIDB.writeData("groups", {
...groupInfo
}, groupInfo.groupID)
groupInfo.eKey = eKey
floGlobals.groups[groupInfo.groupID] = groupInfo
groupConn(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
let eKey = r.newKey
groupInfo.eKey = encrypt(eKey);
compactIDB.writeData("groups", {
...groupInfo
}, groupInfo.groupID)
groupInfo.eKey = eKey
newInbox.keyrevoke.push(groupInfo.groupID)
}
const requestDirectInbox = function() {
if (directInboxConn) { //close existing request connection (if any)
floCloudAPI.closeRequest(directConnID);
directConnID = undefined;
}
let callbackFn = function(dataSet, error) {
if (error)
return console.error(error)
let newInbox = {
messages: {},
mails: {},
newgroups: [],
keyrevoke: []
}
console.debug("Check if key is vc", dataSet);
for (let vc in dataSet) {
try {
//store the pubKey if not stored already
floDapps.storePubKey(dataSet[vc].senderID, dataSet[vc].pubKey);
if (_loaded.blocked.has(dataSet[vc].senderID) && dataSet[vc].type !== "REVOKE_KEY")
throw "blocked-user";
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
let dm = {
time: dataSet[vc].time,
floID: dataSet[vc].senderID,
category: "received",
message: encrypt(dataSet[vc].message)
}
compactIDB.addData("messages", {
...dm
}, `${dm.floID}|${vc}`)
_loaded.chats[dm.floID] = parseInt(vc)
compactIDB.writeData("chats", parseInt(vc), dm.floID)
dm.message = dataSet[vc].message;
newInbox.messages[vc] = dm;
addMark(dm.floID, "unread")
} else if (dataSet[vc].type === "MAIL") {
//process as mail
let data = JSON.parse(dataSet[vc].message);
let mail = {
time: dataSet[vc].time,
from: dataSet[vc].senderID,
to: [myFloID],
subject: data.subject,
content: encrypt(data.content),
ref: data.ref,
prev: data.prev
}
compactIDB.addData("mails", {
...mail
}, mail.ref);
mail.content = data.content;
newInbox.mails[mail.ref] = mail;
addMark(mail.ref, "unread")
} else if (dataSet[vc].type === "CREATE_GROUP") {
//process create group
let groupInfo = JSON.parse(dataSet[vc].message);
let h = ["groupID", "created", "admin"].map(x => groupInfo[x]).join('|')
if (groupInfo.admin === dataSet[vc].senderID &&
floCrypto.verifySign(h, groupInfo.hash, groupInfo.pubKey) &&
floCrypto.getFloID(groupInfo.pubKey) === groupInfo.groupID) {
let eKey = groupInfo.eKey
groupInfo.eKey = encrypt(eKey)
compactIDB.writeData("groups", {
...groupInfo
}, groupInfo.groupID)
groupInfo.eKey = eKey
_loaded.groups[groupInfo.groupID] = groupInfo
requestGroupInbox(groupInfo.groupID)
newInbox.newgroups.push(groupInfo.groupID)
}
} else if (dataSet[vc].type === "REVOKE_KEY") {
let r = JSON.parse(dataSet[vc].message);
let groupInfo = _loaded.groups[r.groupID]
if (dataSet[vc].senderID === groupInfo.admin) {
if (typeof expiredKeys[r.groupID] !== "object")
expiredKeys[r.groupID] = {}
expiredKeys[r.groupID][vc] = groupInfo.eKey
let eKey = r.newKey
groupInfo.eKey = encrypt(eKey);
compactIDB.writeData("groups", {
...groupInfo
}, groupInfo.groupID)
groupInfo.eKey = eKey
newInbox.keyrevoke.push(groupInfo.groupID)
}
} catch (error) {
console.log(error)
} finally {
if (floGlobals.appendix.lastReceived < vc)
floGlobals.appendix.lastReceived = vc;
}
} catch (error) {
if (error !== "blocked-user")
console.log(error);
} finally {
if (_loaded.appendix.lastReceived < vc)
_loaded.appendix.lastReceived = vc;
}
compactIDB.writeData("appendix", floGlobals.appendix.lastReceived, "lastReceived");
console.debug(newInbox);
UIcallback.direct(newInbox)
}
var options = {
compactIDB.writeData("appendix", _loaded.appendix.lastReceived, "lastReceived");
console.debug(newInbox);
UI.direct(newInbox)
}
floCloudAPI.requestApplicationData(null, {
receiverID: myFloID,
lowerVectorClock: floGlobals.appendix.lastReceived + 1,
lowerVectorClock: _loaded.appendix.lastReceived + 1,
callback: callbackFn
}
floCloudAPI.requestApplicationData(null, options)
.then(result => resolve(result))
.catch(error => reject(error))
})
}).then(conn_id => directConnID = conn_id)
.catch(error => console.error("request-direct:", error));
}
messenger.getMail = function(mailRef) {
@ -357,35 +431,32 @@
});
}
messenger.getChatOrder = function(type = ["direct", "group", "mixed"]) {
if (typeof type === "string")
type = type.split('|')
let result = {}
if (type.includes("direct"))
result.direct = Object.keys(floGlobals.chats).map(a => [floGlobals.chats[a], a])
.sort((a, b) => b[0] - a[0]).map(a => a[1])
if (type.includes("group"))
result.group = Object.keys(floGlobals.groups).map(a => [parseInt(floGlobals.appendix[`lastReceived_${a}`]), a])
.sort((a, b) => b[0] - a[0]).map(a => a[1])
if (type.includes("mixed"))
result.mixed = Object.keys(floGlobals.chats).map(a => [floGlobals.chats[a], a])
.concat(Object.keys(floGlobals.groups).map(a => [parseInt(floGlobals.appendix[`lastReceived_${a}`]), a]))
.sort((a, b) => b[0] - a[0]).map(a => a[1])
if (type.length === 1)
result = result[type[0]]
return result
const getChatOrder = messenger.getChatOrder = function(seperate = false) {
let result;
if (seperate) {
result = {};
result.direct = Object.keys(_loaded.chats).map(a => [_loaded.chats[a], a])
.sort((a, b) => b[0] - a[0]).map(a => a[1]);
result.group = Object.keys(_loaded.groups).map(a => [parseInt(_loaded.appendix[`lastReceived_${a}`]), a])
.sort((a, b) => b[0] - a[0]).map(a => a[1]);
} else {
result = Object.keys(_loaded.chats).map(a => [_loaded.chats[a], a])
.concat(Object.keys(_loaded.groups).map(a => [parseInt(_loaded.appendix[`lastReceived_${a}`]), a]))
.sort((a, b) => b[0] - a[0]).map(a => a[1])
}
return result;
}
messenger.storeContact = function(floID, name) {
return floDapps.storeContact(floID, name)
}
const loadDataFromIDB = messenger.loadDataFromIDB = function(dataList = 'default') {
const loadDataFromIDB = function(defaultList = true) {
return new Promise((resolve, reject) => {
if (dataList === 'default')
dataList = ["mails", "marked", "groups", "chats", "appendix"]
else if (dataList === 'all')
dataList = ["messages", "mails", "marked", "chats", "groups", "gkeys", "appendix"]
if (defaultList)
dataList = ["mails", "marked", "groups", "chats", "blocked", "appendix"]
else
dataList = ["messages", "mails", "marked", "chats", "groups", "gkeys", "blocked", "appendix"]
let promises = []
for (var i = 0; i < dataList.length; i++)
promises[i] = compactIDB.readAllData(dataList[i])
@ -393,7 +464,7 @@
let data = {}
for (var i = 0; i < dataList.length; i++)
data[dataList[i]] = results[i]
data.appendix.lastReceived = data.appendix.lastReceived || '0'
data.appendix.lastReceived = data.appendix.lastReceived || '0';
if (data.appendix.AESKey) {
try {
let AESKey = floCrypto.decryptData(data.appendix.AESKey, myPrivKey);
@ -401,16 +472,16 @@
if (dataList.includes("messages"))
for (let m in data.messages)
if (data.messages[m].message)
data.messages[m].message = decrypt(data.messages[m].message, AESKey)
data.messages[m].message = decrypt(data.messages[m].message, AESKey);
if (dataList.includes("mails"))
for (let m in data.mails)
data.mails[m].content = decrypt(data.mails[m].content, AESKey)
data.mails[m].content = decrypt(data.mails[m].content, AESKey);
if (dataList.includes("groups"))
for (let g in data.groups)
data.groups[g].eKey = decrypt(data.groups[g].eKey, AESKey)
data.groups[g].eKey = decrypt(data.groups[g].eKey, AESKey);
if (dataList.includes("gkeys"))
for (let k in data.gkeys)
data.gkeys[k] = decrypt(data.gkeys[k], AESKey)
data.gkeys[k] = decrypt(data.gkeys[k], AESKey);
resolve(data)
} catch (error) {
reject("Corrupted AES Key");
@ -418,7 +489,7 @@
} else {
if (Object.keys(data.mails).length)
return reject("AES Key not Found")
let AESKey = floCrypto.randString(32);
let AESKey = floCrypto.randString(32, false);
let encryptedKey = floCrypto.encryptData(AESKey, myPubKey);
compactIDB.addData("appendix", encryptedKey, "AESKey").then(result => {
data.appendix.AESKey = AESKey;
@ -430,14 +501,14 @@
}
messenger.addMark = function(key, mark) {
if (floGlobals.marked.hasOwnProperty(key) && !floGlobals.marked[key].includes(mark))
floGlobals.marked[key].push(mark)
if (_loaded.marked.hasOwnProperty(key) && !_loaded.marked[key].includes(mark))
_loaded.marked[key].push(mark)
return addMark(key, mark)
}
messenger.removeMark = function(key, mark) {
if (floGlobals.marked.hasOwnProperty(key))
floGlobals.marked[key] = floGlobals.marked[key].filter(v => v !== mark)
if (_loaded.marked.hasOwnProperty(key))
_loaded.marked[key] = _loaded.marked[key].filter(v => v !== mark)
return removeMark(key, mark)
}
@ -491,7 +562,7 @@
messenger.backupData = function() {
return new Promise((resolve, reject) => {
loadDataFromIDB("all").then(data => {
loadDataFromIDB(false).then(data => {
delete data.appendix.AESKey;
data.contacts = floGlobals.contacts;
data.pubKeys = floGlobals.pubKeys;
@ -556,14 +627,14 @@
for (let g in data.groups)
data.groups[g].eKey = encrypt(data.groups[g].eKey)
for (let c in data.chats)
if (data.chats[c] <= floGlobals.chats[c])
if (data.chats[c] <= _loaded.chats[c])
delete data.chats[c]
for (let l in data.appendix)
if (l.startsWith('lastReceived') && data.appendix[l] <= floGlobals.appendix[l])
if (l.startsWith('lastReceived') && data.appendix[l] <= _loaded.appendix[l])
delete data.appendix[l]
for (let c in data.contacts)
if (c in floGlobals.contacts)
delete data.contact[c]
delete data.contacts[c]
for (let p in data.pubKeys)
if (p in floGlobals.pubKeys)
delete data.pubKeys[p]
@ -623,12 +694,12 @@
groupInfo.hash = floCrypto.signData(h, id.privKey)
let eKey = floCrypto.randString(16, false)
groupInfo.eKey = encrypt(eKey)
p1 = compactIDB.addData("groups", groupInfo, id.floID)
p2 = compactIDB.addData("gkeys", encrypt(id.privKey), id.floID)
let p1 = compactIDB.addData("groups", groupInfo, id.floID)
let p2 = compactIDB.addData("gkeys", encrypt(id.privKey), id.floID)
Promise.all([p1, p2]).then(r => {
groupInfo.eKey = eKey
floGlobals.groups[id.floID] = groupInfo;
groupConn(id.floID)
_loaded.groups[id.floID] = groupInfo;
requestGroupInbox(id.floID)
resolve(groupInfo)
}).catch(e => reject(e))
})
@ -636,7 +707,7 @@
messenger.changeGroupName = function(groupID, name) {
return new Promise((resolve, reject) => {
let groupInfo = floGlobals.groups[groupID]
let groupInfo = _loaded.groups[groupID]
if (myFloID !== groupInfo.admin)
return reject("Access denied: Admin only!")
let message = encrypt(name, groupInfo.eKey)
@ -648,7 +719,7 @@
messenger.changeGroupDescription = function(groupID, description) {
return new Promise((resolve, reject) => {
let groupInfo = floGlobals.groups[groupID]
let groupInfo = _loaded.groups[groupID]
if (myFloID !== groupInfo.admin)
return reject("Access denied: Admin only!")
let message = encrypt(description, groupInfo.eKey)
@ -674,11 +745,10 @@
else if (imem2.length)
return reject(`Invalid Members (pubKey not available): ${imem2}`)
//send new newMem list to existing members
let groupInfo = floGlobals.groups[groupID]
let groupInfo = _loaded.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 => sendRaw(groupInfo, m, "CREATE_GROUP", true));
Promise.allSettled(promises).then(results => {
@ -689,8 +759,8 @@
success.push(newMem[i])
else if (results[i].status === "rejected")
failed.push(newMem[i])
console.log(success.join("|"), k)
let message = encrypt(success.join("|"), k)
console.log(success.join("|"))
let message = encrypt(success.join("|"), groupInfo.eKey)
sendRaw(message, groupID, "ADD_MEMBERS", false, note)
.then(r => resolve(`Members added: ${success}`))
.catch(e => reject(e))
@ -702,16 +772,16 @@
return new Promise((resolve, reject) => {
if (!Array.isArray(rmMem) && typeof rmMem === "string")
rmMem = [rmMem]
let groupInfo = floGlobals.groups[groupID]
let groupInfo = _loaded.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 = encrypt(rmMem.join("|"), groupInfo.eKey)
p1 = sendRaw(message, groupID, "RM_MEMBERS", false, note)
let p1 = sendRaw(message, groupID, "RM_MEMBERS", false, note)
groupInfo.members = groupInfo.members.filter(m => !rmMem.includes(m))
p2 = revokeKey(groupID)
let p2 = revokeKey(groupID)
Promise.all([p1, p2])
.then(r => resolve(`Members removed: ${rmMem}`))
.catch(e => reject(e))
@ -720,7 +790,7 @@
const revokeKey = messenger.revokeKey = function(groupID) {
return new Promise((resolve, reject) => {
let groupInfo = floGlobals.groups[groupID]
let groupInfo = _loaded.groups[groupID]
if (myFloID !== groupInfo.admin)
return reject("Access denied: Admin only!")
let newKey = floCrypto.randString(16, false);
@ -734,8 +804,8 @@
}
messenger.sendGroupMessage = function(message, groupID) {
return new Promise(async (resolve, reject) => {
let k = floGlobals.groups[groupID].eKey
return new Promise((resolve, reject) => {
let k = _loaded.groups[groupID].eKey
message = encrypt(message, k)
sendRaw(message, groupID, "GROUP_MSG", false)
.then(result => resolve(`${groupID}: ${message}`))
@ -743,32 +813,46 @@
})
}
messenger.requestGroupInbox = function() {
return new Promise((resolve) => {
let promises = []
let reqFn = (g) => new Promise((res, rej) => {
groupConn(g)
.then(r => res([g, r]))
.catch(e => rej([g, e]))
})
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)
const disableGroup = messenger.disableGroup = function(groupID) {
return new Promise((resolve, reject) => {
if (!_loaded.groups[groupID])
return reject("Group not found");
let groupInfo = {
..._loaded.groups[groupID]
};
if (groupInfo.disabled)
return resolve("Group already diabled");
groupInfo.disabled = true;
groupInfo.eKey = encrypt(groupInfo.eKey)
compactIDB.writeData("groups", groupInfo, groupID).then(result => {
floCloudAPI.closeRequest(groupConnID[groupID]);
delete groupConnID[groupID];
resolve("Group diabled");
}).catch(error => reject(error))
})
}
messenger.init = function() {
return new Promise((resolve, reject) => {
initUserDB().then(result => {
console.debug(result);
loadDataFromIDB().then(data => {
console.debug(data);
//load data to memory
_loaded.appendix = data.appendix;
_loaded.groups = data.groups;
_loaded.chats = data.chats;
_loaded.marked = data.marked;
_loaded.blocked = new Set(Object.keys(data.blocked));
//call UI render functions
UI.chats(getChatOrder());
UI.mails(data.mail);
UI.marked(data.marked);
//request data from cloud
requestDirectInbox();
data.groups.map(g => requestGroupInbox(g))
resolve("Messenger initiated");
}).catch(error => reject(error));
})
})
}