Skip to content

Commit

Permalink
Ready for testing. Calendar UI added
Browse files Browse the repository at this point in the history
  • Loading branch information
pitaj committed Jul 19, 2016
1 parent 4d07f09 commit 7b51418
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 72 deletions.
5 changes: 4 additions & 1 deletion package.json
Expand Up @@ -49,7 +49,9 @@
"eslint-import-resolver-webpack": "^0.3.2",
"eslint-plugin-babel": "^3.2.0",
"eslint-plugin-import": "^1.7.0",
"exports-loader": "^0.6.3",
"file-loader": "^0.9.0",
"imports-loader": "^0.6.5",
"jsdoc": "^3.4.0",
"ts-loader": "^0.8.2",
"typedoc": "^0.4.4",
Expand All @@ -63,7 +65,8 @@
"fullcalendar": "^2.9.0",
"intl": "^1.2.4",
"moment": "^2.14.1",
"validator": "^5.2.0"
"validator": "^5.2.0",
"whatwg-fetch": "^1.0.0"
},
"nbbpm": {
"compatibility": "^1.0.3"
Expand Down
8 changes: 8 additions & 0 deletions public/calendar.less
@@ -1 +1,9 @@
@import (inline) '../node_modules/fullcalendar/dist/fullcalendar.css';

.fc-event {
cursor: pointer;
}

#plugin-calendar-cal-event-display .plugin-calendar-event {
margin: -15px;
}
1 change: 1 addition & 0 deletions public/languages/en_US/calendar.json
Expand Up @@ -6,6 +6,7 @@
"edit_event": "Edit Event",
"event_starting": "Event starting %1: %2",
"now": "now",
"go_to_post": "Go to post",

"event_name": "Event name",
"all_day": "All day",
Expand Down
32 changes: 29 additions & 3 deletions public/templates/calendar.tpl
@@ -1,6 +1,32 @@
<h1>[[calendar:calendar]]</h1>

<div id="calendar"></div>

<script>window['plugin-calendar'] = {{cal}};</script>
<script src="{relative_path}/plugins/nodebb-plugin-calendar/root/calendar.js"></script>
<style type="text/css" class="plugin-calendar-cal-styles">{{calendarEventsStyle}}</style>

<div class="modal fade" id="plugin-calendar-cal-event-display" tabindex="-1"
role="dialog" aria-labelledby="plugin-calendar-cal-event-display-title">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-label="[[global:buttons.close]]">
<i aria-hidden="true" class="fa fa-times"></i>
</button>
<h4 class="modal-title" id="plugin-calendar-cal-event-display-title">
[[calendar:event_title]]
</h4>
</div>
<div class="modal-body">
<div class="plugin-calendar-event panel panel-success"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">
[[global:buttons.close]]
</button>
<a type="button" href="#" class="btn btn-primary" data-dismiss="modal">
[[calendar:go_to_post]]
</a>
</div>
</div>
</div>
</div>
91 changes: 68 additions & 23 deletions src/calendar/index.js
@@ -1,9 +1,7 @@
/* global socket, $, config, app */
/* global socket, $, config, app, RELATIVE_PATH, fetch */

import './vendor/fullcalendar';

// TODO: centralized calendar interface

