mirror of
https://github.com/cryptpad/cryptpad.git
synced 2026-09-14 11:05:41 +05:00
Basic calendar app
This commit is contained in:
parent
d853466ad2
commit
bbd35d0307
@ -419,3 +419,7 @@
|
||||
@cp_flatpickr-bg: @cryptpad_color_grey_800;
|
||||
@cp_flatpickr-highlight: @cryptpad_color_brand_300;
|
||||
@cp_flatpickr-highlight-text: @cryptpad_color_grey_800;
|
||||
|
||||
// Calendar
|
||||
|
||||
@cp_calendar-border: @cryptpad_color_grey_600;
|
||||
|
||||
@ -419,3 +419,7 @@
|
||||
@cp_flatpickr-bg: @cryptpad_color_grey_50;
|
||||
@cp_flatpickr-highlight: @cryptpad_color_brand_fadest;
|
||||
@cp_flatpickr-highlight-text: @cryptpad_text_col;
|
||||
|
||||
// Calendar
|
||||
|
||||
@cp_calendar-border: @cryptpad_color_grey_300;
|
||||
|
||||
@ -13,9 +13,14 @@
|
||||
color: @cp_forms-fg;
|
||||
background-color: @cp_forms-bg;
|
||||
border: 1px solid @cp_forms-border;
|
||||
width: 100%;
|
||||
font-size: 100%;
|
||||
padding: @alertify_padding-base;
|
||||
&:not(.tui-full-calendar-content) {
|
||||
width: 100%;
|
||||
}
|
||||
&.tui-full-calendar-content {
|
||||
font-size: @colortheme_app-font-size;
|
||||
}
|
||||
&[readonly] {
|
||||
background-color: @cp_forms-readonly;
|
||||
border-color: @cp_forms-readonly-border;
|
||||
|
||||
66
www/calendar/app-calendar.less
Normal file
66
www/calendar/app-calendar.less
Normal file
@ -0,0 +1,66 @@
|
||||
@import (reference) '../../customize/src/less2/include/framework.less';
|
||||
|
||||
&.cp-app-calendar {
|
||||
|
||||
.framework_min_main();
|
||||
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
||||
#cp-calendar {
|
||||
.tui-full-calendar-layout {
|
||||
background-color: @cp_sidebar-right-bg !important;
|
||||
color: @cryptpad_text_col;
|
||||
}
|
||||
.tui-full-calendar-timegrid-gridline, .tui-full-calendar-time-date {
|
||||
border-color: @cp_calendar-border !important;
|
||||
}
|
||||
.tui-full-calendar-splitter, .tui-full-calendar-left, .tui-full-calendar-dayname-container, .tui-full-calendar-weekday-grid-line {
|
||||
border-color: @cp_calendar-border !important;
|
||||
|
||||
}
|
||||
.tui-full-calendar-popup-container {
|
||||
background: @cp_flatpickr-bg;
|
||||
color: @cryptpad_text_col;
|
||||
}
|
||||
.tui-full-calendar-popup-section-item {
|
||||
height: auto;
|
||||
&:not(button) {
|
||||
border: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding-left: 0;
|
||||
input { flex: 1; }
|
||||
}
|
||||
}
|
||||
.tui-full-calendar-popup-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.tui-full-calendar-section-date-dash {
|
||||
height: auto;
|
||||
}
|
||||
.tui-full-calendar-section-title, .tui-full-calendar-section-location {
|
||||
width: 100%;
|
||||
}
|
||||
.tui-full-calendar-dropdown-menu {
|
||||
top: 38px;
|
||||
background-color: @cp_dropdown-bg;
|
||||
color: @cp_dropdown-fg;
|
||||
}
|
||||
.tui-full-calendar-section-state, #tui-full-calendar-schedule-private {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cp-calendar-close {
|
||||
height: auto;
|
||||
line-height: initial;
|
||||
border: 1px solid;
|
||||
&:not(:hover) {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
12
www/calendar/index.html
Normal file
12
www/calendar/index.html
Normal file
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CryptPad</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="referrer" content="no-referrer" />
|
||||
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||
<link href="/customize/src/outer.css?ver=1.3.2" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<iframe-placeholder>
|
||||
18
www/calendar/inner.html
Normal file
18
www/calendar/inner.html
Normal file
@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="cp-app-noscroll">
|
||||
<head>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||
<script async data-bootload="/calendar/inner.js" data-main="/common/sframe-boot.js?ver=1.7" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||
<style>
|
||||
.loading-hidden { display: none; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="cp-app-calendar">
|
||||
<div id="cp-toolbar" class="cp-toolbar-container"></div>
|
||||
<div id="cp-container"></div>
|
||||
<noscript>
|
||||
<p><strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.</p>
|
||||
<p><strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.</p>
|
||||
</noscript>
|
||||
</body>
|
||||
|
||||
231
www/calendar/inner.js
Normal file
231
www/calendar/inner.js
Normal file
@ -0,0 +1,231 @@
|
||||
define([
|
||||
'jquery',
|
||||
'/bower_components/chainpad-crypto/crypto.js',
|
||||
'/common/toolbar.js',
|
||||
'/bower_components/nthen/index.js',
|
||||
'/common/sframe-common.js',
|
||||
'/common/common-util.js',
|
||||
'/common/common-hash.js',
|
||||
'/common/common-interface.js',
|
||||
'/common/common-ui-elements.js',
|
||||
'/common/common-realtime.js',
|
||||
'/common/clipboard.js',
|
||||
'/common/inner/common-mediatag.js',
|
||||
'/common/hyperscript.js',
|
||||
'/customize/messages.js',
|
||||
'/customize/application_config.js',
|
||||
'/lib/calendar/tui-calendar.min.js',
|
||||
|
||||
'css!/lib/calendar/tui-calendar.min.css',
|
||||
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'less!/calendar/app-calendar.less',
|
||||
], function (
|
||||
$,
|
||||
Crypto,
|
||||
Toolbar,
|
||||
nThen,
|
||||
SFCommon,
|
||||
Util,
|
||||
Hash,
|
||||
UI,
|
||||
UIElements,
|
||||
Realtime,
|
||||
Clipboard,
|
||||
MT,
|
||||
h,
|
||||
Messages,
|
||||
AppConfig,
|
||||
Calendar
|
||||
)
|
||||
{
|
||||
var APP = window.APP = {
|
||||
};
|
||||
console.log(Calendar);
|
||||
|
||||
|
||||
var common;
|
||||
var sframeChan;
|
||||
|
||||
Messages.calendar = "Calendar"; // XXX
|
||||
|
||||
var updateCalendar = function (data) {
|
||||
console.log(data);
|
||||
|
||||
};
|
||||
|
||||
var templates = {
|
||||
popupSave: function () {
|
||||
return Messages.settings_save;
|
||||
}
|
||||
};
|
||||
|
||||
var makeCalendar = function (ctx) {
|
||||
var $container = $('#cp-container');
|
||||
$container.append([
|
||||
h('div#menu', [
|
||||
h('span#renderRange.render-range')
|
||||
]),
|
||||
h('div#cp-calendar')
|
||||
]);
|
||||
|
||||
var cal = new Calendar('#cp-calendar', {
|
||||
defaultView: 'week', // weekly view option
|
||||
useCreationPopup: true,
|
||||
useDetailPopup: true,
|
||||
usageStatistics: false,
|
||||
calendars: [{
|
||||
id: '1',
|
||||
name: 'My Calendar',
|
||||
color: '#ffffff',
|
||||
bgColor: '#9e5fff',
|
||||
dragBgColor: '#9e5fff',
|
||||
borderColor: '#9e5fff'
|
||||
}, {
|
||||
id: '2',
|
||||
name: 'Company',
|
||||
color: '#00a9ff',
|
||||
bgColor: '#00a9ff',
|
||||
dragBgColor: '#00a9ff',
|
||||
borderColor: '#00a9ff'
|
||||
}]
|
||||
});
|
||||
cal.on('beforeCreateSchedule', function(event) {
|
||||
var startTime = event.start;
|
||||
var endTime = event.end;
|
||||
var isAllDay = event.isAllDay;
|
||||
var guide = event.guide;
|
||||
var triggerEventName = event.triggerEventName;
|
||||
|
||||
// XXX Recurrence (later)
|
||||
// On creation, select a recurrence rule (daily / weekly / monthly / more weird rules)
|
||||
// then mark it under recurrence rule with a uid (the same for all the recurring events)
|
||||
// ie: recurrenceRule: DAILY|{uid}
|
||||
// Use template to hide "recurrenceRule" from the detailPopup or at least to use
|
||||
// a non technical value
|
||||
|
||||
var schedule = {
|
||||
id: Util.uid(),
|
||||
calendarId: event.calendarId,
|
||||
title: Util.fixHTML(event.title),
|
||||
category: "time",
|
||||
location: Util.fixHTML(event.location),
|
||||
start: event.start,
|
||||
end: event.end,
|
||||
};
|
||||
|
||||
/*
|
||||
if (triggerEventName === 'click') {
|
||||
// open writing simple schedule popup
|
||||
schedule = {
|
||||
};
|
||||
} else if (triggerEventName === 'dblclick') {
|
||||
// open writing detail schedule popup
|
||||
schedule = {
|
||||
};
|
||||
}
|
||||
*/
|
||||
|
||||
cal.createSchedules([schedule]);
|
||||
});
|
||||
};
|
||||
|
||||
var createToolbar = function () {
|
||||
var displayed = ['useradmin', 'newpad', 'limit', 'pageTitle', 'notifications'];
|
||||
var configTb = {
|
||||
displayed: displayed,
|
||||
sfCommon: common,
|
||||
$container: APP.$toolbar,
|
||||
pageTitle: Messages.calendar,
|
||||
metadataMgr: common.getMetadataMgr(),
|
||||
};
|
||||
APP.toolbar = Toolbar.create(configTb);
|
||||
APP.toolbar.$rightside.hide();
|
||||
};
|
||||
|
||||
var onEvent = function (obj) {
|
||||
var ev = obj.ev;
|
||||
var data = obj.data;
|
||||
if (ev === 'UPDATE') {
|
||||
console.log('Update');
|
||||
updateCalendar(data);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
nThen(function (waitFor) {
|
||||
$(waitFor(UI.addLoadingScreen));
|
||||
SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
|
||||
}).nThen(function (waitFor) {
|
||||
APP.$toolbar = $('#cp-toolbar');
|
||||
sframeChan = common.getSframeChannel();
|
||||
sframeChan.onReady(waitFor());
|
||||
}).nThen(function (/*waitFor*/) {
|
||||
createToolbar();
|
||||
|
||||
// Fix flatpickr selection
|
||||
var MutationObserver = window.MutationObserver;
|
||||
var onFlatPickr = function (el) {
|
||||
// Don't close event creation popup when clicking on flatpickr
|
||||
$(el).mousedown(function (e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
};
|
||||
var observer = new MutationObserver(function(mutations) {
|
||||
mutations.forEach(function(mutation) {
|
||||
var node;
|
||||
for (var i = 0; i < mutation.addedNodes.length; i++) {
|
||||
var node = mutation.addedNodes[i];
|
||||
if (node.classList && node.classList.contains('flatpickr-calendar')) {
|
||||
onFlatPickr(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
observer.observe($('body')[0], {
|
||||
childList: true,
|
||||
subtree: false
|
||||
});
|
||||
|
||||
// Customize creation/update popup
|
||||
var onCalendarPopup = function (el) {
|
||||
var $el = $(el);
|
||||
$el.find('.tui-full-calendar-confirm').addClass('btn btn-primary');
|
||||
$el.find('input').attr('autocomplete', 'off');
|
||||
$el.find('.tui-full-calendar-dropdown-button').addClass('btn btn-secondary');
|
||||
$el.find('.tui-full-calendar-popup-close').addClass('btn btn-cancel fa fa-times cp-calendar-close').empty();
|
||||
console.log(el);
|
||||
};
|
||||
var observer = new MutationObserver(function(mutations) {
|
||||
mutations.forEach(function(mutation) {
|
||||
var node;
|
||||
for (var i = 0; i < mutation.addedNodes.length; i++) {
|
||||
var node = mutation.addedNodes[i];
|
||||
if (node.classList && node.classList.contains('tui-full-calendar-popup')) {
|
||||
onCalendarPopup(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
observer.observe($('body')[0], {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
|
||||
APP.module = common.makeUniversal('calendar', {
|
||||
onEvent: onEvent
|
||||
});
|
||||
APP.module.execCommand('SUBSCRIBE', null, function () {
|
||||
console.error('subscribed');
|
||||
// XXX build UI
|
||||
makeCalendar();
|
||||
UI.removeLoadingScreen();
|
||||
});
|
||||
|
||||
var metadataMgr = common.getMetadataMgr();
|
||||
var privateData = metadataMgr.getPrivateData();
|
||||
|
||||
APP.origin = privateData.origin;
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
32
www/calendar/main.js
Normal file
32
www/calendar/main.js
Normal file
@ -0,0 +1,32 @@
|
||||
// Load #1, load as little as possible because we are in a race to get the loading screen up.
|
||||
define([
|
||||
'/bower_components/nthen/index.js',
|
||||
'/api/config',
|
||||
'/common/dom-ready.js',
|
||||
'/common/sframe-common-outer.js',
|
||||
], function (nThen, ApiConfig, DomReady, SFCommonO) {
|
||||
|
||||
// Loaded in load #2
|
||||
nThen(function (waitFor) {
|
||||
DomReady.onReady(waitFor());
|
||||
}).nThen(function (waitFor) {
|
||||
SFCommonO.initIframe(waitFor);
|
||||
}).nThen(function (/*waitFor*/) {
|
||||
var getSecrets = function (Cryptpad, Utils, cb) {
|
||||
// XXX open calendar from URL
|
||||
};
|
||||
var addData = function (meta, Cryptpad, user) {
|
||||
// XXX flag when opening URL
|
||||
meta.isOwnProfile = !window.location.hash ||
|
||||
window.location.hash.slice(1) === user.profile;
|
||||
};
|
||||
SFCommonO.start({
|
||||
//getSecrets: getSecrets,
|
||||
//noHash: true, // Don't add the hash in the URL if it doesn't already exist
|
||||
//addRpc: addRpc,
|
||||
//addData: addData,
|
||||
//owned: true,
|
||||
noRealtime: true
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -12,7 +12,7 @@ define(function() {
|
||||
* You should never remove the drive from this list.
|
||||
*/
|
||||
config.availablePadTypes = ['drive', 'teams', 'pad', 'sheet', 'code', 'slide', 'poll', 'kanban', 'whiteboard',
|
||||
/*'doc', 'presentation',*/ 'file', /*'todo',*/ 'contacts'];
|
||||
/*'doc', 'presentation',*/ 'file', /*'todo',*/ 'contacts', 'calendar'];
|
||||
/* The registered only types are apps restricted to registered users.
|
||||
* You should never remove apps from this list unless you know what you're doing. The apps
|
||||
* listed here by default can't work without a user account.
|
||||
@ -20,7 +20,7 @@ define(function() {
|
||||
* users and these users will be redirected to the login page if they still try to access
|
||||
* the app
|
||||
*/
|
||||
config.registeredOnlyTypes = ['file', 'contacts', 'notifications', 'support'];
|
||||
config.registeredOnlyTypes = ['file', 'contacts', 'notifications', 'support', 'calendar'];
|
||||
|
||||
/* CryptPad is available is multiple languages, but only English and French are maintained
|
||||
* by the developers. The other languages may be outdated, and any missing string for a langauge
|
||||
|
||||
@ -15,6 +15,6 @@ define(['/customize/application_config.js'], function (AppConfig) {
|
||||
MAX_TEAMS_SLOTS: AppConfig.maxTeamsSlots || 5,
|
||||
MAX_TEAMS_OWNED: AppConfig.maxOwnedTeams || 5,
|
||||
// Apps
|
||||
criticalApps: ['profile', 'settings', 'debug', 'admin', 'support', 'notifications']
|
||||
criticalApps: ['profile', 'settings', 'debug', 'admin', 'support', 'notifications', 'calendar']
|
||||
};
|
||||
});
|
||||
|
||||
@ -466,7 +466,6 @@ define([
|
||||
|
||||
return {
|
||||
add: function(common, data) {
|
||||
console.log(data);
|
||||
var type = data.content.msg.type;
|
||||
|
||||
if (handlers[type]) {
|
||||
|
||||
@ -19,6 +19,7 @@ define([
|
||||
'/common/outer/team.js',
|
||||
'/common/outer/messenger.js',
|
||||
'/common/outer/history.js',
|
||||
'/common/outer/calendar.js',
|
||||
'/common/outer/network-config.js',
|
||||
'/customize/application_config.js',
|
||||
|
||||
@ -32,7 +33,7 @@ define([
|
||||
], function (Sortify, UserObject, ProxyManager, Migrate, Hash, Util, Constants, Feedback,
|
||||
Realtime, Messaging, Pinpad, Cache,
|
||||
SF, Cursor, OnlyOffice, Mailbox, Profile, Team, Messenger, History,
|
||||
NetConfig, AppConfig,
|
||||
Calendar, NetConfig, AppConfig,
|
||||
Crypto, ChainPad, CpNetflux, Listmap, Netflux, nThen, Saferphore) {
|
||||
|
||||
var onReadyEvt = Util.mkEvent(true);
|
||||
@ -574,7 +575,7 @@ define([
|
||||
getColor().toString(16) +
|
||||
getColor().toString(16);
|
||||
};
|
||||
var getUserColor = function () {
|
||||
Store.getUserColor = function () {
|
||||
var color = Util.find(store, ['proxy', 'settings', 'general', 'cursor', 'color']);
|
||||
if (!color) {
|
||||
color = getRandomColor();
|
||||
@ -601,7 +602,7 @@ define([
|
||||
uid: proxy.uid || store.noDriveUid, // Random uid in nodrive mode
|
||||
avatar: Util.find(proxy, ['profile', 'avatar']),
|
||||
profile: Util.find(proxy, ['profile', 'view']),
|
||||
color: getUserColor(),
|
||||
color: Store.getUserColor(),
|
||||
notifications: Util.find(proxy, ['mailboxes', 'notifications', 'channel']),
|
||||
curvePublic: proxy.curvePublic,
|
||||
},
|
||||
@ -2712,6 +2713,7 @@ define([
|
||||
loadUniversal(Messenger, 'messenger', waitFor);
|
||||
store.messenger = store.modules['messenger'];
|
||||
loadUniversal(Profile, 'profile', waitFor);
|
||||
loadUniversal(Calendar, 'calendar', waitFor);
|
||||
if (store.modules['team']) { store.modules['team'].onReady(waitFor); }
|
||||
loadUniversal(History, 'history', waitFor);
|
||||
}).nThen(function () {
|
||||
|
||||
261
www/common/outer/calendar.js
Normal file
261
www/common/outer/calendar.js
Normal file
@ -0,0 +1,261 @@
|
||||
define([
|
||||
'/common/common-util.js',
|
||||
'/common/common-hash.js',
|
||||
'/common/common-constants.js',
|
||||
'/common/common-realtime.js',
|
||||
'/customize/messages.js',
|
||||
'/bower_components/chainpad-listmap/chainpad-listmap.js',
|
||||
'/bower_components/chainpad-crypto/crypto.js',
|
||||
'/bower_components/chainpad/chainpad.dist.js',
|
||||
], function (Util, Hash, Constants, Realtime, Messages, Listmap, Crypto, ChainPad) {
|
||||
var Calendar = {};
|
||||
|
||||
|
||||
/* TODO
|
||||
* Calendar
|
||||
{
|
||||
href,
|
||||
roHref,
|
||||
channel, (pinning)
|
||||
title, (when created from the UI, own calendar has no title)
|
||||
color
|
||||
}
|
||||
|
||||
|
||||
* Own drive
|
||||
{
|
||||
calendars: {
|
||||
own: calendar,
|
||||
extra: {
|
||||
uid: calendar,
|
||||
uid: calendar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
* Team drive
|
||||
{
|
||||
calendars: {
|
||||
own: calendar,
|
||||
extra: {
|
||||
uid: calendar,
|
||||
uid: calendar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
* Calendars are listmap
|
||||
{
|
||||
content: {},
|
||||
metadata: {
|
||||
title: "pewpewpew"
|
||||
}
|
||||
}
|
||||
|
||||
ctx.calendars[channel] = {
|
||||
lm: lm,
|
||||
proxy: lm.proxy?
|
||||
stores: [teamId, teamId, 1]
|
||||
}
|
||||
|
||||
* calendar app can subscribe to this module
|
||||
* when a listmap changes, push an update for this calendar to subscribed tabs
|
||||
* Ability to open a calendar not stored in the stores but from its URL directly
|
||||
* No "userlist" visible in the UI
|
||||
* No framework
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
var makeCalendar = function () {
|
||||
var hash = Hash.createRandomHash('calendar');
|
||||
var secret = Hash.getSecrets('calendar', hash);
|
||||
var roHash = Hash.getViewHashFromKeys(secret);
|
||||
var href = Hash.hashToHref(hash, 'calendar');
|
||||
var roHref = Hash.hashToHref(roHash, 'calendar');
|
||||
return {
|
||||
href: href,
|
||||
roHref: roHref,
|
||||
channel: secret.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,
|
||||
readOnly: c.readOnly,
|
||||
data: Util.clone(c.proxy)
|
||||
}, ctx.clients);
|
||||
};
|
||||
|
||||
var openChannel = function (ctx, cfg) {
|
||||
var teamId = cfg.storeId;
|
||||
var data = cfg.data;
|
||||
var channel = data.channel;
|
||||
if (!channel) { return; }
|
||||
|
||||
var c = ctx.calendars[channel];
|
||||
if (c) {
|
||||
if (c.stores && c.stores.indexOf(teamId) !== -1) { return; }
|
||||
if (c.readOnly && data.href) {
|
||||
// XXX UPGRADE
|
||||
// XXX different cases if already ready or not?
|
||||
}
|
||||
c.stores.push(teamId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Multiple teams can have the same calendar. Make sure we remember the list of stores
|
||||
// that know it so that we don't close the calendar when leaving/deleting a team.
|
||||
c = ctx.calendars[channel] = {
|
||||
ready: false,
|
||||
channel: channel,
|
||||
readOnly: !data.href,
|
||||
stores: [teamId]
|
||||
};
|
||||
|
||||
var update = function () {
|
||||
console.log(ctx.clients);
|
||||
sendUpdate(ctx, c);
|
||||
};
|
||||
|
||||
|
||||
var parsed = Hash.parsePadUrl(data.href || data.roHref);
|
||||
var secret = Hash.getSecrets('calendar', parsed.hash);
|
||||
var crypto = Crypto.createEncryptor(secret.keys);
|
||||
|
||||
// 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.
|
||||
var edPublic;
|
||||
if (teamId === 1) {
|
||||
edPublic = ctx.store.proxy.edPublic;
|
||||
} else {
|
||||
var teams = ctx.store.modules.team && ctx.store.modules.team.getTeamsData();
|
||||
var team = teams && teams[teamId];
|
||||
edPublic = team ? team.edPublic : undefined;
|
||||
}
|
||||
|
||||
var config = {
|
||||
data: {},
|
||||
network: ctx.store.network, // XXX offline
|
||||
channel: secret.channel,
|
||||
crypto: crypto,
|
||||
owners: [edPublic],
|
||||
ChainPad: ChainPad,
|
||||
validateKey: secret.keys.validateKey || undefined,
|
||||
userName: 'calendar',
|
||||
classic: true
|
||||
};
|
||||
|
||||
console.error(channel, config);
|
||||
var lm = Listmap.create(config);
|
||||
c.lm = lm;
|
||||
c.proxy = lm.proxy;
|
||||
|
||||
lm.proxy.on('ready', function () {
|
||||
c.ready = true;
|
||||
console.warn('READY', channel);
|
||||
setTimeout(update);
|
||||
}).on('change', [], function () {
|
||||
setTimeout(update);
|
||||
});
|
||||
};
|
||||
var openChannels = function (ctx) {
|
||||
var findFromStore = function (store) {
|
||||
var c = store.proxy.calendars;
|
||||
if (!c) { return; }
|
||||
if (c.own) {
|
||||
openChannel(ctx, {
|
||||
storeId: store.id || 1,
|
||||
data: c.own
|
||||
});
|
||||
}
|
||||
if (c.extra) {
|
||||
Object.keys(c.extra).forEach(function (channel) {
|
||||
openChannel(ctx, {
|
||||
storeId: store.id || 1,
|
||||
data: c.extra[channel]
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Personal drive
|
||||
findFromStore(ctx.store);
|
||||
};
|
||||
|
||||
|
||||
var subscribe = function (ctx, data, cId, cb) {
|
||||
// Subscribe to new notifications
|
||||
var idx = ctx.clients.indexOf(cId);
|
||||
if (idx === -1) {
|
||||
ctx.clients.push(cId);
|
||||
}
|
||||
cb();
|
||||
Object.keys(ctx.calendars).forEach(function (channel) {
|
||||
var c = ctx.calendars[channel] || {};
|
||||
console.log(channel, c);
|
||||
if (!c.ready) { return; }
|
||||
sendUpdate(ctx, c);
|
||||
});
|
||||
};
|
||||
|
||||
var removeClient = function (ctx, cId) {
|
||||
var idx = ctx.clients.indexOf(cId);
|
||||
ctx.clients.splice(idx, 1);
|
||||
};
|
||||
|
||||
Calendar.init = function (cfg, waitFor, emit) {
|
||||
var calendar = {};
|
||||
var store = cfg.store;
|
||||
if (!store.loggedIn || !store.proxy.edPublic) { return; } // XXX logged in only?
|
||||
var ctx = {
|
||||
store: store,
|
||||
Store: cfg.Store,
|
||||
pinPads: cfg.pinPads,
|
||||
updateMetadata: cfg.updateMetadata,
|
||||
emit: emit,
|
||||
onReady: Util.mkEvent(true),
|
||||
calendars: {},
|
||||
clients: [],
|
||||
};
|
||||
|
||||
initializeCalendars(ctx, waitFor(function (err) {
|
||||
if (err) { return; }
|
||||
openChannels(ctx);
|
||||
}));
|
||||
|
||||
calendar.removeClient = function (clientId) {
|
||||
removeClient(ctx, clientId);
|
||||
};
|
||||
calendar.execCommand = function (clientId, obj, cb) {
|
||||
var cmd = obj.cmd;
|
||||
var data = obj.data;
|
||||
if (cmd === 'SUBSCRIBE') {
|
||||
return void subscribe(ctx, data, clientId, cb);
|
||||
}
|
||||
};
|
||||
|
||||
return calendar;
|
||||
};
|
||||
|
||||
return Calendar;
|
||||
});
|
||||
|
||||
|
||||
|
||||
@ -15,7 +15,9 @@ define([
|
||||
"json.sortify": "/bower_components/json.sortify/dist/JSON.sortify",
|
||||
//"pdfjs-dist/build/pdf": "/bower_components/pdfjs-dist/build/pdf",
|
||||
//"pdfjs-dist/build/pdf.worker": "/bower_components/pdfjs-dist/build/pdf.worker"
|
||||
cm: '/bower_components/codemirror'
|
||||
cm: '/bower_components/codemirror',
|
||||
'tui-code-snippet': '/lib/calendar/tui-code-snippet.min',
|
||||
'tui-date-picker': '/lib/calendar/date-picker',
|
||||
},
|
||||
map: {
|
||||
'*': {
|
||||
|
||||
46
www/lib/calendar/date-picker.js
Normal file
46
www/lib/calendar/date-picker.js
Normal file
@ -0,0 +1,46 @@
|
||||
define([
|
||||
'jquery',
|
||||
'/lib/datepicker/flatpickr.js',
|
||||
|
||||
'css!/lib/datepicker/flatpickr.min.css',
|
||||
], function ($, Flatpickr) {
|
||||
var createRangePicker = function (cfg) {
|
||||
var start = cfg.startpicker;
|
||||
var end = cfg.endpicker;
|
||||
console.log(cfg);
|
||||
console.error(start, end);
|
||||
|
||||
var e = $(end.input)[0];
|
||||
var endPickr = Flatpickr(e, {
|
||||
enableTime: true,
|
||||
minDate: start.date
|
||||
});
|
||||
endPickr.setDate(end.date);
|
||||
|
||||
var s = $(start.input)[0];
|
||||
var startPickr = Flatpickr(s, {
|
||||
enableTime: true,
|
||||
onChange: function () {
|
||||
endPickr.set('minDate', startPickr.parseDate(s.value));
|
||||
}
|
||||
});
|
||||
startPickr.setDate(start.date);
|
||||
|
||||
var getStartDate = function () {
|
||||
setTimeout(function () { $(startPickr.calendarContainer).remove(); });
|
||||
return startPickr.parseDate(s.value);
|
||||
};
|
||||
var getEndDate = function () {
|
||||
setTimeout(function () { $(endPickr.calendarContainer).remove(); });
|
||||
return endPickr.parseDate(e.value);
|
||||
};
|
||||
|
||||
return {
|
||||
getStartDate: getStartDate,
|
||||
getEndDate: getEndDate,
|
||||
};
|
||||
};
|
||||
return {
|
||||
createRangePicker: createRangePicker
|
||||
};
|
||||
});
|
||||
7
www/lib/calendar/tui-calendar.min.css
vendored
Normal file
7
www/lib/calendar/tui-calendar.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
8
www/lib/calendar/tui-calendar.min.js
vendored
Normal file
8
www/lib/calendar/tui-calendar.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
www/lib/calendar/tui-code-snippet.min.js
vendored
Normal file
7
www/lib/calendar/tui-code-snippet.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user