|
|
|
@ -2,9 +2,10 @@ define([
|
|
|
|
|
'/common/common-util.js',
|
|
|
|
|
'/common/common-hash.js',
|
|
|
|
|
'/common/common-realtime.js',
|
|
|
|
|
'/common/outer/mailbox-handlers.js',
|
|
|
|
|
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
|
|
|
|
'/bower_components/chainpad-crypto/crypto.js',
|
|
|
|
|
], function (Util, Hash, Realtime, CpNetflux, Crypto) {
|
|
|
|
|
], function (Util, Hash, Realtime, Handlers, CpNetflux, Crypto) {
|
|
|
|
|
var Mailbox = {};
|
|
|
|
|
|
|
|
|
|
var TYPES = [
|
|
|
|
@ -14,13 +15,16 @@ define([
|
|
|
|
|
var BLOCKING_TYPES = [
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
var initializeMailboxes = function (mailboxes) {
|
|
|
|
|
var initializeMailboxes = function (ctx, mailboxes) {
|
|
|
|
|
if (!mailboxes['notifications']) {
|
|
|
|
|
mailboxes.notifications = {
|
|
|
|
|
channel: Hash.createChannelId(),
|
|
|
|
|
lastKnownHash: '',
|
|
|
|
|
viewed: []
|
|
|
|
|
};
|
|
|
|
|
ctx.pinPads([mailboxes.notifications.channel], function (res) {
|
|
|
|
|
if (res.error) { console.error(res); }
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -55,6 +59,108 @@ proxy.mailboxes = {
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Send a message to someone else
|
|
|
|
|
var sendTo = function (ctx, type, msg, user, cb) {
|
|
|
|
|
if (!Crypto.Mailbox) {
|
|
|
|
|
return void cb({error: "chainpad-crypto is outdated and doesn't support mailboxes."});
|
|
|
|
|
}
|
|
|
|
|
var keys = getMyKeys(ctx);
|
|
|
|
|
if (!keys) { return void cb({error: "missing asymmetric encryption keys"}); }
|
|
|
|
|
if (!user || !user.channel || !user.curvePublic) { return void cb({error: "no notification channel"}); }
|
|
|
|
|
|
|
|
|
|
var crypto = Crypto.Mailbox.createEncryptor(keys);
|
|
|
|
|
var network = ctx.store.network;
|
|
|
|
|
|
|
|
|
|
var ciphertext = crypto.encrypt(JSON.stringify({
|
|
|
|
|
type: type,
|
|
|
|
|
content: msg
|
|
|
|
|
}), user.curvePublic);
|
|
|
|
|
|
|
|
|
|
network.join(user.channel).then(function (wc) {
|
|
|
|
|
wc.bcast(ciphertext).then(function () {
|
|
|
|
|
cb();
|
|
|
|
|
wc.leave();
|
|
|
|
|
});
|
|
|
|
|
}, function (err) {
|
|
|
|
|
cb({error: err});
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var updateLastKnownHash = function (ctx, type) {
|
|
|
|
|
var m = Util.find(ctx, ['store', 'proxy', 'mailboxes', type]);
|
|
|
|
|
if (!m) { return; }
|
|
|
|
|
var box = ctx.boxes[type];
|
|
|
|
|
if (!box) { return; }
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Mark a message as read
|
|
|
|
|
var dismiss = function (ctx, data, cId, cb) {
|
|
|
|
|
var type = data.type;
|
|
|
|
|
var hash = data.hash;
|
|
|
|
|
var m = Util.find(ctx, ['store', 'proxy', 'mailboxes', type]);
|
|
|
|
|
if (!m) { return void cb({error: 'NOT_FOUND'}); }
|
|
|
|
|
var box = ctx.boxes[type];
|
|
|
|
|
if (!box) { return void cb({error: 'NOT_LOADED'}); }
|
|
|
|
|
|
|
|
|
|
// If the hash in in our history, get the index from the history:
|
|
|
|
|
// - if the index is 0, we can change our lastKnownHash
|
|
|
|
|
// - otherwise, just push to view
|
|
|
|
|
var idx;
|
|
|
|
|
if (box.history.some(function (el, i) {
|
|
|
|
|
if (hash === el) {
|
|
|
|
|
idx = i;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
})) {
|
|
|
|
|
if (idx === 0) {
|
|
|
|
|
m.lastKnownHash = hash;
|
|
|
|
|
box.history.shift();
|
|
|
|
|
delete box.content[hash];
|
|
|
|
|
} else if (m.viewed.indexOf(hash) === -1) {
|
|
|
|
|
m.viewed.push(hash);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear data in memory if needed
|
|
|
|
|
// Check the "viewed" array to see if we're able to bump lastKnownhash more
|
|
|
|
|
var sliceIdx;
|
|
|
|
|
var lastKnownHash;
|
|
|
|
|
box.history.some(function (hash, i) {
|
|
|
|
|
var isViewed = m.viewed.indexOf(hash);
|
|
|
|
|
if (isViewed !== -1) {
|
|
|
|
|
sliceIdx = i + 1;
|
|
|
|
|
m.viewed.splice(isViewed, 1);
|
|
|
|
|
lastKnownHash = hash;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (sliceIdx) {
|
|
|
|
|
box.history = box.history.slice(sliceIdx);
|
|
|
|
|
m.lastKnownHash = lastKnownHash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make sure we remove data about dismissed messages
|
|
|
|
|
Object.keys(box.content).forEach(function (h) {
|
|
|
|
|
if (box.history.indexOf(h) === -1 || m.viewed.indexOf(h) !== -1) {
|
|
|
|
|
delete box.content[h];
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Realtime.whenRealtimeSyncs(ctx.store.realtime, function () {
|
|
|
|
|
cb();
|
|
|
|
|
ctx.emit('VIEWED', {
|
|
|
|
|
type: type,
|
|
|
|
|
hash: hash
|
|
|
|
|
}, ctx.clients.filter(function (clientId) {
|
|
|
|
|
return clientId !== cId;
|
|
|
|
|
}));
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var openChannel = function (ctx, type, m, onReady) {
|
|
|
|
|
var box = ctx.boxes[type] = {
|
|
|
|
|
queue: [], // Store the messages to send when the channel is ready
|
|
|
|
@ -126,8 +232,19 @@ proxy.mailboxes = {
|
|
|
|
|
msg: msg,
|
|
|
|
|
hash: hash
|
|
|
|
|
};
|
|
|
|
|
box.content[hash] = msg;
|
|
|
|
|
showMessage(ctx, type, message);
|
|
|
|
|
Handlers(ctx, box, message, function (toDismiss) {
|
|
|
|
|
if (toDismiss) {
|
|
|
|
|
dismiss(ctx, {
|
|
|
|
|
type: type,
|
|
|
|
|
hash: hash
|
|
|
|
|
}, '', function () {
|
|
|
|
|
console.log('Notification handled automatically');
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
box.content[hash] = msg;
|
|
|
|
|
showMessage(ctx, type, message);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
// Message has already been viewed by the user
|
|
|
|
|
if (Object.keys(box.content).length === 0) {
|
|
|
|
@ -182,105 +299,6 @@ proxy.mailboxes = {
|
|
|
|
|
CpNetflux.start(cfg);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Send a message to someone else
|
|
|
|
|
var sendTo = function (ctx, type, msg, user, cb) {
|
|
|
|
|
if (!Crypto.Mailbox) {
|
|
|
|
|
return void cb({error: "chainpad-crypto is outdated and doesn't support mailboxes."});
|
|
|
|
|
}
|
|
|
|
|
var keys = getMyKeys(ctx);
|
|
|
|
|
if (!keys) { return void cb({error: "missing asymmetric encryption keys"}); }
|
|
|
|
|
if (!user || !user.channel || !user.curvePublic) { return void cb({error: "no notification channel"}); }
|
|
|
|
|
|
|
|
|
|
var crypto = Crypto.Mailbox.createEncryptor(keys);
|
|
|
|
|
var network = ctx.store.network;
|
|
|
|
|
|
|
|
|
|
var ciphertext = crypto.encrypt(JSON.stringify({
|
|
|
|
|
type: type,
|
|
|
|
|
content: msg
|
|
|
|
|
}), user.curvePublic);
|
|
|
|
|
|
|
|
|
|
network.join(user.channel).then(function (wc) {
|
|
|
|
|
wc.bcast(ciphertext).then(function () {
|
|
|
|
|
cb();
|
|
|
|
|
});
|
|
|
|
|
}, function (err) {
|
|
|
|
|
cb({error: err});
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var updateLastKnownHash = function (ctx, type) {
|
|
|
|
|
var m = Util.find(ctx, ['store', 'proxy', 'mailboxes', type]);
|
|
|
|
|
if (!m) { return; }
|
|
|
|
|
var box = ctx.boxes[type];
|
|
|
|
|
if (!box) { return; }
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Mark a message as read
|
|
|
|
|
var dismiss = function (ctx, data, cId, cb) {
|
|
|
|
|
var type = data.type;
|
|
|
|
|
var hash = data.hash;
|
|
|
|
|
var m = Util.find(ctx, ['store', 'proxy', 'mailboxes', type]);
|
|
|
|
|
if (!m) { return void cb({error: 'NOT_FOUND'}); }
|
|
|
|
|
var box = ctx.boxes[type];
|
|
|
|
|
if (!box) { return void cb({error: 'NOT_LOADED'}); }
|
|
|
|
|
|
|
|
|
|
// If the hash in in our history, get the index from the history:
|
|
|
|
|
// - if the index is 0, we can change our lastKnownHash
|
|
|
|
|
// - otherwise, just push to view
|
|
|
|
|
var idx;
|
|
|
|
|
if (box.history.some(function (el, i) {
|
|
|
|
|
if (hash === el) {
|
|
|
|
|
idx = i;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
})) {
|
|
|
|
|
if (idx === 0) {
|
|
|
|
|
m.lastKnownHash = hash;
|
|
|
|
|
box.history.shift();
|
|
|
|
|
delete box.content[hash];
|
|
|
|
|
} else if (m.viewed.indexOf(hash) === -1) {
|
|
|
|
|
m.viewed.push(hash);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear data in memory if needed
|
|
|
|
|
// Check the "viewed" array to see if we're able to bump lastKnownhash more
|
|
|
|
|
var sliceIdx;
|
|
|
|
|
var lastKnownHash;
|
|
|
|
|
box.history.some(function (hash, i) {
|
|
|
|
|
var isViewed = m.viewed.indexOf(hash);
|
|
|
|
|
if (isViewed !== -1) {
|
|
|
|
|
sliceIdx = i + 1;
|
|
|
|
|
m.viewed.splice(isViewed, 1);
|
|
|
|
|
lastKnownHash = hash;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (sliceIdx) {
|
|
|
|
|
box.history = box.history.slice(sliceIdx);
|
|
|
|
|
m.lastKnownHash = lastKnownHash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make sure we remove data about dismissed messages
|
|
|
|
|
Object.keys(box.content).forEach(function (h) {
|
|
|
|
|
if (box.history.indexOf(h) === -1 || m.viewed.indexOf(h) !== -1) {
|
|
|
|
|
delete box.content[h];
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Realtime.whenRealtimeSyncs(ctx.store.realtime, function () {
|
|
|
|
|
cb();
|
|
|
|
|
ctx.emit('VIEWED', {
|
|
|
|
|
type: type,
|
|
|
|
|
hash: hash
|
|
|
|
|
}, ctx.clients.filter(function (clientId) {
|
|
|
|
|
return clientId !== cId;
|
|
|
|
|
}));
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var subscribe = function (ctx, data, cId, cb) {
|
|
|
|
|
// Get existing notifications
|
|
|
|
@ -306,10 +324,13 @@ proxy.mailboxes = {
|
|
|
|
|
ctx.clients.splice(idx, 1);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Mailbox.init = function (store, waitFor, emit) {
|
|
|
|
|
Mailbox.init = function (cfg, waitFor, emit) {
|
|
|
|
|
var mailbox = {};
|
|
|
|
|
var store = cfg.store;
|
|
|
|
|
var ctx = {
|
|
|
|
|
store: store,
|
|
|
|
|
pinPads: cfg.pinPads,
|
|
|
|
|
updateMetadata: cfg.updateMetadata,
|
|
|
|
|
emit: emit,
|
|
|
|
|
clients: [],
|
|
|
|
|
boxes: {}
|
|
|
|
@ -317,7 +338,7 @@ proxy.mailboxes = {
|
|
|
|
|
|
|
|
|
|
var mailboxes = store.proxy.mailboxes = store.proxy.mailboxes || {};
|
|
|
|
|
|
|
|
|
|
initializeMailboxes(mailboxes);
|
|
|
|
|
initializeMailboxes(ctx, mailboxes);
|
|
|
|
|
|
|
|
|
|
Object.keys(mailboxes).forEach(function (key) {
|
|
|
|
|
if (TYPES.indexOf(key) === -1) { return; }
|
|
|
|
@ -346,6 +367,10 @@ proxy.mailboxes = {
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
mailbox.dismiss = function (data, cb) {
|
|
|
|
|
dismiss(ctx, data, '', cb);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
mailbox.sendTo = function (type, msg, user, cb) {
|
|
|
|
|
sendTo(ctx, type, msg, user, cb);
|
|
|
|
|
};
|
|
|
|
|