diff --git a/www/calendar/inner.js b/www/calendar/inner.js index 16d6c95dd..660b8f2a1 100644 --- a/www/calendar/inner.js +++ b/www/calendar/inner.js @@ -125,7 +125,7 @@ Messages.calendar_errorNoCalendar = "No editable calendar selected!"; var getCalendars = function () { return Object.keys(APP.calendars).map(function (id) { var c = APP.calendars[id]; - if (c.hidden || c.restricted) { return; } + if (c.hidden || c.restricted || c.loading) { return; } var md = Util.find(c, ['content', 'metadata']); if (!md) { return void console.error('Ignore calendar without metadata'); } return { @@ -142,7 +142,7 @@ Messages.calendar_errorNoCalendar = "No editable calendar selected!"; var s = []; Object.keys(APP.calendars).forEach(function (id) { var c = APP.calendars[id]; - if (c.hidden || c.restricted) { return; } + if (c.hidden || c.restricted || c.loading) { return; } var data = c.content || {}; Object.keys(data.content || {}).forEach(function (uid) { var obj = data.content[uid]; @@ -353,7 +353,9 @@ Messages.calendar_errorNoCalendar = "No editable calendar selected!"; // XXX handle RESTRICTED calendars (data.restricted) var data = APP.calendars[id]; var edit; - if (!data.readOnly) { + if (data.loading) { + edit = h('i.fa.fa-spinner.fa-spin'); + } else if (!data.readOnly) { edit = makeEditDropdown(id, teamId); } var md = Util.find(data, ['content', 'metadata']); diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 5b381c8ec..6ce036e53 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -178,6 +178,24 @@ define([ typeof(store.proxy.curvePublic) === 'string'; }; + Store.isOwned = function (owners) { + var edPublic = store.proxy.edPublic; + // Not logged in? false + if (!edPublic) { return false; } + // No owners? false + if (!Array.isArray(owners) || !owners.length) { return false; } + + if (owners.indexOf(edPublic) !== -1) { return true; } + + // No team + var teams = store.proxy.teams; + if (!teams) { return false; } + return Object.keys(teams).some(function (id) { + var ed = Util.find(teams[id], ['keys', 'drive', 'edPublic']); + return ed && owners.indexOf(ed) !== -1; + }); + }; + var getUserChannelList = function () { var userChannel = store.driveChannel; if (!userChannel) { return null; } diff --git a/www/common/outer/calendar.js b/www/common/outer/calendar.js index db4c0b654..a5d4b91a6 100644 --- a/www/common/outer/calendar.js +++ b/www/common/outer/calendar.js @@ -90,24 +90,18 @@ ctx.calendars[channel] = { var initializeCalendars = function (ctx, cb) { var proxy = ctx.store.proxy; var calendars = proxy.calendars = proxy.calendars || {}; - /*if (!calendars.own) { - var own = calendars.own = makeCalendar(true); - own.color = ctx.Store.getUserColor(); - } - */ setTimeout(cb); - // XXX for each team, if we have edit rights, create the team calendar? - // XXX or maybe do it from the team app? }; var sendUpdate = function (ctx, c) { ctx.emit('UPDATE', { teams: c.stores, id: c.channel, + loading: !c.ready && !c.cacheready, readOnly: c.readOnly || (!c.ready && c.cacheready), deleted: !c.stores.length, restricted: c.restricted, - owned: false, // XXX XXX destroy + owned: ctx.Store.isOwned(c.owners), content: Util.clone(c.proxy) }, ctx.clients); }; @@ -162,10 +156,53 @@ ctx.calendars[channel] = { var secret = Hash.getSecrets('calendar', parsed.hash); var crypto = Crypto.createEncryptor(secret.keys); - nThen(function (waitFor) { - // XXX Check if channel exists - // XXX Get pad metadata (offline??) + c.proxy = { + metadata: { + color: data.color, + title: data.title + } + }; + update(); + + var onDeleted = function () { + // Remove this calendar from all our teams + c.stores.forEach(function (storeId) { + var store = getStore(ctx, storeId); + if (!store || !store.rpc || !store.proxy.calendars) { return; } + delete store.proxy.calendars[channel]; + // And unpin + var unpin = store.unpin || ctx.unpinPads; + unpin([channel], function (res) { + if (res && res.error) { console.error(res.error); } + }); + }); + + // Close listmap, update the UI and clear the memory + if (c.lm) { c.lm.stop(); } + c.stores = []; + sendUpdate(ctx, c); + delete ctx.calendars[channel]; + }; + nThen(function (waitFor) { + if (!ctx.store.network || cfg.isNew) { return; } + // This is supposed to be an existing channel. Make sure it exists on the server + // before trying to load it. + // NOTE: if we can't check (error), we can skip this step. On "ready", we have + // another check to make sure we won't make a new calendar + ctx.Store.isNewChannel(null, channel, waitFor(function (obj) { + if (obj && obj.error) { + // If we can't check, skip this part + return; + } + if (obj && typeof(obj.isNew) === "boolean") { + if (obj.isNew) { + onDeleted(); + waitFor.abort(); + return; + } + } + })); }).nThen(function () { // Set the owners as the first store opening it. We don't know yet if it's a new or // existing calendar. "owners' will be ignored if the calendar already exists. @@ -180,7 +217,7 @@ ctx.calendars[channel] = { var config = { data: {}, - network: ctx.store.network || ctx.store.networkPromise, // XXX offline + network: ctx.store.network || ctx.store.networkPromise, channel: secret.channel, crypto: crypto, owners: [edPublic], @@ -197,36 +234,18 @@ ctx.calendars[channel] = { c.lm = lm; var proxy = c.proxy = lm.proxy; - var onDeleted = function () { - nThen(function (w) { - c.stores.forEach(function (storeId) { - var store = getStore(ctx, storeId); - if (!store || !store.rpc || !store.proxy.calendars) { return; } - delete store.proxy.calendars[channel]; - var unpin = store.unpin || ctx.unpinPads; - unpin([channel], function (res) { - if (res && res.error) { console.error(res.error); } - }); - ctx.Store.onSync(storeId, w()); - }); - }).nThen(function () { - lm.stop(); - c.stores = []; - sendUpdate(ctx, c); - delete ctx.calendars[channel]; - }); - }; - lm.proxy.on('cacheready', function () { if (!proxy.metadata) { return; } c.cacheready = true; setTimeout(update); if (cb) { cb(null, lm.proxy); } - }).on('ready', function () { + }).on('ready', function (info) { + var md = info.metadata; + c.owners = md.owners || []; c.ready = true; if (!proxy.metadata) { if (!cfg.isNew) { - // XXX no metadata on an existing calendar: deleted calendar + // no metadata on an existing calendar: deleted calendar return void onDeleted(); } proxy.metadata = { @@ -285,8 +304,6 @@ ctx.calendars[channel] = { }); Object.keys(ctx.calendars).forEach(function (channel) { var c = ctx.calendars[channel] || {}; - console.log(channel, c); - if (!c.ready && !c.cacheready) { return; } sendUpdate(ctx, c); }); }; @@ -444,21 +461,27 @@ ctx.calendars[channel] = { return void subscribe(ctx, data, clientId, cb); } if (cmd === 'CREATE') { + if (ctx.store.offline) { return void cb({error: 'OFFLINE'}); } return void createCalendar(ctx, data, clientId, cb); } if (cmd === 'UPDATE') { + if (ctx.store.offline) { return void cb({error: 'OFFLINE'}); } return void updateCalendar(ctx, data, clientId, cb); } if (cmd === 'DELETE') { + if (ctx.store.offline) { return void cb({error: 'OFFLINE'}); } return void deleteCalendar(ctx, data, clientId, cb); } if (cmd === 'CREATE_EVENT') { + if (ctx.store.offline) { return void cb({error: 'OFFLINE'}); } return void createEvent(ctx, data, clientId, cb); } if (cmd === 'UPDATE_EVENT') { + if (ctx.store.offline) { return void cb({error: 'OFFLINE'}); } return void updateEvent(ctx, data, clientId, cb); } if (cmd === 'DELETE_EVENT') { + if (ctx.store.offline) { return void cb({error: 'OFFLINE'}); } return void deleteEvent(ctx, data, clientId, cb); } };