Offline teams

pull/1/head
yflory 4 years ago
parent f7477b65ea
commit cb153187ce

@ -299,7 +299,7 @@ nThen(function (w) {
oscar.edKeys.edPublic oscar.edKeys.edPublic
], ],
keys: rosterKeys, keys: rosterKeys,
anon_rpc: oscar.anonRpc, store: oscar,
lastKnownHash: void 0, lastKnownHash: void 0,
}, w(function (err, roster) { }, w(function (err, roster) {
if (err) { if (err) {
@ -514,7 +514,7 @@ nThen(function (w) {
channel: rosterKeys.channel, channel: rosterKeys.channel,
//owners: [], // Alice doesn't know who the owners might be... //owners: [], // Alice doesn't know who the owners might be...
keys: rosterKeys, keys: rosterKeys,
anon_rpc: alice.anonRpc, store: alice,
lastKnownHash: void 0, // alice should fetch everything from the beginning of time... lastKnownHash: void 0, // alice should fetch everything from the beginning of time...
}, w(function (err, roster) { }, w(function (err, roster) {
if (err) { if (err) {
@ -742,7 +742,7 @@ nThen(function (w) {
network: bob.network, network: bob.network,
channel: rosterKeys.channel, channel: rosterKeys.channel,
keys: rosterKeys, keys: rosterKeys,
anon_rpc: bob.anonRpc, store: bob,
//lastKnownHash: oscar.lastRosterCheckpointHash //lastKnownHash: oscar.lastRosterCheckpointHash
//lastKnownHash: oscar.lastKnownHash, // FIXME this doesn't work. off-by-one? //lastKnownHash: oscar.lastKnownHash, // FIXME this doesn't work. off-by-one?
}, w(function (err, roster) { }, w(function (err, roster) {

@ -1432,7 +1432,7 @@ define([
// Universal // Universal
Store.universal = { Store.universal = {
execCommand: function (clientId, obj, cb) { execCommand: function (clientId, obj, cb) {
onReadyEvt.reg(function () { var todo = function () {
var type = obj.type; var type = obj.type;
var data = obj.data; var data = obj.data;
if (store.modules[type]) { if (store.modules[type]) {
@ -1440,7 +1440,11 @@ define([
} else { } else {
return void cb({error: type + ' is disabled'}); return void cb({error: type + ' is disabled'});
} }
}); };
// Teams support offline/cache mode
if (obj.type === "team") { return void todo(); }
// Other modules should wait for the ready event
onReadyEvt.reg(todo);
} }
}; };
var loadUniversal = function (Module, type, waitFor, clientId) { var loadUniversal = function (Module, type, waitFor, clientId) {
@ -2577,6 +2581,8 @@ define([
}; };
postMessage(clientId, 'LOADING_DRIVE', data); postMessage(clientId, 'LOADING_DRIVE', data);
}); });
}).nThen(function (waitFor) {
loadUniversal(Team, 'team', waitFor, clientId); // TODO load teams offline
}).nThen(function () { }).nThen(function () {
cb(); cb();
}); });
@ -2631,7 +2637,7 @@ define([
loadUniversal(Messenger, 'messenger', waitFor); loadUniversal(Messenger, 'messenger', waitFor);
store.messenger = store.modules['messenger']; store.messenger = store.modules['messenger'];
loadUniversal(Profile, 'profile', waitFor); loadUniversal(Profile, 'profile', waitFor);
loadUniversal(Team, 'team', waitFor, clientId); // TODO load teams offline store.modules['team'].onReady(waitFor);
loadUniversal(History, 'history', waitFor); loadUniversal(History, 'history', waitFor);
}).nThen(function () { }).nThen(function () {
var requestLogin = function () { var requestLogin = function () {

@ -491,13 +491,13 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback)
if (!config.network) { return void cb("EXPECTED_NETWORK"); } if (!config.network) { return void cb("EXPECTED_NETWORK"); }
if (!config.channel || typeof(config.channel) !== 'string' || config.channel.length !== 32) { return void cb("EXPECTED_CHANNEL"); } if (!config.channel || typeof(config.channel) !== 'string' || config.channel.length !== 32) { return void cb("EXPECTED_CHANNEL"); }
if (!config.keys || typeof(config.keys) !== 'object') { return void cb("EXPECTED_CRYPTO_KEYS"); } if (!config.keys || typeof(config.keys) !== 'object') { return void cb("EXPECTED_CRYPTO_KEYS"); }
if (!config.anon_rpc) { return void cb("EXPECTED_ANON_RPC"); } if (!config.store) { return void cb("EXPECTED_STORE"); }
var response = Util.response(function (label, info) { var response = Util.response(function (label, info) {
console.error('ROSTER_RESPONSE__' + label, info); console.error('ROSTER_RESPONSE__' + label, info);
}); });
var anon_rpc = config.anon_rpc; var store = config.store;
var keys = config.keys; var keys = config.keys;
var me = keys.myCurvePublic; var me = keys.myCurvePublic;
var channel = config.channel; var channel = config.channel;
@ -571,6 +571,10 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback)
} }
}; };
var ready = false; var ready = false;
var onCacheReady = function () {
if (!config.onCacheReady) { return; }
config.onCacheReady(roster);
};
var onReady = function (info) { var onReady = function (info) {
//console.log("READY"); //console.log("READY");
webChannel = info; webChannel = info;
@ -670,9 +674,18 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback)
}, delay); }, delay);
}; };
var isCacheCheckpoint = function (msg, author) {
var parsed = Util.tryParse(msg);
if (parsed[0] !== 'CHECKPOINT') { return false; }
var changed = simulate(parsed, author, ref);
return changed;
};
var metadata, crypto; var metadata, crypto;
var send = function (msg, cb) { var send = function (msg, cb) {
if (!isReady()) { return void cb("NOT_READY"); } if (!isReady()) { return void cb("NOT_READY"); }
var anon_rpc = store.anon_rpc;
if (!anon_rpc) { return void cb("ANON_RPC_NOT_READY"); }
var changed = false; var changed = false;
try { try {
@ -816,7 +829,8 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback)
nThen(function (w) { nThen(function (w) {
// get metadata so we know the owners and validateKey // get metadata so we know the owners and validateKey
anon_rpc.send('GET_METADATA', channel, function (err, data) { if (!store.anon_rpc) { return; }
store.anon_rpc.send('GET_METADATA', channel, function (err, data) {
if (err) { if (err) {
w.abort(); w.abort();
return void console.error(err); return void console.error(err);
@ -827,6 +841,10 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback)
if (!config.keys.teamEdPublic && metadata && metadata.validateKey) { if (!config.keys.teamEdPublic && metadata && metadata.validateKey) {
config.keys.teamEdPublic = metadata.validateKey; config.keys.teamEdPublic = metadata.validateKey;
} }
if (!config.keys.teamEdPublic) {
w.abort();
return void cb("NO_VALIDATE_KEY");
}
try { try {
crypto = Crypto.Team.createEncryptor(config.keys); crypto = Crypto.Team.createEncryptor(config.keys);
@ -854,6 +872,10 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback)
owners: config.owners, owners: config.owners,
Cache: config.Cache,
isCacheCheckpoint: isCacheCheckpoint,
onCacheReady: onCacheReady,
onChannelError: onChannelError, onChannelError: onChannelError,
onReady: onReady, onReady: onReady,
onConnect: onConnect, onConnect: onConnect,

@ -27,6 +27,8 @@ define([
var Team = {}; var Team = {};
var Nacl = window.nacl; var Nacl = window.nacl;
var onStoreReady = Util.mkEvent(true);
var openCachedTeamChat = function () {}; // Placeholder
var registerChangeEvents = function (ctx, team, proxy, fId) { var registerChangeEvents = function (ctx, team, proxy, fId) {
if (!team) { return; } if (!team) { return; }
@ -127,9 +129,11 @@ define([
delete ctx.store.proxy.teams[teamId]; delete ctx.store.proxy.teams[teamId];
ctx.emit('LEAVE_TEAM', teamId, team.clients); ctx.emit('LEAVE_TEAM', teamId, team.clients);
ctx.updateMetadata(); ctx.updateMetadata();
if (ctx.store.mailbox) {
ctx.store.mailbox.close('team-'+teamId, function () { ctx.store.mailbox.close('team-'+teamId, function () {
// Close team mailbox // Close team mailbox
}); });
}
}; };
var getTeamChannelList = function (ctx, id) { var getTeamChannelList = function (ctx, id) {
@ -180,29 +184,13 @@ define([
Pinpad.create(ctx.store.network, data, function (e, call) { Pinpad.create(ctx.store.network, data, function (e, call) {
if (e) { return void cb(e); } if (e) { return void cb(e); }
team.rpc = call; team.rpc = call;
team.pin = function (data, cb) {
if (!team.rpc) { return void cb({error: 'TEAM_RPC_NOT_READY'}); }
if (typeof(cb) !== 'function') { console.error('expected a callback'); }
team.rpc.pin(data, function (e, hash) {
if (e) { return void cb({error: e}); }
cb({hash: hash});
});
};
team.unpin = function (data, cb) {
if (!team.rpc) { return void cb({error: 'TEAM_RPC_NOT_READY'}); }
if (typeof(cb) !== 'function') { console.error('expected a callback'); }
team.rpc.unpin(data, function (e, hash) {
if (e) { return void cb({error: e}); }
cb({hash: hash});
});
};
cb(); cb();
}); });
}); });
}; };
var onReady = function (ctx, id, lm, roster, keys, cId, cb) { var onCacheReady = function (ctx, id, lm, roster, keys, cId, cb) {
if (ctx.cache[id]) { return void cb(); }
var proxy = lm.proxy; var proxy = lm.proxy;
var team = { var team = {
id: id, id: id,
@ -212,8 +200,10 @@ define([
realtime: lm.realtime, realtime: lm.realtime,
handleSharedFolder: function (sfId, rt) { handleSharedFolder(ctx, id, sfId, rt); }, handleSharedFolder: function (sfId, rt) { handleSharedFolder(ctx, id, sfId, rt); },
sharedFolders: {}, // equivalent of store.sharedFolders in async-store sharedFolders: {}, // equivalent of store.sharedFolders in async-store
roster: roster roster: roster,
offline: true
}; };
ctx.cache[id] = team;
// Subscribe to events // Subscribe to events
if (cId) { team.clients.push(cId); } if (cId) { team.clients.push(cId); }
@ -240,11 +230,6 @@ define([
rosterData.lastKnownHash = hash; rosterData.lastKnownHash = hash;
}); });
// Update metadata
var state = roster.getState();
var teamData = Util.find(ctx, ['store', 'proxy', 'teams', id]);
if (teamData) { teamData.metadata = state.metadata; }
// Broadcast an event to all the tabs displaying this team // Broadcast an event to all the tabs displaying this team
team.sendEvent = function (q, data, sender) { team.sendEvent = function (q, data, sender) {
ctx.emit(q, data, team.clients.filter(function (cId) { ctx.emit(q, data, team.clients.filter(function (cId) {
@ -266,26 +251,37 @@ define([
}; };
}; };
var secret; team.pin = function (data, cb) {
team.pin = function (data, cb) { return void cb({error: 'EFORBIDDEN'}); }; if (!keys.drive.edPrivate) { return void cb({error: 'EFORBIDDEN'}); }
team.unpin = function (data, cb) { return void cb({error: 'EFORBIDDEN'}); }; if (!team.rpc) { return void cb({error: 'TEAM_RPC_NOT_READY'}); }
nThen(function (waitFor) { if (typeof(cb) !== 'function') { console.error('expected a callback'); }
// Init Team RPC team.rpc.pin(data, function (e, hash) {
if (!keys.drive.edPrivate) { return; } if (e) { return void cb({error: e}); }
initRpc(ctx, team, keys.drive, waitFor(function () {})); cb({hash: hash});
}).nThen(function () { });
};
team.unpin = function (data, cb) {
if (!keys.drive.edPrivate) { return void cb({error: 'EFORBIDDEN'}); }
if (!team.rpc) { return void cb({error: 'TEAM_RPC_NOT_READY'}); }
if (typeof(cb) !== 'function') { console.error('expected a callback'); }
team.rpc.unpin(data, function (e, hash) {
if (e) { return void cb({error: e}); }
cb({hash: hash});
});
};
// Create the proxy manager // Create the proxy manager
var loadSharedFolder = function (id, data, cb, isNew) { var loadSharedFolder = function (id, data, cb, isNew) {
SF.load({ SF.load({
isNew: isNew, isNew: isNew,
network: ctx.store.network, network: ctx.store.network || ctx.store.networkPromise,
store: team, store: team,
isNewChannel: ctx.Store.isNewChannel isNewChannel: ctx.Store.isNewChannel
}, id, data, cb); }, id, data, cb);
}; };
var teamData = ctx.store.proxy.teams[team.id]; var teamData = ctx.store.proxy.teams[team.id];
var hash = teamData.hash || teamData.roHash; var hash = teamData.hash || teamData.roHash;
secret = Hash.getSecrets('team', hash, teamData.password); var secret = Hash.getSecrets('team', hash, teamData.password);
var manager = team.manager = ProxyManager.create(proxy.drive, { var manager = team.manager = ProxyManager.create(proxy.drive, {
onSync: function (cb) { ctx.Store.onSync(id, cb); }, onSync: function (cb) { ctx.Store.onSync(id, cb); },
edPublic: keys.drive.edPublic, edPublic: keys.drive.edPublic,
@ -323,12 +319,45 @@ define([
}); });
team.secondaryKey = secret && secret.keys.secondaryKey; team.secondaryKey = secret && secret.keys.secondaryKey;
team.userObject = manager.user.userObject; team.userObject = manager.user.userObject;
team.userObject.fixFiles();
}).nThen(function (waitFor) { nThen(function (waitFor) {
// Load the shared folders // Load the shared folders
ctx.teams[id] = team; ctx.teams[id] = team;
delete ctx.cache[id];
registerChangeEvents(ctx, team, proxy); registerChangeEvents(ctx, team, proxy);
SF.checkMigration(team.secondaryKey, proxy, team.userObject, waitFor()); var network = ctx.store.network || ctx.store.networkPromise;
SF.loadSharedFolders(ctx.Store, network, team,
team.userObject, waitFor, function (data) {
ctx.progress += 70/(ctx.numberOfTeams * data.max);
ctx.updateProgress({
progress: ctx.progress
});
});
}).nThen(function () {
cb();
});
};
var onReady = function (ctx, id, lm, roster, keys, cId, cb) {
// Update metadata
var state = roster.getState();
var teamData = Util.find(ctx, ['store', 'proxy', 'teams', id]);
if (teamData) { teamData.metadata = state.metadata; }
var team;
if (!ctx.store.proxy.teams[id]) { return; }
nThen(function (waitFor) {
if (ctx.cache[id]) { return; }
onCacheReady(ctx, id, lm, roster, keys, cId, waitFor());
}).nThen(function (waitFor) {
team = ctx.teams[id];
// Init Team RPC
if (!keys.drive.edPrivate) { return; }
initRpc(ctx, team, keys.drive, waitFor(function () {}));
}).nThen(function (waitFor) {
// Load the shared folders
team.userObject.fixFiles();
SF.checkMigration(team.secondaryKey, team.proxy, team.userObject, waitFor());
SF.loadSharedFolders(ctx.Store, ctx.store.network, team, SF.loadSharedFolders(ctx.Store, ctx.store.network, team,
team.userObject, waitFor, function (data) { team.userObject, waitFor, function (data) {
ctx.progress += 70/(ctx.numberOfTeams * data.max); ctx.progress += 70/(ctx.numberOfTeams * data.max);
@ -351,6 +380,7 @@ define([
} }
}); });
}).nThen(function () { }).nThen(function () {
team.offline = false;
if (ctx.onReadyHandlers[id]) { if (ctx.onReadyHandlers[id]) {
ctx.onReadyHandlers[id].forEach(function (obj) { ctx.onReadyHandlers[id].forEach(function (obj) {
// Callback and subscribe the client to new notifications // Callback and subscribe the client to new notifications
@ -368,10 +398,36 @@ define([
}; };
var checkTeamChannels = function (ctx, id, channel, roster, waitFor, cb) {
var close = function () {
if (ctx.cache[id] || ctx.teams[id]) { closeTeam(ctx, id); }
delete ctx.store.proxy.teams[id];
delete ctx.onReadyHandlers[id];
waitFor.abort();
cb({error: 'ENOENT'});
};
if (channel) {
ctx.store.anon_rpc.send("IS_NEW_CHANNEL", channel, waitFor(function (e, res) {
if (res && res.length && typeof(res[0]) === 'boolean' && res[0]) {
// Channel is empty: remove this team
close();
}
}));
}
if (roster) {
ctx.store.anon_rpc.send("IS_NEW_CHANNEL", roster, waitFor(function (e, res) {
if (res && res.length && typeof(res[0]) === 'boolean' && res[0]) {
// Channel is empty: remove this team
close();
}
}));
}
};
// Progress: // Progress:
// One team = (30/(#teams))% // One team = (30/(#teams))%
// One shared folder = (70/(#teams * #folders))% // One shared folder = (70/(#teams * #folders))%
var openChannel = function (ctx, teamData, id, _cb) { var openChannel = function (ctx, teamData, id, _cb, cache) {
var cb = Util.once(Util.mkAsync(_cb)); var cb = Util.once(Util.mkAsync(_cb));
var hash = teamData.hash || teamData.roHash; var hash = teamData.hash || teamData.roHash;
@ -402,32 +458,55 @@ define([
: Crypto.Team.deriveGuestKeys(rosterData.view || ''); : Crypto.Team.deriveGuestKeys(rosterData.view || '');
nThen(function (waitFor) { nThen(function (waitFor) {
ctx.store.anon_rpc.send("IS_NEW_CHANNEL", secret.channel, waitFor(function (e, response) { if (cache) {
if (response && response.length && typeof(response[0]) === 'boolean' && response[0]) { // If we're in cache mode, make sure we have a cache for this team
// Channel is empty: remove this team Cache.getChannelCache(secret.channel, waitFor(function (err, obj) {
delete ctx.store.proxy.teams[id]; var c = obj && obj.c; // content
if (!c) {
waitFor.abort(); waitFor.abort();
cb({error: 'ENOENT'}); cb({error: 'NOCACHE_DRIVE'});
} }
})); }));
ctx.store.anon_rpc.send("IS_NEW_CHANNEL", rosterKeys.channel, waitFor(function (e, response) { Cache.getChannelCache(rosterKeys.channel, waitFor(function (err, obj) {
if (response && response.length && typeof(response[0]) === 'boolean' && response[0]) { var c = obj && obj.c; // content
// Channel is empty: remove this team var k = obj && obj.k;
delete ctx.store.proxy.teams[id]; if (k && !rosterKeys.teamEdPublic) {
rosterKeys.teamEdPublic = k;
}
if (!c) {
waitFor.abort(); waitFor.abort();
cb({error: 'ENOENT'}); cb({error: 'NOCACHE_ROSTER'});
} }
})); }));
return;
}
checkTeamChannels(ctx, id, secret.channel, rosterKeys.channel, waitFor, cb);
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
var cacheRdy = {
lm: false,
roster: false,
check: function () {
if (!this.lm || !this.roster) { return; }
if (!cache) { return; }
// Both are cacheready!
ctx.progress += 30/ctx.numberOfTeams;
ctx.updateProgress({
progress: ctx.progress
});
onCacheReady(ctx, id, lm, roster, keys, null, cb);
this.check = function () {};
}
};
// Load the proxy // Load the proxy
var cfg = { var cfg = {
data: {}, data: {},
readOnly: !Boolean(secret.keys.signKey), readOnly: !Boolean(secret.keys.signKey),
network: ctx.store.network, network: ctx.store.network || ctx.store.networkPromise,
channel: secret.channel, channel: secret.channel,
crypto: crypto, crypto: crypto,
ChainPad: ChainPad, ChainPad: ChainPad,
Cache: Cache, // ICE team cache Cache: Cache,
metadata: { metadata: {
validateKey: secret.keys.validateKey || undefined, validateKey: secret.keys.validateKey || undefined,
}, },
@ -440,6 +519,10 @@ define([
ctx.emit('ROSTER_CHANGE', id, team.clients); ctx.emit('ROSTER_CHANGE', id, team.clients);
}; };
lm = Listmap.create(cfg); lm = Listmap.create(cfg);
lm.proxy.on('cacheready', function () {
cacheRdy.lm = true;
cacheRdy.check();
});
lm.proxy.on('ready', waitFor()); lm.proxy.on('ready', waitFor());
lm.proxy.on('error', function (info) { lm.proxy.on('error', function (info) {
if (info && typeof (info.loaded) !== "undefined" && !info.loaded) { if (info && typeof (info.loaded) !== "undefined" && !info.loaded) {
@ -454,11 +537,17 @@ define([
// Load the roster // Load the roster
Roster.create({ Roster.create({
network: ctx.store.network, network: ctx.store.network || ctx.store.networkPromise,
channel: rosterKeys.channel, channel: rosterKeys.channel,
keys: rosterKeys, keys: rosterKeys,
anon_rpc: ctx.store.anon_rpc, store: ctx.store,
lastKnownHash: rosterData.lastKnownHash, lastKnownHash: rosterData.lastKnownHash,
onCacheReady: function (_roster) {
roster = _roster;
cacheRdy.roster = true;
cacheRdy.check();
},
Cache: Cache
}, waitFor(function (err, _roster) { }, waitFor(function (err, _roster) {
if (err) { if (err) {
waitFor.abort(); waitFor.abort();
@ -475,6 +564,7 @@ define([
var me = Util.find(ctx, ['store', 'proxy', 'curvePublic']); var me = Util.find(ctx, ['store', 'proxy', 'curvePublic']);
if (!state.members[me]) { return; } if (!state.members[me]) { return; }
onStoreReady.reg(function () {
// If you're allowed to edit the roster, try to update your data // If you're allowed to edit the roster, try to update your data
if (!rosterData.edit) { return; } if (!rosterData.edit) { return; }
var data = {}; var data = {};
@ -486,6 +576,7 @@ define([
if (err === 'NO_CHANGE') { return; } if (err === 'NO_CHANGE') { return; }
console.error(err); console.error(err);
}); });
});
})); }));
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
// Make sure we have not been kicked from the roster // Make sure we have not been kicked from the roster
@ -537,10 +628,12 @@ define([
Feedback.send("TEAM_RIGHTS_OWNER"); Feedback.send("TEAM_RIGHTS_OWNER");
} }
}).nThen(function () { }).nThen(function () {
if (!cache) {
ctx.progress += 30/ctx.numberOfTeams; ctx.progress += 30/ctx.numberOfTeams;
ctx.updateProgress({ ctx.updateProgress({
progress: ctx.progress progress: ctx.progress
}); });
}
onReady(ctx, id, lm, roster, keys, null, cb); onReady(ctx, id, lm, roster, keys, null, cb);
}); });
}; };
@ -581,12 +674,13 @@ define([
nThen(function (waitFor) { nThen(function (waitFor) {
// Initialize the roster // Initialize the roster
Roster.create({ Roster.create({
network: ctx.store.network, network: ctx.store.network || ctx.store.networkPromise,
channel: rosterKeys.channel, //sharedConfig.rosterChannel, channel: rosterKeys.channel, //sharedConfig.rosterChannel,
owners: [ctx.store.proxy.edPublic], owners: [ctx.store.proxy.edPublic],
keys: rosterKeys, keys: rosterKeys,
anon_rpc: ctx.store.anon_rpc, store: ctx.store,
lastKnownHash: void 0, lastKnownHash: void 0,
Cache: Cache
}, waitFor(function (err, _roster) { }, waitFor(function (err, _roster) {
if (err) { if (err) {
waitFor.abort(); waitFor.abort();
@ -897,6 +991,7 @@ define([
} }
// Add online status (using messenger data) // Add online status (using messenger data)
if (ctx.store.messenger) {
var chatData = team.getChatData(); var chatData = team.getChatData();
var online = ctx.store.messenger.getOnlineList(chatData.channel) || []; var online = ctx.store.messenger.getOnlineList(chatData.channel) || [];
online.forEach(function (curve) { online.forEach(function (curve) {
@ -904,6 +999,7 @@ define([
members[curve].online = true; members[curve].online = true;
} }
}); });
}
cb(members); cb(members);
}; };
@ -1357,10 +1453,11 @@ define([
try { try {
ctx.store.messenger.removeClient(cId); ctx.store.messenger.removeClient(cId);
} catch (e) {} } catch (e) {}
openCachedTeamChat = function () {};
if (!id) { return void cb(); } if (!id) { return void cb(); }
// If the team is loading, as ourselves in the list // If the team is loading, add ourselves to the list
if (ctx.onReadyHandlers[id]) { if (ctx.onReadyHandlers[id] && !ctx.teams[id]) {
var _idx = ctx.onReadyHandlers[id].indexOf(cId); var _idx = ctx.onReadyHandlers[id].indexOf(cId);
if (_idx === -1) { if (_idx === -1) {
ctx.onReadyHandlers[id].push({ ctx.onReadyHandlers[id].push({
@ -1388,8 +1485,14 @@ define([
var onUpdate = function () { var onUpdate = function () {
ctx.emit('ROSTER_CHANGE', data.teamId, team.clients); ctx.emit('ROSTER_CHANGE', data.teamId, team.clients);
}; };
if (ctx.store.messenger) {
ctx.store.messenger.openTeamChat(team.getChatData(), onUpdate, cId, cb);
} else {
openCachedTeamChat = function () {
ctx.store.messenger.openTeamChat(team.getChatData(), onUpdate, cId, cb); ctx.store.messenger.openTeamChat(team.getChatData(), onUpdate, cId, cb);
}; };
}
};
var createInviteLink = function (ctx, data, cId, _cb) { var createInviteLink = function (ctx, data, cId, _cb) {
var cb = Util.mkAsync(Util.once(_cb)); var cb = Util.mkAsync(Util.once(_cb));
@ -1629,10 +1732,11 @@ define([
} }
var rosterKeys = Crypto.Team.deriveMemberKeys(rosterData.edit, myKeys); var rosterKeys = Crypto.Team.deriveMemberKeys(rosterData.edit, myKeys);
Roster.create({ Roster.create({
network: ctx.store.network, network: ctx.store.network || ctx.store.networkPromise,
channel: rosterData.channel, channel: rosterData.channel,
keys: rosterKeys, keys: rosterKeys,
anon_rpc: ctx.store.anon_rpc, store: ctx.store,
Cache: Cache
}, waitFor(function (err, roster) { }, waitFor(function (err, roster) {
if (err) { if (err) {
waitFor.abort(); waitFor.abort();
@ -1704,6 +1808,7 @@ define([
emit: emit, emit: emit,
onReadyHandlers: {}, onReadyHandlers: {},
teams: {}, teams: {},
cache: {},
updateMetadata: cfg.updateMetadata, updateMetadata: cfg.updateMetadata,
updateProgress: cfg.updateLoadingProgress, updateProgress: cfg.updateLoadingProgress,
progress: 0 progress: 0
@ -1734,6 +1839,7 @@ define([
}; };
// Remove duplicate teams // Remove duplicate teams
var removeDuplicates = function () {
var _teams = {}; var _teams = {};
Object.keys(teams).forEach(function (id) { Object.keys(teams).forEach(function (id) {
try { try {
@ -1788,9 +1894,59 @@ define([
delete teams[id]; delete teams[id];
} catch (e) { console.error(e); } } catch (e) { console.error(e); }
}); });
};
// Load teams // Load teams
Object.keys(teams).forEach(function (id) { Object.keys(teams).forEach(function (id) {
ctx.onReadyHandlers[id] = [];
if (!Util.find(teams, [id, 'keys', 'mailbox'])) {
teams[id].keys.mailbox = deriveMailbox(teams[id]);
}
openChannel(ctx, teams[id], id, waitFor(function (err) {
if (err) {
var txt = typeof(err) === "string" ? err : (err.type || err.message);
Feedback.send("TEAM_LOADING_ERROR="+txt);
return void console.error(err);
}
console.debug('Team '+id+' cache ready');
}), true);
});
// Proxy is ready, check if our team list has changed
team.onReady = function (waitFor) {
removeDuplicates();
// Close all the teams from our cache that have been removed and add waitFor to the
// one that still exist
var checkTeam = function (id) {
if (!teams[id]) {
closeTeam(ctx, id);
delete ctx.onReadyHandlers[id];
return true;
}
return false;
};
Object.keys(ctx.teams).forEach(checkTeam);
Object.keys(ctx.onReadyHandlers).forEach(function (id) {
var closed = checkTeam(id);
if (closed) { return; }
var team = ctx.store.proxy.teams[id];
var rosterChan = Util.find(team, ['keys', 'roster', 'channel']);
var _cb = waitFor();
nThen(function (w) {
checkTeamChannels(ctx, id, team.channel, rosterChan, w, _cb);
});
ctx.onReadyHandlers[id].push({
cb: _cb
});
});
// Load all the teams that weren't in our cache
Object.keys(teams).forEach(function (id) {
// Team already loaded? abort
if (ctx.onReadyHandlers[id] || ctx.teams[id]) { return; }
// Load team
ctx.onReadyHandlers[id] = []; ctx.onReadyHandlers[id] = [];
if (!Util.find(teams, [id, 'keys', 'mailbox'])) { if (!Util.find(teams, [id, 'keys', 'mailbox'])) {
teams[id].keys.mailbox = deriveMailbox(teams[id]); teams[id].keys.mailbox = deriveMailbox(teams[id]);
@ -1805,6 +1961,10 @@ define([
})); }));
}); });
openCachedTeamChat();
onStoreReady.fire();
};
team.getTeam = function (id) { team.getTeam = function (id) {
return ctx.teams[id]; return ctx.teams[id];
}; };

@ -431,7 +431,6 @@ define([
content.push(h('h3', Messages.team_listTitle + ' ' + slots)); content.push(h('h3', Messages.team_listTitle + ' ' + slots));
console.error(createSlots, Constants);
APP.teams = {}; APP.teams = {};
var created = 0; var created = 0;
@ -945,6 +944,9 @@ define([
teamId: APP.team teamId: APP.team
}, function (obj) { }, function (obj) {
if (obj && obj.error) { if (obj && obj.error) {
if (obj.error === 'OFFLINE') {
return; // XXX show offline message in chat section
}
return void UI.alert(Messages.error); return void UI.alert(Messages.error);
} }
common.setTeamChat(obj.channel); common.setTeamChat(obj.channel);

@ -114,6 +114,7 @@ define([
addRpc: addRpc, addRpc: addRpc,
addData: addData, addData: addData,
isDrive: true, // Used for history... isDrive: true, // Used for history...
cache: true,
}); });
}); });
}); });

Loading…
Cancel
Save