const convertToFC = event => {
const ev = {
id: event.pid,
Expand All @@ -17,6 +15,14 @@ const convertToFC = event => {
return ev;
};

const toggle = e => {
const panel = $(e.target).closest('.panel');
const cont = panel.find('.panel-collapse');
const height = cont.children()[0].scrollHeight;
cont.css('height', `${height}px`);
panel.toggleClass('closed');
};

const calendarOptions = {
editable: false,
header: {
Expand All @@ -38,35 +44,74 @@ const calendarOptions = {
});
},
eventClick: event => {
// TODO: show modal on event click
const pid = event.id;
fetch(`${RELATIVE_PATH}/api/post/${pid}`)
.then(response => response.json())
.then(path => fetch(`${RELATIVE_PATH}/api${path}`))
.then(response => response.json())
.then(topic => topic.posts.find(post => parseInt(post.pid, 10) === pid).content)
.then(content => {
const $c = $(content);
let div = $c.filter('.plugin-calendar-event');
if (!div.length) {
div = $c.find('.plugin-calendar-event');
}
return div;
})
.then(div => {
const modal = $('#plugin-calendar-cal-event-display');
modal
.find('.modal-body')
.empty()
.append(div);
modal
.find('.modal-footer a.btn-primary')
.attr('href', `${RELATIVE_PATH}/post/${pid}`);
modal
.find('.modal-body .plugin-calendar-event-responses-lists .panel-heading a')
.on('click', toggle);
modal
.find('.plugin-calendar-event-responses-lists .panel-body')
.addClass('topic')
.find('ul')
.addClass('posts');
$(window).trigger('action:calendar.event.display', { pid, modal });
modal
.attr('data-pid', pid)
.modal('show');
});
},
};

/* eslint-disable */
function shadeColor2(color, percent) {
var f=parseInt(color.slice(1),16),t=percent<0?0:255,p=percent<0?percent*-1:percent,R=f>>16,G=f>>8&0x00FF,B=f&0x0000FF;
return "#"+(0x1000000+(Math.round((t-R)*p)+R)*0x10000+(Math.round((t-G)*p)+G)*0x100+(Math.round((t-B)*p)+B)).toString(16).slice(1);
}
// function shadeColor2(color, percent) {
// var f=parseInt(color.slice(1),16),t=percent<0?0:255,p=percent<0?percent*-1:percent,R=f>>16,G=f>>8&0x00FF,B=f&0x0000FF;
// return "#"+(0x1000000+(Math.round((t-R)*p)+R)*0x10000+(Math.round((t-G)*p)+G)*0x100+(Math.round((t-B)*p)+B)).toString(16).slice(1);
// }
/* eslint-enable */

const init = () => {
socket.emit('plugins.calendar.getCategoryColors', (err, colors) => {
if (err) {
app.alertError(err);
return;
}
let style = '<style type="text/css" class="plugin-calendar-cal-styles">';
for (const { cid, bgColor } of colors) {
style += `.plugin-calendar-cal-event-category-${cid} {
background-color: ${bgColor};
border-color: ${shadeColor2(bgColor, -0.2)};
}`;
}
style += '</style>';
$(document.head).append(style);
});
// socket.emit('plugins.calendar.getCategoryColors', (err, colors) => {
// if (err) {
// app.alertError(err);
// return;
// }
// let style = '<style type="text/css" class="plugin-calendar-cal-styles">';
// for (const { cid, bgColor } of colors) {
// style += `.plugin-calendar-cal-event-category-${cid} {
// background-color: ${bgColor};
// border-color: ${shadeColor2(bgColor, -0.2)};
// }`;
// }
// style += '</style>';
// $(document.head).append(style);
// });

$('#calendar').fullCalendar(calendarOptions);

$(window).on('action:ajaxify.end', () => {
// TODO: automatically go to and open event when pid is in hash of url
});
};

$(document).ready(init);
43 changes: 41 additions & 2 deletions src/client/responses.js
Expand Up @@ -77,7 +77,7 @@ const initResponses = () => {
$(document.body).on('click', '.plugin-calendar-event-responses-user .btn', e => {
const button = $(e.target);
const value = button.data('value');
const pid = button.closest('[component=post]').data('pid');
const pid = button.closest('[data-pid]').data('pid');

socket.emit('plugins.calendar.submitResponse', { pid, value }, err => {
if (err) {
Expand All @@ -99,7 +99,46 @@ const initResponses = () => {
}
};

$(window).on('action:posts.loaded action:ajaxify.end action:posts.edited', checkPosts);
$(window).on('action:calendar.event.display', (e, { pid, modal }) => {
const buttonCont = modal.find('.plugin-calendar-event-responses-user');
socket.emit('plugins.calendar.getUserResponse', pid, (err, value) => {
const button = buttonCont.find(`[data-value=${value}]`);

button.siblings().removeClass('active');
button.addClass('active');
});

const $responses = modal.find('.plugin-calendar-event-responses-lists');
socket.emit('plugins.calendar.getResponses', pid, (err, responses) => {
if (err) {
app.alertError(err);
return;
}
if (!responses ||
!responses.yes ||
!responses.maybe ||
!responses.no) {
return;
}

const yess = $responses.find('.plugin-calendar-event-responses-list-yes');
const maybes = $responses.find('.plugin-calendar-event-responses-list-maybe');
const nos = $responses.find('.plugin-calendar-event-responses-list-no');

const yes = responses.yes.map(userTemplate);
const maybe = responses.maybe.map(userTemplate);
const no = responses.no.map(userTemplate);

yess.empty().append(yes.length ? yes : noYesResponses);
maybes.empty().append(maybe.length ? maybe : noMaybeResponses);
nos.empty().append(no.length ? no : noNoResponses);
});
});

$(window).on(
'action:posts.loaded action:ajaxify.end action:posts.edited',
checkPosts
);
checkPosts(null, ajaxify.data);

$(document.body).on(
Expand Down
51 changes: 42 additions & 9 deletions src/lib/controllers.js
@@ -1,4 +1,19 @@
const privileges = require.main.require('./src/privileges');
const categories = require.main.require('./src/categories');

// import { getEventsByDate, filterByPid, escapeEvent } from './event';
import Promise from 'bluebird';
const p = Promise.promisify;

const getAllCategoryFields = p(categories.getAllCategoryFields);
const filterCids = p(privileges.categories.filterCids);

/* eslint-disable */
function shadeColor2(color, percent) {
var f=parseInt(color.slice(1),16),t=percent<0?0:255,p=percent<0?percent*-1:percent,R=f>>16,G=f>>8&0x00FF,B=f&0x0000FF;
return "#"+(0x1000000+(Math.round((t-R)*p)+R)*0x10000+(Math.round((t-G)*p)+G)*0x100+(Math.round((t-B)*p)+B)).toString(16).slice(1);
}
/* eslint-enable */

export default (router, middleware) => {
const renderAdmin = (req, res) => {
Expand All @@ -7,14 +22,14 @@ export default (router, middleware) => {
router.get('/admin/plugins/calendar', middleware.admin.buildHeader, renderAdmin);
router.get('/api/admin/plugins/calendar', renderAdmin);

const renderPage = (req, res) => { // next
// const cb = (err, data) => {
// if (err) {
// next(err);
// return;
// }
// res.render('calendar', data);
// };
const renderPage = (req, res, next) => {
const cb = (err, data) => {
if (err) {
next(err);
return;
}
res.render('calendar', data);
};
//
// const startDate = new Date();
// const endDate = new Date();
Expand All @@ -35,8 +50,26 @@ export default (router, middleware) => {
// };
// })(req.uid).asCallback(cb);

(async () => {
const cats = await getAllCategoryFields(['cid', 'bgColor']);
const filtered = await filterCids('read', cats.map(c => c.cid), req.uid);

const colors = cats.filter(c => filtered.includes(c.cid));

const style = colors.map(({ cid, bgColor }) =>
`.plugin-calendar-cal-event-category-${cid} {
background-color: ${bgColor};
border-color: ${shadeColor2(bgColor, -0.2)};
}`
);

return {
calendarEventsStyle: style.join('\n'),
title: '[[calendar:calendar]]',
};
})().asCallback(cb);

res.render('calendar', {});
// res.render('calendar', {});
};

router.get('/calendar', middleware.buildHeader, renderPage);
Expand Down
19 changes: 6 additions & 13 deletions src/lib/sockets.js
@@ -1,15 +1,8 @@
const privileges = require.main.require('./src/privileges');
const pluginSockets = require.main.require('./src/socket.io/plugins');
const categories = require.main.require('./src/categories');

import { getAll as getAllResponses, submitResponse, getUserResponse } from './responses';
import { getEventsByDate, filterByPid, escapeEvent } from './event';
import Promise from 'bluebird';

const p = Promise.promisify;

const getAllCategoryFields = p(categories.getAllCategoryFields);
const filterCids = p(privileges.categories.filterCids);

const perm = 'plugin-calendar:event:post';

Expand Down Expand Up @@ -46,12 +39,12 @@ pluginSockets.calendar.getUserResponse = ({ uid }, pid, cb) => {
getUserResponse({ uid, pid }).asCallback(cb);
};

pluginSockets.calendar.getCategoryColors = ({ uid }, cb) => (async () => {
const cats = await getAllCategoryFields(['cid', 'bgColor']);
const filtered = await filterCids('read', cats.map(c => c.cid), uid);

return cats.filter(c => filtered.includes(c.cid));
})().asCallback(cb);
// pluginSockets.calendar.getCategoryColors = ({ uid }, cb) => (async () => {
// const cats = await getAllCategoryFields(['cid', 'bgColor']);
// const filtered = await filterCids('read', cats.map(c => c.cid), uid);
//
// return cats.filter(c => filtered.includes(c.cid));
// })().asCallback(cb);

pluginSockets.calendar.getEventsByDate = ({ uid }, { startDate, endDate }, cb) =>
(async () => {
Expand Down
1 change: 1 addition & 0 deletions ts/index.js
@@ -0,0 +1 @@
// TODO: convert to TypeScript?

0 comments on commit 7b51418

Please sign in to comment.