Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize calendar performance and fix bugs #93

Merged
merged 18 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 64 additions & 56 deletions calendar/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@
var grist;

// to keep all calendar related logic;
let calendarHandler;
const CALENDAR_NAME = 'standardCalendar';
var calendarHandler;

var CALENDAR_NAME = 'standardCalendar';

const urlParams = new URLSearchParams(window.location.search);
const isReadOnly = urlParams.get('readonly') === 'true' ||
(urlParams.has('access') && urlParams.get('access') !== 'full');

//for tests
let dataVersion = Date.now();
function testGetDataVersion(){
return dataVersion;
}
// for tests
var dataVersion = Date.now();
georgegevoian marked this conversation as resolved.
Show resolved Hide resolved

//registering code to run when a document is ready
function ready(fn) {
Expand Down Expand Up @@ -168,7 +166,6 @@ class CalendarHandler {
container.classList.add('readonly')
}
const options = this._getCalendarOptions();
this.previousIds = new Set();
this.calendar = new tui.Calendar(container, options);

// Not sure how to get a reference to this constructor, so doing it in a roundabout way.
Expand Down Expand Up @@ -223,6 +220,12 @@ class CalendarHandler {
container.querySelector('button.toastui-calendar-popup-confirm')?.click();
}
});

// All events, indexed by id.
this._allEvents = new Map();

// Ids of visible events that fall within the current date range. */
this._visibleEventIds = new Set();
}

