mirror of https://github.com/xwiki-labs/cryptpad
Friend requests with mailboxes
This commit is contained in:
parent
0732773bba
commit
991c56fec3
|
@ -15,12 +15,21 @@
|
|||
.cp-notification-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
p {
|
||||
word-break: break-all;
|
||||
}
|
||||
&.cp-clickable {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: rgba(0,0,0,0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-notification-dismiss {
|
||||
color: black;
|
||||
width: 25px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
span {
|
||||
|
|
|
@ -7,14 +7,7 @@ define([
|
|||
|
||||
'/common/common-realtime.js',
|
||||
], function (Crypto, Hash, Util, Constants, Messages, Realtime) {
|
||||
var Msg = {
|
||||
inputs: [],
|
||||
};
|
||||
|
||||
// TODO
|
||||
// - mute a channel (hide notifications or don't open it?)
|
||||
var pending = {};
|
||||
var pendingRequests = [];
|
||||
var Msg = {};
|
||||
|
||||
var createData = Msg.createData = function (proxy, hash) {
|
||||
return {
|
||||
|
@ -23,11 +16,13 @@ define([
|
|||
profile: proxy.profile && proxy.profile.view,
|
||||
edPublic: proxy.edPublic,
|
||||
curvePublic: proxy.curvePublic,
|
||||
notifications: Util.find(proxy, ['mailboxes', 'notifications', 'channel']),
|
||||
avatar: proxy.profile && proxy.profile.avatar
|
||||
};
|
||||
};
|
||||
|
||||
var getFriend = function (proxy, pubkey) {
|
||||
var getFriend = Msg.getFriend = function (proxy, pubkey) {
|
||||
if (!pubkey) { return; }
|
||||
if (pubkey === proxy.curvePublic) {
|
||||
var data = createData(proxy);
|
||||
delete data.channel;
|
||||
|
@ -56,21 +51,18 @@ define([
|
|||
return list;
|
||||
};
|
||||
|
||||
// TODO make this internal to the messenger
|
||||
var channels = Msg.channels = {};
|
||||
|
||||
Msg.getLatestMessages = function () {
|
||||
Object.keys(channels).forEach(function (id) {
|
||||
if (id === 'me') { return; }
|
||||
var friend = channels[id];
|
||||
friend.getMessagesSinceDisconnect();
|
||||
friend.refresh();
|
||||
Msg.acceptFriendRequest = function (store, data, cb) {
|
||||
var friend = getFriend(store.proxy, data.curvePublic) || {};
|
||||
var myData = createData(store.proxy, friend.channel || data.channel);
|
||||
store.mailbox.sendTo('ACCEPT_FRIEND_REQUEST', myData, {
|
||||
channel: data.notifications,
|
||||
curvePublic: data.curvePublic
|
||||
}, function (obj) {
|
||||
cb(obj);
|
||||
if (obj && obj.error) { return void cb(obj); }
|
||||
});
|
||||
};
|
||||
|
||||
// Invitation
|
||||
// FIXME there are too many functions with this name
|
||||
var addToFriendList = Msg.addToFriendList = function (cfg, data, cb) {
|
||||
Msg.addToFriendList = function (cfg, data, cb) {
|
||||
var proxy = cfg.proxy;
|
||||
var friends = getFriendList(proxy);
|
||||
var pubKey = data.curvePublic; // todo validata data
|
||||
|
@ -85,135 +77,6 @@ define([
|
|||
if (res.error) { console.error(res.error); }
|
||||
});
|
||||
});
|
||||
cfg.updateMetadata();
|
||||
};
|
||||
|
||||
/* Used to accept friend requests within apps other than /contacts/ */
|
||||
Msg.addDirectMessageHandler = function (cfg, href) {
|
||||
var network = cfg.network;
|
||||
var proxy = cfg.proxy;
|
||||
if (!network) { return void console.error('Network not ready'); }
|
||||
network.on('message', function (message, sender) {
|
||||
var msg;
|
||||
if (sender === network.historyKeeper) { return; }
|
||||
try {
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
var secret = Hash.getSecrets(parsed.type, parsed.hash);
|
||||
if (!parsed.hashData) { return; }
|
||||
var chan = secret.channel;
|
||||
// Decrypt
|
||||
var key = secret.keys ? secret.keys.cryptKey : Hash.decodeBase64(secret.key);
|
||||
var decryptMsg;
|
||||
try {
|
||||
decryptMsg = Crypto.decrypt(message, key);
|
||||
} catch (e) {
|
||||
// If we can't decrypt, it means it is not a friend request message
|
||||
}
|
||||
if (!decryptMsg) { return; }
|
||||
// Parse
|
||||
msg = JSON.parse(decryptMsg);
|
||||
if (msg[1] !== chan) { return; }
|
||||
var msgData = msg[2];
|
||||
var msgStr;
|
||||
if (msg[0] === "FRIEND_REQ") {
|
||||
msg = ["FRIEND_REQ_NOK", chan];
|
||||
var todo = function (yes) {
|
||||
if (yes) {
|
||||
pending[sender] = msgData;
|
||||
msg = ["FRIEND_REQ_OK", chan, createData(proxy, msgData.channel)];
|
||||
}
|
||||
msgStr = Crypto.encrypt(JSON.stringify(msg), key);
|
||||
network.sendto(sender, msgStr);
|
||||
};
|
||||
var existing = getFriend(proxy, msgData.curvePublic);
|
||||
if (existing) {
|
||||
todo(true);
|
||||
return;
|
||||
}
|
||||
var confirmMsg = Messages._getKey('contacts_request', [
|
||||
Util.fixHTML(msgData.displayName)
|
||||
]);
|
||||
cfg.friendRequest(confirmMsg, todo);
|
||||
return;
|
||||
}
|
||||
if (msg[0] === "FRIEND_REQ_OK") {
|
||||
var idx = pendingRequests.indexOf(sender);
|
||||
if (idx !== -1) { pendingRequests.splice(idx, 1); }
|
||||
|
||||
// FIXME clarify this function's name
|
||||
addToFriendList(cfg, msgData, function (err) {
|
||||
if (err) {
|
||||
return void cfg.friendComplete({
|
||||
logText: Messages.contacts_addError,
|
||||
netfluxId: sender
|
||||
});
|
||||
}
|
||||
cfg.friendComplete({
|
||||
logText: Messages.contacts_added,
|
||||
netfluxId: sender,
|
||||
friend: msgData
|
||||
});
|
||||
var msg = ["FRIEND_REQ_ACK", chan];
|
||||
var msgStr = Crypto.encrypt(JSON.stringify(msg), key);
|
||||
network.sendto(sender, msgStr);
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (msg[0] === "FRIEND_REQ_NOK") {
|
||||
var i = pendingRequests.indexOf(sender);
|
||||
if (i !== -1) { pendingRequests.splice(i, 1); }
|
||||
cfg.friendComplete({
|
||||
logText: Messages.contacts_rejected,
|
||||
netfluxId: sender,
|
||||
});
|
||||
cfg.updateMetadata();
|
||||
return;
|
||||
}
|
||||
if (msg[0] === "FRIEND_REQ_ACK") {
|
||||
var data = pending[sender];
|
||||
if (!data) { return; }
|
||||
addToFriendList(cfg, data, function (err) {
|
||||
if (err) {
|
||||
return void cfg.friendComplete({
|
||||
logText: Messages.contacts_addError,
|
||||
netfluxId: sender
|
||||
});
|
||||
}
|
||||
cfg.friendComplete({
|
||||
logText: Messages.contacts_added,
|
||||
netfluxId: sender,
|
||||
friend: data
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
// TODO: timeout ACK: warn the user
|
||||
} catch (e) {
|
||||
console.error("Cannot parse direct message", msg || message, "from", sender, e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Msg.inviteFromUserlist = function (cfg, data, cb) {
|
||||
var network = cfg.network;
|
||||
var netfluxId = data.netfluxId;
|
||||
var parsed = Hash.parsePadUrl(data.href);
|
||||
var secret = Hash.getSecrets(parsed.type, parsed.hash);
|
||||
if (!parsed.hashData) { return; }
|
||||
// Message
|
||||
var chan = secret.channel;
|
||||
var myData = createData(cfg.proxy);
|
||||
var msg = ["FRIEND_REQ", chan, myData];
|
||||
// Encryption
|
||||
var key = secret.keys ? secret.keys.cryptKey : Hash.decodeBase64(secret.key);
|
||||
var msgStr = Crypto.encrypt(JSON.stringify(msg), key);
|
||||
// Send encrypted message
|
||||
if (pendingRequests.indexOf(netfluxId) === -1) {
|
||||
pendingRequests.push(netfluxId);
|
||||
cfg.updateMetadata(); // redraws the userlist in pad
|
||||
}
|
||||
network.sendto(netfluxId, msgStr);
|
||||
cb();
|
||||
};
|
||||
|
||||
return Msg;
|
||||
|
|
|
@ -2607,5 +2607,25 @@ define([
|
|||
return m;
|
||||
};
|
||||
|
||||
UIElements.displayFriendRequestModal = function (common, data) {
|
||||
var msg = data.content.msg;
|
||||
var text = Messages._getKey('contacts_request', [msg.content.displayName]);
|
||||
UI.confirm(text, function (yes) {
|
||||
common.getSframeChannel().query("Q_ANSWER_FRIEND_REQUEST", {
|
||||
data: data,
|
||||
value: yes
|
||||
}, function (err, obj) {
|
||||
var error = err || (obj && obj.error);
|
||||
if (error) {
|
||||
return void UI.warn(error);
|
||||
}
|
||||
UI.log(Messages.contacts_added);
|
||||
});
|
||||
}, {
|
||||
ok: 'Accept', // XXX
|
||||
cancel: 'Ignore the request' // XXX
|
||||
}, true);
|
||||
};
|
||||
|
||||
return UIElements;
|
||||
});
|
||||
|
|
|
@ -604,16 +604,6 @@ define([
|
|||
});
|
||||
};
|
||||
|
||||
// Messaging (manage friends from the userlist)
|
||||
common.inviteFromUserlist = function (netfluxId, cb) {
|
||||
postMessage("INVITE_FROM_USERLIST", {
|
||||
netfluxId: netfluxId,
|
||||
href: window.location.href
|
||||
}, function (obj) {
|
||||
if (obj && obj.error) { return void cb(obj.error); }
|
||||
cb();
|
||||
});
|
||||
};
|
||||
|
||||
// Admin
|
||||
common.adminRpc = function (data, cb) {
|
||||
|
@ -625,14 +615,13 @@ define([
|
|||
common.onNetworkReconnect = Util.mkEvent();
|
||||
common.onNewVersionReconnect = Util.mkEvent();
|
||||
|
||||
// Messaging
|
||||
// Messaging (friend requests)
|
||||
var messaging = common.messaging = {};
|
||||
messaging.onFriendRequest = Util.mkEvent();
|
||||
messaging.onFriendComplete = Util.mkEvent();
|
||||
messaging.addHandlers = function (href) {
|
||||
postMessage("ADD_DIRECT_MESSAGE_HANDLERS", {
|
||||
href: href
|
||||
});
|
||||
messaging.answerFriendRequest = function (data, cb) {
|
||||
postMessage("ANSWER_FRIEND_REQUEST", data, cb);
|
||||
};
|
||||
messaging.sendFriendRequest = function (data, cb) {
|
||||
postMessage("SEND_FRIEND_REQUEST", data, cb);
|
||||
};
|
||||
|
||||
// Onlyoffice
|
||||
|
@ -1081,9 +1070,6 @@ define([
|
|||
var localToken = tryParsing(localStorage.getItem(Constants.tokenKey));
|
||||
if (localToken !== data.token) { requestLogin(); }
|
||||
},
|
||||
// Messaging
|
||||
Q_FRIEND_REQUEST: common.messaging.onFriendRequest.fire,
|
||||
EV_FRIEND_COMPLETE: common.messaging.onFriendComplete.fire,
|
||||
// Network
|
||||
NETWORK_DISCONNECT: common.onNetworkDisconnect.fire,
|
||||
NETWORK_RECONNECT: function (data) {
|
||||
|
|
|
@ -461,7 +461,8 @@ define([
|
|||
friends: store.proxy.friends || {},
|
||||
settings: store.proxy.settings,
|
||||
thumbnails: disableThumbnails === false,
|
||||
isDriveOwned: Boolean(Util.find(store, ['driveMetadata', 'owners']))
|
||||
isDriveOwned: Boolean(Util.find(store, ['driveMetadata', 'owners'])),
|
||||
pendingFriends: store.proxy.friends_pending || {}
|
||||
}
|
||||
};
|
||||
cb(JSON.parse(JSON.stringify(metadata)));
|
||||
|
@ -902,36 +903,63 @@ define([
|
|||
|
||||
|
||||
// Messaging (manage friends from the userlist)
|
||||
var getMessagingCfg = function (clientId) {
|
||||
return {
|
||||
proxy: store.proxy,
|
||||
realtime: store.realtime,
|
||||
network: store.network,
|
||||
updateMetadata: function () {
|
||||
postMessage(clientId, "UPDATE_METADATA");
|
||||
},
|
||||
pinPads: function (data, cb) { Store.pinPads(null, data, cb); },
|
||||
friendComplete: function (data) {
|
||||
if (data.friend && store.messenger && store.messenger.onFriendAdded) {
|
||||
store.messenger.onFriendAdded(data.friend);
|
||||
}
|
||||
postMessage(clientId, "EV_FRIEND_COMPLETE", data);
|
||||
},
|
||||
friendRequest: function (data, cb) {
|
||||
postMessage(clientId, "Q_FRIEND_REQUEST", data, cb);
|
||||
},
|
||||
};
|
||||
};
|
||||
Store.inviteFromUserlist = function (clientId, data, cb) {
|
||||
var messagingCfg = getMessagingCfg(clientId);
|
||||
Messaging.inviteFromUserlist(messagingCfg, data, cb);
|
||||
};
|
||||
Store.addDirectMessageHandlers = function (clientId, data) {
|
||||
var messagingCfg = getMessagingCfg(clientId);
|
||||
Messaging.addDirectMessageHandler(messagingCfg, data.href);
|
||||
};
|
||||
Store.answerFriendRequest = function (clientId, obj, cb) {
|
||||
console.log(obj);
|
||||
var value = obj.value;
|
||||
var data = obj.data;
|
||||
if (data.type !== 'notifications') { return void cb ({error: 'EINVAL'}); }
|
||||
var hash = data.content.hash;
|
||||
var msg = data.content.msg;
|
||||
|
||||
// Messenger
|
||||
var dismiss = function (cb) {
|
||||
cb = cb || function () {};
|
||||
store.mailbox.dismiss({
|
||||
hash: hash,
|
||||
type: 'notifications'
|
||||
}, cb);
|
||||
};
|
||||
|
||||
if (value) {
|
||||
Messaging.acceptFriendRequest(store, msg.content, function (obj) {
|
||||
if (obj && obj.error) { return void cb(obj); }
|
||||
Messaging.addToFriendList({
|
||||
proxy: store.proxy,
|
||||
realtime: store.realtime,
|
||||
pinPads: function (data, cb) { Store.pinPads(null, data, cb); },
|
||||
}, msg.content, function (err) {
|
||||
broadcast([], "UPDATE_METADATA");
|
||||
if (err) { return void cb({error: err}); }
|
||||
dismiss(cb);
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
dismiss();
|
||||
};
|
||||
Store.sendFriendRequest = function (clientId, data, cb) {
|
||||
var friend = Messaging.getFriend(store.proxy, data.curvePublic);
|
||||
if (friend) { return void cb({error: 'ALREADY_FRIEND'}); }
|
||||
if (!data.notifications || !data.curvePublic) { return void cb({error: 'INVALID_USER'}); }
|
||||
|
||||
store.proxy.friends_pending = store.proxy.friends_pending || {};
|
||||
|
||||
var twoDaysAgo = +new Date(); // (+new Date() - (2 * 24 * 3600 * 1000)); // XXX
|
||||
if (store.proxy.friends_pending[data.curvePublic] &&
|
||||
store.proxy.friends_pending[data.curvePublic] > twoDaysAgo) {
|
||||
return void cb({error: 'TIMEOUT'});
|
||||
}
|
||||
|
||||
store.proxy.friends_pending[data.curvePublic] = +new Date();
|
||||
broadcast([], "UPDATE_METADATA");
|
||||
|
||||
var myData = Messaging.createData(store.proxy);
|
||||
store.mailbox.sendTo('FRIEND_REQUEST', myData, {
|
||||
channel: data.notifications,
|
||||
curvePublic: data.curvePublic
|
||||
}, function (obj) {
|
||||
cb(obj);
|
||||
});
|
||||
};
|
||||
|
||||
// Get hashes for the share button
|
||||
Store.getStrongerHash = function (clientId, data, cb) {
|
||||
|
@ -946,6 +974,7 @@ define([
|
|||
cb();
|
||||
};
|
||||
|
||||
// Messenger
|
||||
Store.messenger = {
|
||||
execCommand: function (clientId, data, cb) {
|
||||
if (!store.messenger) { return void cb({error: 'Messenger is disabled'}); }
|
||||
|
@ -1444,7 +1473,13 @@ define([
|
|||
if (!store.loggedIn || !store.proxy.edPublic) {
|
||||
return;
|
||||
}
|
||||
store.mailbox = Mailbox.init(store, waitFor, function (ev, data, clients) {
|
||||
store.mailbox = Mailbox.init({
|
||||
store: store,
|
||||
updateMetadata: function () {
|
||||
broadcast([], "UPDATE_METADATA");
|
||||
},
|
||||
pinPads: function (data, cb) { Store.pinPads(null, data, cb); },
|
||||
}, waitFor, function (ev, data, clients) {
|
||||
clients.forEach(function (cId) {
|
||||
postMessage(cId, 'MAILBOX_EVENT', {
|
||||
ev: ev,
|
||||
|
@ -1606,6 +1641,10 @@ define([
|
|||
// Trigger userlist update when the friendlist has changed
|
||||
broadcast([], "UPDATE_METADATA");
|
||||
});
|
||||
proxy.on('change', ['friends_pending'], function () {
|
||||
// Trigger userlist update when the friendlist has changed
|
||||
broadcast([], "UPDATE_METADATA");
|
||||
});
|
||||
proxy.on('change', ['settings'], function () {
|
||||
broadcast([], "UPDATE_METADATA");
|
||||
});
|
||||
|
|
|
@ -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,133 +59,6 @@ proxy.mailboxes = {
|
|||
};
|
||||
};
|
||||
|
||||
var openChannel = function (ctx, type, m, onReady) {
|
||||
var box = ctx.boxes[type] = {
|
||||
queue: [], // Store the messages to send when the channel is ready
|
||||
history: [], // All the hashes loaded from the server in corretc order
|
||||
content: {}, // Content of the messages that should be displayed
|
||||
sendMessage: function (msg) { // To send a message to our box
|
||||
try {
|
||||
msg = JSON.stringify(msg);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
box.queue.push(msg);
|
||||
}
|
||||
};
|
||||
Crypto = Crypto;
|
||||
if (!Crypto.Mailbox) {
|
||||
return void console.error("chainpad-crypto is outdated and doesn't support mailboxes.");
|
||||
}
|
||||
var keys = getMyKeys(ctx);
|
||||
if (!keys) { return void console.error("missing asymmetric encryption keys"); }
|
||||
var crypto = Crypto.Mailbox.createEncryptor(keys);
|
||||
// XXX remove 'test'
|
||||
if (type === 'test') {
|
||||
crypto = {
|
||||
encrypt: function (x) { return x; },
|
||||
decrypt: function (x) { return x; }
|
||||
};
|
||||
}
|
||||
var cfg = {
|
||||
network: ctx.store.network,
|
||||
channel: m.channel,
|
||||
noChainPad: true,
|
||||
crypto: crypto,
|
||||
owners: [ctx.store.proxy.edPublic],
|
||||
lastKnownHash: m.lastKnownHash
|
||||
};
|
||||
cfg.onConnect = function (wc, sendMessage) {
|
||||
// Send a message to our box?
|
||||
// NOTE: we use our own curvePublic so that we can decrypt our own message :)
|
||||
box.sendMessage = function (msg) {
|
||||
try {
|
||||
msg = JSON.stringify(msg);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
sendMessage(msg, function (err, hash) {
|
||||
if (m.viewed.indexOf(hash) === -1) {
|
||||
m.viewed.push(hash);
|
||||
}
|
||||
}, keys.curvePublic);
|
||||
};
|
||||
box.queue.forEach(function (msg) {
|
||||
box.sendMessage(msg);
|
||||
});
|
||||
box.queue = [];
|
||||
};
|
||||
cfg.onMessage = function (msg, user, vKey, isCp, hash, author) {
|
||||
if (hash === m.lastKnownHash) { return; }
|
||||
try {
|
||||
msg = JSON.parse(msg);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
if (author) { msg.author = author; }
|
||||
box.history.push(hash);
|
||||
if (isMessageNew(hash, m)) {
|
||||
// Message should be displayed
|
||||
var message = {
|
||||
msg: msg,
|
||||
hash: hash
|
||||
};
|
||||
box.content[hash] = msg;
|
||||
showMessage(ctx, type, message);
|
||||
} else {
|
||||
// Message has already been viewed by the user
|
||||
if (Object.keys(box.content).length === 0) {
|
||||
// If nothing is displayed yet, we can bump our lastKnownHash and remove this hash
|
||||
// from our "viewed" array
|
||||
m.lastKnownHash = hash;
|
||||
box.history = [];
|
||||
var idxViewed = m.viewed.indexOf(hash);
|
||||
if (idxViewed !== -1) { m.viewed.splice(idxViewed, 1); }
|
||||
}
|
||||
}
|
||||
};
|
||||
cfg.onReady = function () {
|
||||
// Clean the "viewed" array: make sure all the "viewed" hashes are
|
||||
// in history
|
||||
var toClean = [];
|
||||
m.viewed.forEach(function (h, i) {
|
||||
if (box.history.indexOf(h) === -1) {
|
||||
toClean.push(i);
|
||||
}
|
||||
});
|
||||
for (var i = toClean.length-1; i>=0; i--) {
|
||||
m.viewed.splice(toClean[i], 1);
|
||||
}
|
||||
// Listen for changes in the "viewed" and lastKnownHash values
|
||||
var view = function (h) {
|
||||
delete box.content[h];
|
||||
ctx.emit('VIEWED', {
|
||||
type: type,
|
||||
hash: h
|
||||
}, ctx.clients);
|
||||
};
|
||||
ctx.store.proxy.on('change', ['mailboxes', type], function (o, n, p) {
|
||||
if (p[2] === 'lastKnownHash') {
|
||||
// Hide everything up to this hash
|
||||
var sliceIdx;
|
||||
box.history.some(function (h, i) {
|
||||
sliceIdx = i + 1;
|
||||
view(h);
|
||||
if (h === n) { return true; }
|
||||
});
|
||||
box.history = box.history.slice(sliceIdx);
|
||||
}
|
||||
if (p[2] === 'viewed') {
|
||||
// Hide this message
|
||||
view(n);
|
||||
}
|
||||
});
|
||||
// Continue
|
||||
onReady();
|
||||
};
|
||||
CpNetflux.start(cfg);
|
||||
};
|
||||
|
||||
// Send a message to someone else
|
||||
var sendTo = function (ctx, type, msg, user, cb) {
|
||||
if (!Crypto.Mailbox) {
|
||||
|
@ -202,6 +79,7 @@ proxy.mailboxes = {
|
|||
network.join(user.channel).then(function (wc) {
|
||||
wc.bcast(ciphertext).then(function () {
|
||||
cb();
|
||||
wc.leave();
|
||||
});
|
||||
}, function (err) {
|
||||
cb({error: err});
|
||||
|
@ -282,6 +160,146 @@ proxy.mailboxes = {
|
|||
});
|
||||
};
|
||||
|
||||
|
||||
var openChannel = function (ctx, type, m, onReady) {
|
||||
var box = ctx.boxes[type] = {
|
||||
queue: [], // Store the messages to send when the channel is ready
|
||||
history: [], // All the hashes loaded from the server in corretc order
|
||||
content: {}, // Content of the messages that should be displayed
|
||||
sendMessage: function (msg) { // To send a message to our box
|
||||
try {
|
||||
msg = JSON.stringify(msg);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
box.queue.push(msg);
|
||||
}
|
||||
};
|
||||
Crypto = Crypto;
|
||||
if (!Crypto.Mailbox) {
|
||||
return void console.error("chainpad-crypto is outdated and doesn't support mailboxes.");
|
||||
}
|
||||
var keys = getMyKeys(ctx);
|
||||
if (!keys) { return void console.error("missing asymmetric encryption keys"); }
|
||||
var crypto = Crypto.Mailbox.createEncryptor(keys);
|
||||
// XXX remove 'test'
|
||||
if (type === 'test') {
|
||||
crypto = {
|
||||
encrypt: function (x) { return x; },
|
||||
decrypt: function (x) { return x; }
|
||||
};
|
||||
}
|
||||
var cfg = {
|
||||
network: ctx.store.network,
|
||||
channel: m.channel,
|
||||
noChainPad: true,
|
||||
crypto: crypto,
|
||||
owners: [ctx.store.proxy.edPublic],
|
||||
lastKnownHash: m.lastKnownHash
|
||||
};
|
||||
cfg.onConnect = function (wc, sendMessage) {
|
||||
// Send a message to our box?
|
||||
// NOTE: we use our own curvePublic so that we can decrypt our own message :)
|
||||
box.sendMessage = function (msg) {
|
||||
try {
|
||||
msg = JSON.stringify(msg);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
sendMessage(msg, function (err, hash) {
|
||||
if (m.viewed.indexOf(hash) === -1) {
|
||||
m.viewed.push(hash);
|
||||
}
|
||||
}, keys.curvePublic);
|
||||
};
|
||||
box.queue.forEach(function (msg) {
|
||||
box.sendMessage(msg);
|
||||
});
|
||||
box.queue = [];
|
||||
};
|
||||
cfg.onMessage = function (msg, user, vKey, isCp, hash, author) {
|
||||
if (hash === m.lastKnownHash) { return; }
|
||||
try {
|
||||
msg = JSON.parse(msg);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
if (author) { msg.author = author; }
|
||||
box.history.push(hash);
|
||||
if (isMessageNew(hash, m)) {
|
||||
// Message should be displayed
|
||||
var message = {
|
||||
msg: msg,
|
||||
hash: hash
|
||||
};
|
||||
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) {
|
||||
// If nothing is displayed yet, we can bump our lastKnownHash and remove this hash
|
||||
// from our "viewed" array
|
||||
m.lastKnownHash = hash;
|
||||
box.history = [];
|
||||
var idxViewed = m.viewed.indexOf(hash);
|
||||
if (idxViewed !== -1) { m.viewed.splice(idxViewed, 1); }
|
||||
}
|
||||
}
|
||||
};
|
||||
cfg.onReady = function () {
|
||||
// Clean the "viewed" array: make sure all the "viewed" hashes are
|
||||
// in history
|
||||
var toClean = [];
|
||||
m.viewed.forEach(function (h, i) {
|
||||
if (box.history.indexOf(h) === -1) {
|
||||
toClean.push(i);
|
||||
}
|
||||
});
|
||||
for (var i = toClean.length-1; i>=0; i--) {
|
||||
m.viewed.splice(toClean[i], 1);
|
||||
}
|
||||
// Listen for changes in the "viewed" and lastKnownHash values
|
||||
var view = function (h) {
|
||||
delete box.content[h];
|
||||
ctx.emit('VIEWED', {
|
||||
type: type,
|
||||
hash: h
|
||||
}, ctx.clients);
|
||||
};
|
||||
ctx.store.proxy.on('change', ['mailboxes', type], function (o, n, p) {
|
||||
if (p[2] === 'lastKnownHash') {
|
||||
// Hide everything up to this hash
|
||||
var sliceIdx;
|
||||
box.history.some(function (h, i) {
|
||||
sliceIdx = i + 1;
|
||||
view(h);
|
||||
if (h === n) { return true; }
|
||||
});
|
||||
box.history = box.history.slice(sliceIdx);
|
||||
}
|
||||
if (p[2] === 'viewed') {
|
||||
// Hide this message
|
||||
view(n);
|
||||
}
|
||||
});
|
||||
// Continue
|
||||
onReady();
|
||||
};
|
||||
CpNetflux.start(cfg);
|
||||
};
|
||||
|
||||
|
||||
var subscribe = function (ctx, data, cId, cb) {
|
||||
// Get existing notifications
|
||||
Object.keys(ctx.boxes).forEach(function (type) {
|
||||
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -58,8 +58,8 @@ define([
|
|||
ADD_SHARED_FOLDER: Store.addSharedFolder,
|
||||
LOAD_SHARED_FOLDER: Store.loadSharedFolderAnon,
|
||||
// Messaging
|
||||
INVITE_FROM_USERLIST: Store.inviteFromUserlist,
|
||||
ADD_DIRECT_MESSAGE_HANDLERS: Store.addDirectMessageHandlers,
|
||||
ANSWER_FRIEND_REQUEST: Store.answerFriendRequest,
|
||||
SEND_FRIEND_REQUEST: Store.sendFriendRequest,
|
||||
// Chat
|
||||
CHAT_COMMAND: Store.messenger.execCommand,
|
||||
// OnlyOffice
|
||||
|
|
|
@ -502,16 +502,11 @@ define([
|
|||
});
|
||||
|
||||
// Messaging
|
||||
sframeChan.on('Q_SEND_FRIEND_REQUEST', function (netfluxId, cb) {
|
||||
Cryptpad.inviteFromUserlist(netfluxId, cb);
|
||||
sframeChan.on('Q_SEND_FRIEND_REQUEST', function (data, cb) {
|
||||
Cryptpad.messaging.sendFriendRequest(data, cb);
|
||||
});
|
||||
Cryptpad.messaging.onFriendRequest.reg(function (confirmText, cb) {
|
||||
sframeChan.query('Q_INCOMING_FRIEND_REQUEST', confirmText, function (err, data) {
|
||||
cb(data);
|
||||
});
|
||||
});
|
||||
Cryptpad.messaging.onFriendComplete.reg(function (data) {
|
||||
sframeChan.event('EV_FRIEND_REQUEST', data);
|
||||
sframeChan.on('Q_ANSWER_FRIEND_REQUEST', function (data, cb) {
|
||||
Cryptpad.messaging.answerFriendRequest(data, cb);
|
||||
});
|
||||
|
||||
// History
|
||||
|
@ -954,9 +949,6 @@ define([
|
|||
readOnly: readOnly,
|
||||
crypto: Crypto.createEncryptor(secret.keys),
|
||||
onConnect: function () {
|
||||
var href = parsed.getUrl();
|
||||
// Add friends requests handlers when we have the final href
|
||||
Cryptpad.messaging.addHandlers(href);
|
||||
if (window.location.hash && window.location.hash !== '#') {
|
||||
/*window.location = parsed.getUrl({
|
||||
present: parsed.hashData.present,
|
||||
|
|
|
@ -380,14 +380,24 @@ define([
|
|||
funcs.mergeAnonDrive = function (cb) {
|
||||
ctx.sframeChan.query('Q_MERGE_ANON_DRIVE', null, cb);
|
||||
};
|
||||
// Friends
|
||||
var pendingFriends = [];
|
||||
|
||||
// Create friend request
|
||||
funcs.getPendingFriends = function () {
|
||||
return pendingFriends.slice();
|
||||
return ctx.metadataMgr.getPrivateData().pendingFriends;
|
||||
};
|
||||
funcs.sendFriendRequest = function (netfluxId) {
|
||||
ctx.sframeChan.query('Q_SEND_FRIEND_REQUEST', netfluxId, $.noop);
|
||||
pendingFriends.push(netfluxId);
|
||||
funcs.sendFriendRequest = function (data, cb) {
|
||||
ctx.sframeChan.query('Q_SEND_FRIEND_REQUEST', data, cb);
|
||||
};
|
||||
// Friend requests received
|
||||
var friendRequests = {};
|
||||
funcs.addFriendRequest = function (data) {
|
||||
var curve = Util.find(data, ['content', 'msg', 'author']);
|
||||
console.log(data);
|
||||
console.log(curve);
|
||||
friendRequests[curve] = data;
|
||||
};
|
||||
funcs.getFriendRequests = function () {
|
||||
return JSON.parse(JSON.stringify(friendRequests));
|
||||
};
|
||||
|
||||
// Feedback
|
||||
|
@ -524,15 +534,6 @@ define([
|
|||
|
||||
UI.addTooltips();
|
||||
|
||||
ctx.sframeChan.on('Q_INCOMING_FRIEND_REQUEST', function (confirmMsg, cb) {
|
||||
UI.confirm(confirmMsg, cb, null, true);
|
||||
});
|
||||
ctx.sframeChan.on('EV_FRIEND_REQUEST', function (data) {
|
||||
var i = pendingFriends.indexOf(data.netfluxId);
|
||||
if (i !== -1) { pendingFriends.splice(i, 1); }
|
||||
UI.log(data.logText);
|
||||
});
|
||||
|
||||
ctx.sframeChan.on("EV_PAD_PASSWORD", function () {
|
||||
UIElements.displayPasswordPrompt(funcs);
|
||||
});
|
||||
|
|
|
@ -8,10 +8,11 @@ define([
|
|||
'/common/common-util.js',
|
||||
'/common/common-feedback.js',
|
||||
'/common/hyperscript.js',
|
||||
'/common/notifications.js',
|
||||
'/common/messenger-ui.js',
|
||||
'/customize/messages.js',
|
||||
], function ($, Config, ApiConfig, UIElements, UI, Hash, Util, Feedback, h,
|
||||
MessengerUI, Messages) {
|
||||
Notifications, MessengerUI, Messages) {
|
||||
var Common;
|
||||
|
||||
var Bar = {
|
||||
|
@ -232,7 +233,11 @@ MessengerUI, Messages) {
|
|||
// Display the userlist
|
||||
|
||||
// Editors
|
||||
var pendingFriends = Common.getPendingFriends();
|
||||
var pendingFriends = Common.getPendingFriends(); // Friend requests sent
|
||||
var friendRequests = Common.getFriendRequests(); // Friend requests received
|
||||
console.log(friendRequests);
|
||||
var friendTo = +new Date() - (2 * 24 * 3600 * 1000);
|
||||
friendTo = +new Date(); // XXX
|
||||
editUsersNames.forEach(function (data) {
|
||||
var name = data.name || Messages.anonymous;
|
||||
var $span = $('<span>', {'class': 'cp-avatar'});
|
||||
|
@ -300,9 +305,21 @@ MessengerUI, Messages) {
|
|||
}
|
||||
} else if (Common.isLoggedIn() && data.curvePublic && !friends[data.curvePublic]
|
||||
&& !priv.readOnly) {
|
||||
if (pendingFriends.indexOf(data.netfluxId) !== -1) {
|
||||
console.log(pendingFriends);
|
||||
if (pendingFriends[data.curvePublic] && pendingFriends[data.curvePublic] > friendTo) {
|
||||
$('<span>', {'class': 'cp-toolbar-userlist-friend'}).text(Messages.userlist_pending)
|
||||
.appendTo($rightCol);
|
||||
} else if (friendRequests[data.curvePublic]) {
|
||||
$('<button>', {
|
||||
'class': 'fa fa-bell cp-toolbar-userlist-button',
|
||||
'title': 'Pending friend request' /*Messages._getKey('userlist_addAsFriendTitle', [ // XXX
|
||||
name
|
||||
])*/
|
||||
}).appendTo($nameSpan).click(function (e) {
|
||||
e.stopPropagation();
|
||||
UIElements.displayFriendRequestModal(Common, friendRequests[data.curvePublic]);
|
||||
});
|
||||
|
||||
} else {
|
||||
$('<button>', {
|
||||
'class': 'fa fa-user-plus cp-toolbar-userlist-button',
|
||||
|
@ -311,7 +328,10 @@ MessengerUI, Messages) {
|
|||
])
|
||||
}).appendTo($nameSpan).click(function (e) {
|
||||
e.stopPropagation();
|
||||
Common.sendFriendRequest(data.netfluxId);
|
||||
Common.sendFriendRequest(data, function (err, obj) {
|
||||
if (err || (obj && obj.error)) { return void console.error(err || obj.error); }
|
||||
// XXX
|
||||
});
|
||||
});
|
||||
}
|
||||
} else if (Common.isLoggedIn() && data.curvePublic && friends[data.curvePublic]) {
|
||||
|
@ -948,7 +968,10 @@ MessengerUI, Messages) {
|
|||
|
||||
Common.mailbox.subscribe({
|
||||
onMessage: function (data, el) {
|
||||
if (el) { div.appendChild(el); }
|
||||
if (el) {
|
||||
Notifications(Common, data, el);
|
||||
div.appendChild(el);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue