From f825c69ce54ec5f5ad3cccfbdb8fb211f70ea8bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 23 May 2022 15:00:32 +0200 Subject: [PATCH] fetch remote calendar and show on FullCalendar frame --- .../Controller/CalendarController.php | 4 ++ .../RemoteCalendarProxyController.php | 51 ++++++++++++------- .../Calendar/Components/CalendarActive.vue | 2 +- .../Resources/public/vuejs/Calendar/api.js | 9 ++++ .../public/vuejs/Calendar/store/actions.js | 28 ++++++++-- .../public/vuejs/Calendar/store/getters.js | 41 ++++++++++++++- .../public/vuejs/Calendar/store/index.js | 2 +- .../public/vuejs/Calendar/store/mutations.js | 31 +++++++++-- .../public/vuejs/Calendar/store/utils.js | 13 +++++ 9 files changed, 151 insertions(+), 30 deletions(-) diff --git a/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php b/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php index 932addd27..5f9f8cd1d 100644 --- a/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php +++ b/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php @@ -269,6 +269,10 @@ class CalendarController extends AbstractController */ public function newAction(Request $request): Response { + if (!$this->remoteCalendarConnector->isReady()) { + return $this->remoteCalendarConnector->getMakeReadyResponse($request->getUri()); + } + $view = null; $em = $this->getDoctrine()->getManager(); diff --git a/src/Bundle/ChillCalendarBundle/Controller/RemoteCalendarProxyController.php b/src/Bundle/ChillCalendarBundle/Controller/RemoteCalendarProxyController.php index 5c5ad5978..e1926cdfe 100644 --- a/src/Bundle/ChillCalendarBundle/Controller/RemoteCalendarProxyController.php +++ b/src/Bundle/ChillCalendarBundle/Controller/RemoteCalendarProxyController.php @@ -13,6 +13,8 @@ namespace Chill\CalendarBundle\Controller; use Chill\CalendarBundle\RemoteCalendar\Connector\RemoteCalendarConnectorInterface; use Chill\MainBundle\Entity\User; +use Chill\MainBundle\Pagination\PaginatorFactory; +use Chill\MainBundle\Serializer\Model\Collection; use DateTimeImmutable; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -30,8 +32,11 @@ class RemoteCalendarProxyController private SerializerInterface $serializer; - public function __construct(RemoteCalendarConnectorInterface $remoteCalendarConnector, SerializerInterface $serializer) + private PaginatorFactory $paginatorFactory; + + public function __construct(PaginatorFactory $paginatorFactory, RemoteCalendarConnectorInterface $remoteCalendarConnector, SerializerInterface $serializer) { + $this->paginatorFactory = $paginatorFactory; $this->remoteCalendarConnector = $remoteCalendarConnector; $this->serializer = $serializer; } @@ -41,30 +46,38 @@ class RemoteCalendarProxyController */ public function listEventForCalendar(User $user, Request $request): Response { - if ($request->query->has('startDate')) { - $startDate = DateTimeImmutable::createFromFormat('Y-m-d\TH:i:s', $request->query->get('startDate') . 'T00:00:00'); - - if (false === $startDate) { - throw new BadRequestHttpException('startDate on bad format'); - } - } else { - throw new BadRequestHttpException('startDate not provided'); + if (!$request->query->has('dateFrom')) { + throw new BadRequestHttpException('You must provide a dateFrom parameter'); } - if ($request->query->has('endDate')) { - $endDate = DateTimeImmutable::createFromFormat('Y-m-d\TH:i:s', $request->query->get('endDate') . 'T23:59:59'); - - if (false === $endDate) { - throw new BadRequestHttpException('endDate on bad format'); - } - } else { - throw new BadRequestHttpException('endDate not provided'); + if (false === $dateFrom = DateTimeImmutable::createFromFormat( + DateTimeImmutable::ATOM, + $request->query->get('dateFrom') + )) { + throw new BadRequestHttpException('dateFrom not parsable'); } - $events = $this->remoteCalendarConnector->listEventsForUser($user, $startDate, $endDate); + if (!$request->query->has('dateTo')) { + throw new BadRequestHttpException('You must provide a dateTo parameter'); + } + + if (false === $dateTo = DateTimeImmutable::createFromFormat( + DateTimeImmutable::ATOM, + $request->query->get('dateTo') + )) { + throw new BadRequestHttpException('dateTo not parsable'); + } + + $events = $this->remoteCalendarConnector->listEventsForUser($user, $dateFrom, $dateTo); + $paginator = $this->paginatorFactory->create(count($events)); + if (count($events) > 0) { + $paginator->setItemsPerPage($paginator->getTotalItems()); + } + + $collection = new Collection($events, $paginator); return new JsonResponse( - $this->serializer->serialize($events, 'json', ['groups' => ['read']]), + $this->serializer->serialize($collection, 'json', ['groups' => ['read']]), JsonResponse::HTTP_OK, [], true diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/Components/CalendarActive.vue b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/Components/CalendarActive.vue index 0e03e8e00..23ef4565e 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/Components/CalendarActive.vue +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/Components/CalendarActive.vue @@ -35,7 +35,7 @@ export default { }, remoteShow: { set (value) { - this.$store.commit('showUserOnCalendar', {user: this.user, remote: value}); + this.$store.commit('showUserOnCalendar', {user: this.user, remotes: value}); }, get() { return this.$store.getters.isRemoteShownOnCalendarForUser(this.user); diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/api.js b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/api.js index 94dcffd58..d1a4c8426 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/api.js +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/api.js @@ -33,7 +33,16 @@ const fetchCalendarRangeForUser = (user, start, end) => { return fetchResults(uri, {dateFrom, dateTo}); } +const fetchCalendarRemoteForUser = (user, start, end) => { + const uri = `/api/1.0/calendar/proxy/calendar/by-user/${user.id}/events`; + const dateFrom = datetimeToISO(start); + const dateTo = datetimeToISO(end); + + return fetchResults(uri, {dateFrom, dateTo}); +} + export { whoami, fetchCalendarRangeForUser, + fetchCalendarRemoteForUser, }; diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/actions.js b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/actions.js index 222066018..2ec3aad76 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/actions.js +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/actions.js @@ -4,7 +4,10 @@ import { removeIdFromValue, mapEntity } from './utils'; -import {fetchCalendarRangeForUser} from './../api'; +import { + fetchCalendarRangeForUser, + fetchCalendarRemoteForUser, +} from './../api'; export default { setCurrentDatesView({ commit, dispatch }, {start, end}) { @@ -21,8 +24,16 @@ export default { for (const uid of state.currentView.users.keys()) { console.log('fetchCalendarEventsFor', uid); promises.push( - dispatch('fetchCalendarRangeForUser', - {user: state.usersData.get(uid).user, start: state.currentView.start, end: state.currentView.end}) + dispatch( + 'fetchCalendarRangeForUser', + {user: state.usersData.get(uid).user, start: state.currentView.start, end: state.currentView.end} + ) + ); + promises.push( + dispatch( + 'fetchCalendarRemotesForUser', + {user: state.usersData.get(uid).user, start: state.currentView.start, end: state.currentView.end} + ) ); } @@ -37,6 +48,15 @@ export default { }); } }, + fetchCalendarRemotesForUser({ commit, getters }, { user, start, end }) { + if (!getters.isCalendarRemoteLoadedForUser({user, start, end})) { + return fetchCalendarRemoteForUser(user, start, end).then((remotes) => { + commit('addCalendarRemotesForUser', {user, remotes, start, end}); + + return Promise.resolve(); + }); + } + }, addPersonsInvolved({ commit, dispatch }, payload) { console.log('### action addPersonsInvolved', payload.result.type); switch (payload.result.type) { @@ -51,7 +71,7 @@ export default { case 'user': let aUsers = document.getElementById("chill_activitybundle_activity_users"); aUsers.value = addIdToValue(aUsers.value, payload.result.id); - commit('showUserOnCalendar', {user: payload.result, ranges: false, remote: true}); + commit('showUserOnCalendar', {user: payload.result, ranges: false, remotes: true}); dispatch('fetchCalendarEvents'); break; }; diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/getters.js b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/getters.js index 31d62edb7..e3ce3a61c 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/getters.js +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/getters.js @@ -47,7 +47,7 @@ export default { if (kinds.ranges && userData.calendarRanges.length > 0) { console.log('adding ranges for user', userId); const s = { - 'id': `ranges_${userId}`, + id: `ranges_${userId}`, events: userData.calendarRanges, color: userData.mainColor, backgroundColor: 'white', @@ -61,6 +61,22 @@ export default { } else { console.log('not adding ranges for user', userId); } + + if (kinds.remotes && userData.remotes.length > 0) { + console.log('adding remotes for user', userId); + const s = { + 'id': `remote_${userId}`, + events: userData.remotes, + color: userData.mainColor, + textColor: 'black', + editable: false, + }; + + console.log('remote source', s); + sources.push(s); + } else { + console.log('not adding remotes for user', userId); + } } console.log('eventSources', sources); @@ -112,6 +128,27 @@ export default { return false; }, + /** + * return true if there was a fetch query for event between this date (start and end), + * those date are included. + * + * @param state + * @param getters + * @returns {(function({user: *, start: *, end: *}): (boolean))|*} + */ + isCalendarRemoteLoadedForUser: (state, getters) => ({user, start, end}) => { + if (!getters.hasUserData(user)) { + return false; + } + + for (let interval of getters.getUserData(user).remotesLoaded) { + if (start >= interval.start && end <= interval.end) { + return true; + } + } + + return false; + }, /** * return true if the user ranges are shown on calendar * @@ -142,7 +179,7 @@ export default { return false; } - return k.remote; + return k.remotes; }, diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/index.js b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/index.js index 07ef33867..01322be0f 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/index.js +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/index.js @@ -54,7 +54,7 @@ whoami().then(me => { }); if (null !== store.getters.getMainUser) { - store.commit('showUserOnCalendar', {ranges: true, remote: true, user: store.getters.getMainUser}); + store.commit('showUserOnCalendar', {ranges: true, remotes: true, user: store.getters.getMainUser}); } export default store; diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/mutations.js b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/mutations.js index eb4c7fc00..364393fc4 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/mutations.js +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/mutations.js @@ -1,4 +1,8 @@ -import {createUserData, calendarRangeToFullCalendarEvent} from './utils'; +import { + createUserData, + calendarRangeToFullCalendarEvent, + remoteToFullCalendarEvent, +} from './utils'; export default { setWhoAmiI(state, me) { @@ -8,7 +12,7 @@ export default { state.currentView.start = start; state.currentView.end = end; }, - showUserOnCalendar(state, {user, ranges, remote}) { + showUserOnCalendar(state, {user, ranges, remotes}) { if (!state.usersData.has(user.id)) { state.usersData.set(user.id, createUserData(user, state.usersData.size)); } @@ -19,7 +23,7 @@ export default { user.id, { ranges: typeof ranges !== 'undefined' ? ranges : cur.ranges, - remote: typeof remote !== 'undefined' ? remote : cur.remote, + remotes: typeof remotes !== 'undefined' ? remotes : cur.remotes, } ); }, @@ -85,6 +89,27 @@ export default { userData.calendarRanges = userData.calendarRanges.concat(eventRanges); userData.calendarRangesLoaded.push({start, end}); }, + addCalendarRemotesForUser(state, {user, remotes, start, end}) { + let userData; + if (state.usersData.has(user.id)) { + userData = state.usersData.get(user.id); + } else { + userData = createUserData(user, state.usersData.size); + state.usersData.set(user.id, userData); + } + + const eventRemotes = remotes + .filter(r => !state.existingEvents.has(`remote_${r.id}`)) + .map(r => { + // add to existing ids + state.existingEvents.add(`remote_${r.id}`); + return r; + }) + .map(r => remoteToFullCalendarEvent(r)); + + userData.remotes = userData.remotes.concat(eventRemotes); + userData.remotesLoaded.push({start, end}); + }, /* // Calendar setEvents(state, payload) { diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/utils.js b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/utils.js index f3301a3c0..da6d14ace 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/utils.js +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/utils.js @@ -29,6 +29,8 @@ const createUserData = (user, colorIndex) => { user: user, calendarRanges: [], calendarRangesLoaded: [], + remotes: [], + remotesLoaded: [], mainColor: COLORS[colorId], } } @@ -44,10 +46,21 @@ const calendarRangeToFullCalendarEvent = (entity) => { }; } +const remoteToFullCalendarEvent = (entity) => { + return { + id: `range_${entity.id}`, + title: entity.title, + start: entity.startDate.datetime8601, + end: entity.endDate.datetime8601, + allDay: false, + }; +} + export { addIdToValue, calendarRangeToFullCalendarEvent, removeIdFromValue, + remoteToFullCalendarEvent, mapEntity, createUserData, };