_isMultidayInMonthViewEvent(rec) {
Expand All @@ -239,13 +242,14 @@ class CalendarHandler {
if (!isRecordValid(record) || this._selectedRecordId === record.id) {
return;
}

if (this._selectedRecordId) {
this._colorCalendarEvent(this._selectedRecordId, this._mainColor);
}
this._selectedRecordId = record.id;
const [startType] = await colTypesFetcher.getColTypes();
const startDate = getAdjustedDate(record.startDate, startType);
this.calendar.setDate(startDate);
this._selectedRecordId = record.id;
updateUIAfterNavigation();

// If the view has a vertical timeline, scroll to the start of the event.
Expand All @@ -270,8 +274,13 @@ class CalendarHandler {

_colorCalendarEvent(eventId, color) {
const event = this.calendar.getEvent(eventId, CALENDAR_NAME);
if (!event) { return; }

const shouldPaintBackground = this._isMultidayInMonthViewEvent(event);
this.calendar.updateEvent(eventId, CALENDAR_NAME, {borderColor: color, backgroundColor: shouldPaintBackground?color:this._mainColor});
this.calendar.updateEvent(eventId, CALENDAR_NAME, {
borderColor: color,
backgroundColor: shouldPaintBackground ? color : this._mainColor,
});
}

// change calendar perspective between week, month and day.
Expand Down Expand Up @@ -304,31 +313,49 @@ class CalendarHandler {
}
}

// update calendar events based on the collection of records from the grist table.
async updateCalendarEvents(calendarEvents) {
// we need to keep track the ids of the events that are currently in the calendar to compare it
// with the new set of events when they come.
const currentIds = new Set();
for (const record of calendarEvents) {
// check if an event already exists in the calendar - update it if so, create new otherwise
const event = this.calendar.getEvent(record.id, CALENDAR_NAME);
const eventData = record;
if (!event) {
this.calendar.createEvents([eventData]);
getEvents() {
return this._allEvents;
}

setEvents(events) {
this._allEvents = events;
}

/**
* Adds/updates events that fall within the current date range, and removes
* events that do not.
*/
renderVisibleEvents() {
const newVisibleEventIds = new Set();
const dateRangeStart = this.calendar.getDateRangeStart();
const dateRangeEnd = this.calendar.getDateRangeEnd().setHours(23, 99, 99, 999);

// Add or update events that are now visible.
for (const event of this._allEvents.values()) {
const isEventInRange = (
(event.start >= dateRangeStart && event.start <= dateRangeEnd) ||
(event.end >= dateRangeStart && event.end <= dateRangeEnd) ||
(event.start < dateRangeStart && event.end > dateRangeEnd)
);
if (!isEventInRange) { continue; }

const calendarEvent = this.calendar.getEvent(event.id, CALENDAR_NAME);
if (!calendarEvent) {
this.calendar.createEvents([event]);
paulfitz marked this conversation as resolved.
Show resolved Hide resolved
} else {
this.calendar.updateEvent(record.id, CALENDAR_NAME, eventData);
this.calendar.updateEvent(event.id, CALENDAR_NAME, event);
}
currentIds.add(record.id);
newVisibleEventIds.add(event.id);
}
// if some events are not in the new set of events, we need to remove them from the calendar
if (this.previousIds) {
for (const id of this.previousIds) {
if (!currentIds.has(id)) {
this.calendar.deleteEvent(id, CALENDAR_NAME);
}

// Remove events that are no longer visible.
for (const eventId of this._visibleEventIds) {
if (!newVisibleEventIds.has(eventId)) {
this.calendar.deleteEvent(eventId, CALENDAR_NAME);
}
}
this.previousIds = currentIds;

this._visibleEventIds = newVisibleEventIds;
}

setTheme(gristThemeConfiguration) {
Expand Down Expand Up @@ -386,6 +413,7 @@ function getGristOptions() {


function updateUIAfterNavigation(){
calendarHandler.renderVisibleEvents();
// update name of the month and year displayed on the top of the widget
document.getElementById('calendar-title').innerText = getMonthName();
// refresh colors of selected event (in month view it's different from in other views)
Expand Down Expand Up @@ -521,11 +549,11 @@ function selectRadioButton(value) {
for (const element of document.getElementsByName('calendar-options')) {
if (element.value === value) {
element.checked = true;
element.parentElement.classList.add('active')
element.parentElement.classList.add('active');
}
else{
element.checked = false;
element.parentElement.classList.remove('active')
element.parentElement.classList.remove('active');
}
}
}
Expand All @@ -549,7 +577,7 @@ function buildCalendarEventObject(record, colTypes) {
let [startType, endType] = colTypes;
endType = endType || startType;
start = getAdjustedDate(start, startType);
end = end ? getAdjustedDate(end, endType) : start
end = end ? getAdjustedDate(end, endType) : start;

// Normalize records with invalid start/end times so that they're visible
// in the calendar.
Expand Down Expand Up @@ -582,8 +610,9 @@ async function updateCalendar(records, mappings) {
// if any records were successfully mapped, create or update them in the calendar
if (mappedRecords) {
const colTypes = await colTypesFetcher.getColTypes();
const CalendarEventObjects = mappedRecords.filter(isRecordValid).map(r => buildCalendarEventObject(r, colTypes));
await calendarHandler.updateCalendarEvents(CalendarEventObjects);
const events = mappedRecords.filter(isRecordValid).map(r => buildCalendarEventObject(r, colTypes));
calendarHandler.setEvents(new Map(events.map(event => ([event.id, event]))));
calendarHandler.renderVisibleEvents();
}
dataVersion = Date.now();
}
Expand Down Expand Up @@ -645,24 +674,3 @@ class ColTypesFetcher {
}

const colTypesFetcher = new ColTypesFetcher();

function testGetCalendarEvent(eventId) {
const calendarObject = calendarHandler.calendar.getEvent(eventId, CALENDAR_NAME);
if (calendarObject) {
const eventData = {
title: calendarObject?.title,
startDate: calendarObject?.start.d.d,
endDate: calendarObject?.end.d.d,
isAllDay: calendarObject?.isAllday ?? false,
selected: calendarObject?.borderColor === calendarHandler._selectedColor
};
return JSON.stringify(eventData);
} else {
return null;
}
}

function testGetCalendarViewName(){
// noinspection JSUnresolvedReference
return calendarHandler.calendar.getViewName();
}
Loading