diff --git a/apps/calendar/README.md b/apps/calendar/README.md index 6cf369563..5f90d0d52 100644 --- a/apps/calendar/README.md +++ b/apps/calendar/README.md @@ -10,7 +10,7 @@ Basic calendar - Swipe down (Bangle.js 2 only) to go to the next year - Touch to display events for current month - Press the button (button 3 on Bangle.js 1) to exit -- Holidays have same color as weekends and can be edited with the 'Download'-interface +- Holidays have same color as weekends and can be edited with the 'Download'-interface, e.g. by uploading an iCalendar file. ## Settings diff --git a/apps/calendar/calendar.js b/apps/calendar/calendar.js index 92e3428ba..7977b0196 100644 --- a/apps/calendar/calendar.js +++ b/apps/calendar/calendar.js @@ -38,12 +38,15 @@ const events = (require("Storage").readJSON("sched.json",1) || []).filter(a => a date.setSeconds(time.s); return {date: date, msg: a.msg, type: "e"}; }); -// add holidays -(require("Storage").readJSON("calendar.holiday.json",1) || []).forEach(h => { - const date = new Date(h.date); - events.push({date: date, msg: h.name, type: "h"}); +// add holidays & other events +(require("Storage").readJSON("calendar.days.json",1) || []).forEach(d => { + const date = new Date(d.date); + const o = {date: date, msg: d.name, type: d.type}; + if (d.repeat) { + o.repeat = d.repeat; + } + events.push(o); }); -events.sort((a,b) => a.date - b.date); if (settings.ndColors === undefined) { settings.ndColors = !g.theme.dark; @@ -158,8 +161,12 @@ function drawCalendar(date) { weekBeforeMonth.setDate(weekBeforeMonth.getDate() - 7); const week2AfterMonth = new Date(date.getFullYear(), date.getMonth()+1, 0); week2AfterMonth.setDate(week2AfterMonth.getDate() + 14); + events.forEach(ev => { + if (ev.repeat === "y") { + ev.date.setFullYear(date.getFullYear()); + } + }); const eventsThisMonth = events.filter(ev => ev.date > weekBeforeMonth && ev.date < week2AfterMonth); - let i = 0; for (y = 0; y < rowN - 1; y++) { for (x = 0; x < colN; x++) { @@ -177,15 +184,21 @@ function drawCalendar(date) { // Display events for this day eventsThisMonth.forEach((ev, idx) => { if (sameDay(ev.date, curDay)) { - if (ev.type === "e") { // alarm/event - const hour = ev.date.getHours() + ev.date.getMinutes()/60.0; - const slice = hour/24*(eventsPerDay-1); // slice 0 for 0:00 up to eventsPerDay for 23:59 - const height = (y2-2) - (y1+2); // height of a cell - const sliceHeight = height/eventsPerDay; - const ystart = (y1+2) + slice*sliceHeight; - g.setColor(bgEvent).fillRect(x1+1, ystart, x2-2, ystart+sliceHeight); - } else if (ev.type === "h") { // holiday - g.setColor(bgColorWeekend).fillRect(x1+1, y1+1, x2-1, y2-1); + switch(ev.type) { + case "e": // alarm/event + const hour = ev.date.getHours() + ev.date.getMinutes()/60.0; + const slice = hour/24*(eventsPerDay-1); // slice 0 for 0:00 up to eventsPerDay for 23:59 + const height = (y2-2) - (y1+2); // height of a cell + const sliceHeight = height/eventsPerDay; + const ystart = (y1+2) + slice*sliceHeight; + g.setColor(bgEvent).fillRect(x1+1, ystart, x2-2, ystart+sliceHeight); + break; + case "h": // holiday + g.setColor(bgColorWeekend).fillRect(x1+1, y1+1, x2-1, y2-1); + break; + case "o": // other + g.setColor("#88ff00").fillRect(x1+1, y1+1, x2-1, y2-1); + break; } eventsThisMonth.splice(idx, 1); // this event is no longer needed @@ -242,6 +255,7 @@ function setUI() { }, btn: (n) => n === (process.env.HWVERSION === 2 ? 1 : 3) && load(), touch: (n,e) => { + events.sort((a,b) => a.date - b.date); const menu = events.filter(ev => ev.date.getFullYear() === date.getFullYear() && ev.date.getMonth() === date.getMonth()).map(e => { const dateStr = require("locale").date(e.date, 1); const timeStr = require("locale").time(e.date, 1); diff --git a/apps/calendar/interface.html b/apps/calendar/interface.html index 068c71514..509a6bebd 100644 --- a/apps/calendar/interface.html +++ b/apps/calendar/interface.html @@ -53,6 +53,7 @@ function eventToHoliday(event) { const holiday = { date: formatDate(date), name: event.summary, + type: 'h', }; return holiday; } @@ -63,7 +64,7 @@ function formatDate(d) { function upload() { Util.showModal("Saving..."); - Util.writeStorage("calendar.holiday.json", JSON.stringify(holidays), () => { + Util.writeStorage("calendar.days.json", JSON.stringify(holidays), () => { location.reload(); // reload so we see current data }); } @@ -82,7 +83,7 @@ function renderHoliday(holiday) { inputTime.value = formatDate(localDate) inputTime.onchange = (e => { const date = new Date(inputTime.value); - holiday.date = formatDate(date.toISOString()) + holiday.date = formatDate(date); }); tdTime.appendChild(inputTime); @@ -101,13 +102,50 @@ function renderHoliday(holiday) { tdSummary.appendChild(inputSummary); inputSummary.onchange(); - const tdInfo = document.createElement('td'); - tr.appendChild(tdInfo); + const tdType = document.createElement('td'); + tr.appendChild(tdType); + const selectType = document.createElement("select"); + selectType.classList.add('form-select'); + tdType.prepend(selectType); + const optionHoliday = document.createElement("option"); + optionHoliday.text = "Holiday"; + optionHoliday.value = "h"; + optionHoliday.selected = holiday.type === "h"; + selectType.add(optionHoliday); + const optionOther = document.createElement("option"); + optionOther.text = "Other"; + optionOther.value = "o"; + optionOther.selected = holiday.type === "o"; + selectType.add(optionOther); + selectType.onchange = (e => { + holiday.type = e.target.value; + }); + + const tdRepeat = document.createElement('td'); + tr.appendChild(tdRepeat); + const selectRepeat = document.createElement("select"); + selectRepeat.classList.add('form-select'); + tdRepeat.prepend(selectRepeat); + const optionNever = document.createElement("option"); + optionNever.text = "Never"; + optionNever.selected = !holiday.repeat; + selectRepeat.add(optionNever); + const optionYearly = document.createElement("option"); + optionYearly.text = "Yearly"; + optionYearly.value = "y"; + optionYearly.selected = holiday.repeat === "y"; + selectRepeat.add(optionYearly); + selectRepeat.onchange = (e => { + holiday.repeat = e.target.value; + }); + + const tdAction = document.createElement('td'); + tr.appendChild(tdAction); const buttonDelete = document.createElement('button'); buttonDelete.classList.add('btn'); buttonDelete.classList.add('btn-action'); - tdInfo.prepend(buttonDelete); + tdAction.prepend(buttonDelete); const iconDelete = document.createElement('i'); iconDelete.classList.add('icon'); iconDelete.classList.add('icon-delete'); @@ -129,22 +167,21 @@ function addHoliday() { } function getData() { + Util.showModal("Loading..."); Puck.write(`\x10(function() { - Bluetooth.print(JSON.stringify(require("Storage").list("calendar.holiday.json").sort())); + Bluetooth.print(JSON.stringify(require("Storage").list("calendar.days.json").sort())); })()\n`, contents => { const fileNames = JSON.parse(contents); if (fileNames.length > 0) { - Util.showModal("Loading..."); - Util.readStorage('calendar.holiday.json',data=>{ + Util.readStorage('calendar.days.json',data=>{ holidays = JSON.parse(data || "[]") || []; Util.hideModal(); - holidays.forEach(holiday => { - renderHoliday(holiday); - }); + render(); }); } else { holidays = []; + Util.hideModal(); } }); } @@ -156,7 +193,7 @@ function onInit() { -

Holidays

+

Holidays

- +
+ + diff --git a/apps/calendar/metadata.json b/apps/calendar/metadata.json index b7e40a1fd..44a68d879 100644 --- a/apps/calendar/metadata.json +++ b/apps/calendar/metadata.json @@ -15,5 +15,5 @@ {"name":"calendar.settings.js","url":"settings.js"}, {"name":"calendar.img","url":"calendar-icon.js","evaluate":true} ], - "data": [{"name":"calendar.json"}, {"name":"calendar.holiday.json"}] + "data": [{"name":"calendar.json"}, {"name":"calendar.days.json"}] }
Date HolidayTypeRepeat