Update layout
This commit is contained in:
@@ -36,9 +36,11 @@ function addDays(dateStr, n) {
|
||||
function localParts(date, tz) {
|
||||
// ponytail: no TZID means "floating" time per RFC 5545 - interpret in the
|
||||
// system's local zone (matches how node-ical encodes floating DATE values),
|
||||
// not UTC.
|
||||
// not UTC. A real tz (incl. "Etc/UTC" for Z-suffixed timestamps) is an
|
||||
// absolute instant - always display it in Amsterdam, not whatever zone
|
||||
// the source event happened to carry.
|
||||
const fmt = new Intl.DateTimeFormat('en-CA', {
|
||||
...(tz ? { timeZone: tz } : {}),
|
||||
...(tz ? { timeZone: 'Europe/Amsterdam' } : {}),
|
||||
year: 'numeric', month: '2-digit', day: '2-digit',
|
||||
hour: '2-digit', minute: '2-digit', hour12: false,
|
||||
});
|
||||
@@ -58,9 +60,9 @@ function formatHour(hour) {
|
||||
}
|
||||
|
||||
function segmentsForDay(dayStr, startLocal, lastDayStr, endLocal, isFullDay) {
|
||||
if (isFullDay) return [0, 1, 2];
|
||||
const dayStartHour = dayStr === startLocal.dateStr ? startLocal.hour : 0;
|
||||
const dayEndHour = dayStr === lastDayStr ? endLocal.hour : 24;
|
||||
const dayStartHour = isFullDay ? 0 : (dayStr === startLocal.dateStr ? startLocal.hour : 0);
|
||||
const dayEndHour = isFullDay ? 24 : (dayStr === lastDayStr ? endLocal.hour : 24);
|
||||
if (isFullDay) return { lit: [0, 1, 2], start: dayStartHour, end: dayEndHour };
|
||||
const lit = [];
|
||||
SEGMENT_RANGES.forEach(([segStart, segEnd], i) => {
|
||||
if (dayStartHour < segEnd && dayEndHour > segStart) lit.push(i);
|
||||
@@ -69,7 +71,17 @@ function segmentsForDay(dayStr, startLocal, lastDayStr, endLocal, isFullDay) {
|
||||
const idx = SEGMENT_RANGES.findIndex(([, segEnd]) => dayStartHour < segEnd);
|
||||
lit.push(idx === -1 ? 2 : idx);
|
||||
}
|
||||
return lit;
|
||||
return { lit, start: dayStartHour, end: dayEndHour };
|
||||
}
|
||||
|
||||
// two tagged events sharing a day overlap if their local-hour spans intersect
|
||||
function hasOverlap(intervals) {
|
||||
for (let i = 0; i < intervals.length; i++) {
|
||||
for (let j = i + 1; j < intervals.length; j++) {
|
||||
if (intervals[i].start < intervals[j].end && intervals[j].start < intervals[i].end) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function buildOverview(icsText, tagsConfig, rangeStartStr, rangeEndStr) {
|
||||
@@ -79,7 +91,7 @@ function buildOverview(icsText, tagsConfig, rangeStartStr, rangeEndStr) {
|
||||
const days = {};
|
||||
|
||||
const getDay = (dateStr) => {
|
||||
if (!days[dateStr]) days[dateStr] = { segments: [null, null, null], events: [] };
|
||||
if (!days[dateStr]) days[dateStr] = { segments: [null, null, null], events: [], taggedIntervals: [] };
|
||||
return days[dateStr];
|
||||
};
|
||||
|
||||
@@ -99,6 +111,8 @@ function buildOverview(icsText, tagsConfig, rangeStartStr, rangeEndStr) {
|
||||
const summary = inst.summary || '(untitled)';
|
||||
const colors = leadingEmojis(summary).map((e) => (tagsConfig[e] ? tagsConfig[e].color : UNKNOWN_EMOJI_COLOR));
|
||||
const color = colors.length === 2 ? colors : colors[0] || null;
|
||||
// "nc" as a standalone word anywhere in the title marks a conflict as resolved
|
||||
const noConflict = /\bnc\b/i.test(summary);
|
||||
|
||||
const startLocal = localParts(inst.start, inst.start.tz);
|
||||
const endInstant = isFullDay
|
||||
@@ -111,11 +125,12 @@ function buildOverview(icsText, tagsConfig, rangeStartStr, rangeEndStr) {
|
||||
while (true) {
|
||||
if (dayStr >= rangeStartStr && dayStr <= rangeEndStr) {
|
||||
const day = getDay(dayStr);
|
||||
const lit = segmentsForDay(dayStr, startLocal, lastDayStr, endLocal, isFullDay);
|
||||
const { lit, start, end } = segmentsForDay(dayStr, startLocal, lastDayStr, endLocal, isFullDay);
|
||||
for (const segIdx of lit) {
|
||||
if (color) day.segments[segIdx] = color;
|
||||
else if (!day.segments[segIdx]) day.segments[segIdx] = 'muted';
|
||||
}
|
||||
if (color && !noConflict) day.taggedIntervals.push({ start, end });
|
||||
day.events.push({
|
||||
hour: dayStr === startLocal.dateStr ? startLocal.hour : 0,
|
||||
isFullDay,
|
||||
@@ -133,7 +148,7 @@ function buildOverview(icsText, tagsConfig, rangeStartStr, rangeEndStr) {
|
||||
const lines = day.events
|
||||
.sort((a, b) => a.hour - b.hour)
|
||||
.map((e) => `${e.isFullDay ? 'All day' : formatHour(e.hour)} ${e.text}`);
|
||||
result[dateStr] = { segments: day.segments, tooltip: lines.join('\n') };
|
||||
result[dateStr] = { segments: day.segments, tooltip: lines.join('\n'), conflict: hasOverlap(day.taggedIntervals) };
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user