{"version":3,"file":"calendar-view-c2c72c86.js","sources":["../../../src/calendar/view/CalendarEventBubble.ts","../../../src/calendar/view/ContinuingCalendarEventBubble.ts","../../../src/calendar/view/EventDragHandler.ts","../../../src/calendar/view/CalendarGuiUtils.ts","../../../src/calendar/view/CalendarMonthView.ts","../../../src/calendar/view/CalendarAgendaView.ts","../../../src/calendar/view/EditCalendarDialog.ts","../../../src/calendar/view/CalendarDayEventsView.ts","../../../src/calendar/view/MultiDayCalendarView.ts","../../../src/calendar/view/eventeditor/AttendeeListEditor.ts","../../../src/calendar/view/eventeditor/EventTimeEditor.ts","../../../src/calendar/view/eventeditor/RepeatRuleEditor.ts","../../../src/calendar/view/eventeditor/CalendarEventEditView.ts","../../../src/calendar/view/eventeditor/CalendarEventEditDialog.ts","../../../src/calendar/view/eventpopup/EventPreviewView.ts","../../../src/calendar/view/eventpopup/CalendarEventPopup.ts","../../../src/calendar/view/eventpopup/CalendarEventPopupViewModel.ts","../../../src/calendar/view/CalendarMobileHeader.ts","../../../src/calendar/view/CalendarDesktopToolbar.ts","../../../src/calendar/view/CalendarView.ts","../../../src/calendar/view/CalendarViewModel.ts"],"sourcesContent":["import m, { Child, Children, Component, Vnode } from \"mithril\"\nimport { colorForBg } from \"../date/CalendarUtils\"\nimport { px, size } from \"../../gui/size\"\nimport { Icon } from \"../../gui/base/Icon\"\nimport { Icons } from \"../../gui/base/icons/Icons\"\nimport type { clickHandler } from \"../../gui/base/GuiUtils\"\n\nexport type CalendarEventBubbleAttrs = {\n\ttext: string\n\tsecondLineText?: string | null\n\tcolor: string\n\thasAlarm: boolean\n\tclick: clickHandler\n\theight?: number\n\tnoBorderRight?: boolean\n\tnoBorderLeft?: boolean\n\tverticalPadding?: number\n\tfadeIn: boolean\n\topacity: number\n\tenablePointerEvents: boolean\n}\nconst lineHeight = size.calendar_line_height\nconst lineHeightPx = px(lineHeight)\n\nexport class CalendarEventBubble implements Component<CalendarEventBubbleAttrs> {\n\t_hasFinishedInitialRender: boolean = false\n\n\toncreate(vnode: Vnode<CalendarEventBubbleAttrs>) {\n\t\tthis._hasFinishedInitialRender = true\n\t}\n\n\tview({ attrs }: Vnode<CalendarEventBubbleAttrs>): Children {\n\t\t// This helps us stop flickering in certain cases where we want to disable and re-enable fade in (ie. when dragging events)\n\t\t// Reapplying the animation to the element will cause it to trigger instantly, so we don't want to do that\n\t\tconst doFadeIn = !this._hasFinishedInitialRender && attrs.fadeIn\n\t\tlet enablePointerEvents = attrs.enablePointerEvents\n\t\treturn m(\n\t\t\t\".calendar-event.small.overflow-hidden.flex.cursor-pointer\" +\n\t\t\t\t(doFadeIn ? \".fade-in\" : \"\") +\n\t\t\t\t(attrs.noBorderLeft ? \".event-continues-left\" : \"\") +\n\t\t\t\t(attrs.noBorderRight ? \".event-continues-right\" : \"\"),\n\t\t\t{\n\t\t\t\tstyle: {\n\t\t\t\t\tbackground: \"#\" + attrs.color,\n\t\t\t\t\tcolor: colorForBg(\"#\" + attrs.color),\n\t\t\t\t\tminHeight: lineHeightPx,\n\t\t\t\t\theight: px(attrs.height ? Math.max(attrs.height, 0) : lineHeight),\n\t\t\t\t\t\"padding-top\": px(attrs.verticalPadding || 0),\n\t\t\t\t\topacity: attrs.opacity,\n\t\t\t\t\tpointerEvents: enablePointerEvents ? \"auto\" : \"none\",\n\t\t\t\t},\n\t\t\t\tonclick: (e: MouseEvent) => {\n\t\t\t\t\te.stopPropagation()\n\t\t\t\t\tattrs.click(e, e.target as HTMLElement)\n\t\t\t\t},\n\t\t\t},\n\t\t\t[\n\t\t\t\tattrs.hasAlarm\n\t\t\t\t\t? m(Icon, {\n\t\t\t\t\t\t\ticon: Icons.Notifications,\n\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\tfill: colorForBg(\"#\" + attrs.color),\n\t\t\t\t\t\t\t\t\"padding-top\": \"2px\",\n\t\t\t\t\t\t\t\t\"padding-right\": \"2px\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tclass: \"icon-small\",\n\t\t\t\t\t  })\n\t\t\t\t\t: null,\n\t\t\t\tm(\n\t\t\t\t\t\".flex.col\",\n\t\t\t\t\t{\n\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t// Limit the width to trigger ellipsis\n\t\t\t\t\t\t\twidth: \"95%\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tthis.renderContent(attrs),\n\t\t\t\t),\n\t\t\t],\n\t\t)\n\t}\n\n\trenderContent({ height: maybeHeight, text, secondLineText, color }: CalendarEventBubbleAttrs): Children {\n\t\t// If the bubble has 2 or more lines worth of vertical space, then we will render the text + the secondLineText on separate lines\n\t\t// Otherwise we will combine them onto a single line\n\t\tconst height = maybeHeight ?? lineHeight\n\t\tconst isMultiline = height >= lineHeight * 2\n\n\t\tif (isMultiline) {\n\t\t\t// How many lines of text that will fit in the bubble\n\t\t\t// we dont want any cut in half lines in case the bubble cannot fit a whole number of lines\n\t\t\tconst linesInBubble = Math.floor(height / lineHeight)\n\t\t\t// leave space for the second text line. it will be restricted to a maximum of one line in height\n\t\t\tconst topSectionMaxLines = secondLineText != null ? linesInBubble - 1 : linesInBubble\n\t\t\tconst topSectionClass = topSectionMaxLines === 1 ? \".text-ellipsis\" : \".text-overflow\"\n\t\t\treturn [\n\t\t\t\tthis.renderTextSection(topSectionClass, text, topSectionMaxLines * lineHeight),\n\t\t\t\tsecondLineText ? this.renderTextSection(\".text-ellipsis\", secondLineText, lineHeight) : null,\n\t\t\t]\n\t\t} else {\n\t\t\treturn this.renderTextSection(\n\t\t\t\t\".text-ellipsis\",\n\t\t\t\tsecondLineText\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t`${text} `,\n\t\t\t\t\t\t\tm(Icon, {\n\t\t\t\t\t\t\t\ticon: Icons.Time,\n\t\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\t\tfill: colorForBg(\"#\" + color),\n\t\t\t\t\t\t\t\t\t\"padding-top\": \"2px\",\n\t\t\t\t\t\t\t\t\t\"padding-right\": \"2px\",\n\t\t\t\t\t\t\t\t\t\"vertical-align\": \"text-top\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tclass: \"icon-small\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t`${secondLineText}`,\n\t\t\t\t\t  ]\n\t\t\t\t\t: text,\n\t\t\t\tlineHeight,\n\t\t\t)\n\t\t}\n\t}\n\n\trenderTextSection(classes: string, text: Children, maxHeight: number): Child {\n\t\treturn m(\n\t\t\tclasses,\n\t\t\t{\n\t\t\t\tstyle: {\n\t\t\t\t\tlineHeight: lineHeightPx,\n\t\t\t\t\tmaxHeight: px(maxHeight),\n\t\t\t\t},\n\t\t\t},\n\t\t\ttext,\n\t\t)\n\t}\n}\n","import m, { Children, Component, Vnode } from \"mithril\"\nimport { formatEventTime, hasAlarmsForTheUser } from \"../date/CalendarUtils\"\nimport { CalendarEventBubble } from \"./CalendarEventBubble\"\nimport type { CalendarEvent } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport type { User } from \"../../api/entities/sys/TypeRefs.js\"\nimport type { EventTextTimeOption } from \"../../api/common/TutanotaConstants\"\nimport type { CalendarEventBubbleClickHandler } from \"./CalendarViewModel\"\n\ntype ContinuingCalendarEventBubbleAttrs = {\n\tevent: CalendarEvent\n\tstartsBefore: boolean\n\tendsAfter: boolean\n\tcolor: string\n\tonEventClicked: CalendarEventBubbleClickHandler\n\tshowTime: EventTextTimeOption | null\n\tuser: User\n\tfadeIn: boolean\n\topacity: number\n\tenablePointerEvents: boolean\n}\n\nexport class ContinuingCalendarEventBubble implements Component<ContinuingCalendarEventBubbleAttrs> {\n\tview({ attrs }: Vnode<ContinuingCalendarEventBubbleAttrs>): Children {\n\t\treturn m(\".flex.calendar-event-container.darker-hover\", [\n\t\t\tattrs.startsBefore\n\t\t\t\t? m(\".event-continues-right-arrow\", {\n\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\"border-left-color\": \"transparent\",\n\t\t\t\t\t\t\t\"border-top-color\": \"#\" + attrs.color,\n\t\t\t\t\t\t\t\"border-bottom-color\": \"#\" + attrs.color,\n\t\t\t\t\t\t\topacity: attrs.opacity,\n\t\t\t\t\t\t},\n\t\t\t\t  })\n\t\t\t\t: null,\n\t\t\tm(\n\t\t\t\t\".flex-grow.overflow-hidden\",\n\t\t\t\tm(CalendarEventBubble, {\n\t\t\t\t\ttext: (attrs.showTime != null ? formatEventTime(attrs.event, attrs.showTime) + \" \" : \"\") + attrs.event.summary,\n\t\t\t\t\tcolor: attrs.color,\n\t\t\t\t\tclick: (e) => attrs.onEventClicked(attrs.event, e),\n\t\t\t\t\tnoBorderLeft: attrs.startsBefore,\n\t\t\t\t\tnoBorderRight: attrs.endsAfter,\n\t\t\t\t\thasAlarm: hasAlarmsForTheUser(attrs.user, attrs.event),\n\t\t\t\t\tfadeIn: attrs.fadeIn,\n\t\t\t\t\topacity: attrs.opacity,\n\t\t\t\t\tenablePointerEvents: attrs.enablePointerEvents,\n\t\t\t\t}),\n\t\t\t),\n\t\t\tattrs.endsAfter\n\t\t\t\t? m(\".event-continues-right-arrow\", {\n\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\"border-left-color\": \"#\" + attrs.color,\n\t\t\t\t\t\t\topacity: attrs.opacity,\n\t\t\t\t\t\t},\n\t\t\t\t  })\n\t\t\t\t: null,\n\t\t])\n\t}\n}\n","import type { CalendarEvent } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport m from \"mithril\"\nimport { getAllDayDateUTC, isAllDayEvent } from \"../../api/common/utils/CommonCalendarUtils\"\nimport { Time } from \"../date/Time.js\"\n\nconst DRAG_THRESHOLD = 10\nexport type MousePos = {\n\tx: number\n\ty: number\n}\n// Convenience wrapper for nullability\ntype DragData = {\n\toriginalEvent: CalendarEvent\n\toriginalDateUnderMouse: Date\n\toriginalMousePos: MousePos\n\tkeepTime: boolean // Indicates whether the time on the original event should be kept or modified. In case this is set to true the drag operation just shifts event start by whole days.\n}\n\nexport interface EventDragHandlerCallbacks {\n\treadonly onDragStart: (calendarEvent: CalendarEvent, timeToMoveBy: number) => void\n\treadonly onDragUpdate: (timeToMoveBy: number) => void\n\treadonly onDragEnd: (timeToMoveBy: number) => Promise<void>\n}\n\n/**\n * Handles logic for dragging events in the calendar child views.\n */\nexport class EventDragHandler {\n\t_data: DragData | null = null\n\t_isDragging: boolean = false\n\t_lastDiffBetweenDates: number | null = null\n\t_hasChanged: boolean = false\n\t_draggingArea: HTMLBodyElement\n\t_eventDragCallbacks: EventDragHandlerCallbacks\n\n\tconstructor(draggingArea: HTMLBodyElement, callbacks: EventDragHandlerCallbacks) {\n\t\tthis._draggingArea = draggingArea\n\t\tthis._eventDragCallbacks = callbacks\n\t}\n\n\tget isDragging(): boolean {\n\t\treturn this._isDragging\n\t}\n\n\tget originalEvent(): CalendarEvent | null {\n\t\treturn this._data?.originalEvent ?? null\n\t}\n\n\t/**\n\t * Check if the handler has changed since the last time you called this function\n\t */\n\tqueryHasChanged(): boolean {\n\t\tconst isChanged = this._hasChanged\n\t\tthis._hasChanged = false\n\t\treturn isChanged\n\t}\n\n\t/**\n\t * Call on mouse down, to initialize an upcoming drag event.\n\t * Doesn't start the drag yet, because we want to wait until the mouse has moved beyond some threshhold\n\t * @param calendarEvent The calendar event for which a drag operation is prepared\n\t * @param dateUnderMouse The original date under mouse when preparing the drag.\n\t * @param keepTime Indicates whether the time on the original event should be kept or modified. In case this is set to true the drag\n\t * operation just shifts event start by whole days otherwise the time from dateUnderMouse should be used as new time for the event.\n\t */\n\tprepareDrag(calendarEvent: CalendarEvent, dateUnderMouse: Date, mousePos: MousePos, keepTime: boolean) {\n\t\tthis._draggingArea.classList.add(\"cursor-grabbing\")\n\n\t\tthis._data = {\n\t\t\toriginalEvent: calendarEvent,\n\t\t\t// We always differentiate between eventStart and originalDateUnderMouse to be able to shift it relative to the mouse position\n\t\t\t// and not the start date. This is important for larger events in day/week view\n\t\t\toriginalDateUnderMouse: this.adjustDateUnderMouse(calendarEvent.startTime, dateUnderMouse, keepTime),\n\t\t\toriginalMousePos: mousePos,\n\t\t\tkeepTime: keepTime,\n\t\t}\n\t\tthis._hasChanged = false\n\t\tthis._isDragging = false\n\t}\n\n\t/**\n\t * Call on mouse move.\n\t * Will be a no-op if the prepareDrag hasn't been called or if cancelDrag has been called since the last prepareDrag call\n\t * The dragging doesn't actually begin until the distance between the mouse and it's original location is greater than some threshold\n\t * @param dateUnderMouse The current date under the mouse courser, may include a time.\n\t */\n\thandleDrag(dateUnderMouse: Date, mousePos: MousePos) {\n\t\tif (this._data) {\n\t\t\tconst dragData = this._data\n\t\t\tconst adjustedDateUnderMouse = this.adjustDateUnderMouse(dragData.originalEvent.startTime, dateUnderMouse, dragData.keepTime)\n\t\t\t// Calculate the distance from the original mouse location to the current mouse location\n\t\t\t// We don't want to actually start the drag until the mouse has moved by some distance\n\t\t\t// So as to avoid accidentally dragging when you meant to click but moved the mouse a little\n\t\t\tconst distanceX = dragData.originalMousePos.x - mousePos.x\n\t\t\tconst distanceY = dragData.originalMousePos.y - mousePos.y\n\t\t\tconst distance = Math.sqrt(distanceX ** 2 + distanceY ** 2)\n\n\t\t\tif (this._isDragging) {\n\t\t\t\tconst diffBetweenDates = this.getDayUnderMouseDiff(dragData, adjustedDateUnderMouse)\n\n\t\t\t\t// We don't want to trigger a redraw everytime the drag call is triggered, only when necessary\n\t\t\t\tif (diffBetweenDates !== this._lastDiffBetweenDates) {\n\t\t\t\t\tthis._lastDiffBetweenDates = diffBetweenDates\n\n\t\t\t\t\tthis._eventDragCallbacks.onDragUpdate(diffBetweenDates)\n\n\t\t\t\t\tthis._hasChanged = true\n\t\t\t\t\tm.redraw()\n\t\t\t\t}\n\t\t\t} else if (distance > DRAG_THRESHOLD) {\n\t\t\t\tthis._isDragging = true\n\t\t\t\tthis._lastDiffBetweenDates = this.getDayUnderMouseDiff(dragData, adjustedDateUnderMouse)\n\n\t\t\t\tthis._eventDragCallbacks.onDragStart(dragData.originalEvent, this._lastDiffBetweenDates)\n\n\t\t\t\tthis._hasChanged = true\n\t\t\t\tm.redraw()\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Call on mouseup or mouseleave. Ends a drag event if one has been started, and hasn't been cancelled.\n\t *\n\t * This function will only trigger when prepareDrag has been called\n\t */\n\tasync endDrag(dateUnderMouse: Date): Promise<void> {\n\t\tthis._draggingArea.classList.remove(\"cursor-grabbing\")\n\n\t\tif (this._isDragging && this._data) {\n\t\t\tconst dragData = this._data\n\t\t\tconst adjustedDateUnderMouse = this.adjustDateUnderMouse(dragData.originalEvent.startTime, dateUnderMouse, dragData.keepTime)\n\t\t\t// We update our state first because the updateCallback might take some time, and\n\t\t\t// we want the UI to be able to react to the drop having happened before we get the result\n\t\t\tthis._isDragging = false\n\t\t\tthis._data = null\n\t\t\tconst diffBetweenDates = this.getDayUnderMouseDiff(dragData, adjustedDateUnderMouse)\n\n\t\t\t// If the date hasn't changed we still have to do the callback so the view model can cancel the drag\n\t\t\ttry {\n\t\t\t\tawait this._eventDragCallbacks.onDragEnd(diffBetweenDates)\n\t\t\t} finally {\n\t\t\t\tthis._hasChanged = true\n\t\t\t\tm.redraw()\n\t\t\t}\n\t\t} else {\n\t\t\tthis.cancelDrag()\n\t\t}\n\t}\n\n\tadjustDateUnderMouse(eventStart: Date, dateUnderMouse: Date, keepTime: boolean): Date {\n\t\tif (keepTime) {\n\t\t\treturn Time.fromDate(eventStart).toDate(dateUnderMouse)\n\t\t} else {\n\t\t\treturn dateUnderMouse\n\t\t}\n\t}\n\n\tgetDayUnderMouseDiff(dragData: DragData, adjustedDateUnderMouse: Date): number {\n\t\tconst { originalEvent, originalDateUnderMouse } = dragData\n\t\treturn isAllDayEvent(originalEvent)\n\t\t\t? getAllDayDateUTC(adjustedDateUnderMouse).getTime() - getAllDayDateUTC(originalDateUnderMouse).getTime()\n\t\t\t: adjustedDateUnderMouse.getTime() - originalDateUnderMouse.getTime()\n\t}\n\n\tcancelDrag() {\n\t\tthis._data = null\n\t\tthis._isDragging = false\n\t\tthis._hasChanged = true\n\t\tthis._lastDiffBetweenDates = null\n\t}\n}\n","import m, { Child } from \"mithril\"\nimport type { TranslationKey } from \"../../misc/LanguageViewModel\"\nimport { lang } from \"../../misc/LanguageViewModel\"\nimport { ButtonType } from \"../../gui/base/Button.js\"\nimport { Icons } from \"../../gui/base/icons/Icons\"\nimport { Dialog } from \"../../gui/base/Dialog\"\nimport type { MousePosAndBounds } from \"../../gui/base/GuiUtils\"\nimport { Time } from \"../date/Time.js\"\nimport { assert, clamp, incrementDate, lastThrow } from \"@tutao/tutanota-utils\"\nimport { IconButton } from \"../../gui/base/IconButton.js\"\nimport { formatDateWithWeekday, formatMonthWithFullYear } from \"../../misc/Formatter.js\"\nimport { getStartOfTheWeekOffset, getStartOfWeek, getWeekNumber } from \"../date/CalendarUtils.js\"\nimport { WeekStart } from \"../../api/common/TutanotaConstants.js\"\n\nexport function renderCalendarSwitchLeftButton(label: TranslationKey, click: () => unknown): Child {\n\treturn m(IconButton, {\n\t\ttitle: label,\n\t\ticon: Icons.ArrowBackward,\n\t\tclick,\n\t})\n}\n\nexport function renderCalendarSwitchRightButton(label: TranslationKey, click: () => unknown): Child {\n\treturn m(IconButton, {\n\t\ttitle: label,\n\t\ticon: Icons.ArrowForward,\n\t\tclick,\n\t})\n}\n\nfunction weekTitle(date: Date, weekStart: WeekStart): string {\n\tconst startOfTheWeekOffset = getStartOfTheWeekOffset(weekStart)\n\tconst firstDate = getStartOfWeek(date, startOfTheWeekOffset)\n\tconst lastDate = incrementDate(new Date(firstDate), 6)\n\n\tif (firstDate.getMonth() !== lastDate.getMonth()) {\n\t\tif (firstDate.getFullYear() !== lastDate.getFullYear()) {\n\t\t\treturn `${lang.formats.monthLong.format(firstDate)} ${lang.formats.yearNumeric.format(firstDate)} -\n\t\t\t\t\t\t\t\t\t\t\t${lang.formats.monthLong.format(lastDate)} ${lang.formats.yearNumeric.format(lastDate)}`\n\t\t}\n\t\treturn `${lang.formats.monthLong.format(firstDate)} - ${lang.formats.monthLong.format(lastDate)} ${lang.formats.yearNumeric.format(firstDate)}`\n\t} else {\n\t\treturn `${lang.formats.monthLong.format(firstDate)} ${lang.formats.yearNumeric.format(firstDate)}`\n\t}\n}\n\nexport function getNextFourteenDays(startOfToday: Date): Array<Date> {\n\tlet calculationDate = new Date(startOfToday)\n\tconst days: Date[] = []\n\n\tfor (let i = 0; i < 14; i++) {\n\t\tdays.push(new Date(calculationDate.getTime()))\n\t\tcalculationDate = incrementDate(calculationDate, 1)\n\t}\n\n\treturn days\n}\n\nfunction agendaTitle(date: Date): string {\n\tconst days = getNextFourteenDays(date)\n\tconst lastDay = lastThrow(days)\n\tconst dateRangeText =\n\t\tdays[0].getFullYear() === lastDay.getFullYear()\n\t\t\t? `${lang.formats.dateWithWeekday.format(days[0])} - ${lang.formats.dateWithWeekdayAndYear.format(lastDay)}`\n\t\t\t: `${lang.formats.dateWithWeekdayAndYear.format(days[0])} - ${lang.formats.dateWithWeekdayAndYear.format(lastDay)}`\n\treturn `${lang.get(\"agenda_label\")} ${dateRangeText}`\n}\n\nexport type CalendarNavConfiguration = { back: Child; title: string; forward: Child; week: string | null }\n\nfunction calendarWeek(date: Date, weekStart: WeekStart) {\n\t// According to ISO 8601, weeks always start on Monday. Week numbering systems for\n\t// weeks that do not start on Monday are not strictly defined, so we only display\n\t// a week number if the user's client is configured to start weeks on Monday\n\tif (weekStart !== WeekStart.MONDAY) {\n\t\treturn null\n\t}\n\n\treturn lang.get(\"weekNumber_label\", {\n\t\t\"{week}\": String(getWeekNumber(date)),\n\t})\n}\n\nexport enum CalendarViewType {\n\tDAY = \"day\",\n\tWEEK = \"week\",\n\tMONTH = \"month\",\n\tAGENDA = \"agenda\",\n}\n\nexport function calendarNavConfiguration(\n\tviewType: CalendarViewType,\n\tdate: Date,\n\tweekStart: WeekStart,\n\tswitcher: (viewType: CalendarViewType, next: boolean) => unknown,\n): CalendarNavConfiguration {\n\tconst onBack = () => switcher(viewType, false)\n\tconst onForward = () => switcher(viewType, true)\n\tswitch (viewType) {\n\t\tcase CalendarViewType.DAY:\n\t\t\treturn {\n\t\t\t\tback: renderCalendarSwitchLeftButton(\"prevDay_label\", onBack),\n\t\t\t\tforward: renderCalendarSwitchRightButton(\"nextDay_label\", onForward),\n\t\t\t\ttitle: formatDateWithWeekday(date),\n\t\t\t\tweek: calendarWeek(date, weekStart),\n\t\t\t}\n\t\tcase CalendarViewType.MONTH:\n\t\t\treturn {\n\t\t\t\tback: renderCalendarSwitchLeftButton(\"prevMonth_label\", onBack),\n\t\t\t\tforward: renderCalendarSwitchRightButton(\"nextMonth_label\", onForward),\n\t\t\t\ttitle: formatMonthWithFullYear(date),\n\t\t\t\tweek: null,\n\t\t\t}\n\t\tcase CalendarViewType.WEEK:\n\t\t\treturn {\n\t\t\t\tback: renderCalendarSwitchLeftButton(\"prevWeek_label\", onBack),\n\t\t\t\tforward: renderCalendarSwitchRightButton(\"nextWeek_label\", onForward),\n\t\t\t\ttitle: weekTitle(date, weekStart),\n\t\t\t\tweek: calendarWeek(date, weekStart),\n\t\t\t}\n\t\tcase CalendarViewType.AGENDA:\n\t\t\treturn {\n\t\t\t\tback: null,\n\t\t\t\tforward: null,\n\t\t\t\ttitle: agendaTitle(date),\n\t\t\t\tweek: null,\n\t\t\t}\n\t}\n}\n\nexport function askIfShouldSendCalendarUpdatesToAttendees(): Promise<\"yes\" | \"no\" | \"cancel\"> {\n\treturn new Promise((resolve) => {\n\t\tlet alertDialog: Dialog\n\t\tconst cancelButton = {\n\t\t\tlabel: \"cancel_action\",\n\t\t\tclick: () => {\n\t\t\t\tresolve(\"cancel\")\n\t\t\t\talertDialog.close()\n\t\t\t},\n\t\t\ttype: ButtonType.Secondary,\n\t\t} as const\n\t\tconst noButton = {\n\t\t\tlabel: \"no_label\",\n\t\t\tclick: () => {\n\t\t\t\tresolve(\"no\")\n\t\t\t\talertDialog.close()\n\t\t\t},\n\t\t\ttype: ButtonType.Secondary,\n\t\t} as const\n\t\tconst yesButton = {\n\t\t\tlabel: \"yes_label\",\n\t\t\tclick: () => {\n\t\t\t\tresolve(\"yes\")\n\t\t\t\talertDialog.close()\n\t\t\t},\n\t\t\ttype: ButtonType.Primary,\n\t\t} as const\n\n\t\tconst onclose = (positive: boolean) => (positive ? resolve(\"yes\") : resolve(\"cancel\"))\n\n\t\talertDialog = Dialog.confirmMultiple(\"sendUpdates_msg\", [cancelButton, noButton, yesButton], onclose)\n\t})\n}\n\n/**\n * Map the location of a mouse click on an element to a give date, given a list of weeks\n * there should be neither zero weeks, nor zero length weeks\n */\nexport function getDateFromMousePos({ x, y, targetWidth, targetHeight }: MousePosAndBounds, weeks: Array<Array<Date>>): Date {\n\tassert(weeks.length > 0, \"Weeks must not be zero length\")\n\tconst unitHeight = targetHeight / weeks.length\n\tconst currentSquareY = Math.floor(y / unitHeight)\n\tconst week = weeks[clamp(currentSquareY, 0, weeks.length - 1)]\n\tassert(week.length > 0, \"Week must not be zero length\")\n\tconst unitWidth = targetWidth / week.length\n\tconst currentSquareX = Math.floor(x / unitWidth)\n\treturn week[clamp(currentSquareX, 0, week.length - 1)]\n}\n\n/**\n * Map the vertical position of a mouse click on an element to a time of day\n * @param y\n * @param targetHeight\n * @param hourDivision: how many times to divide the hour\n */\nexport function getTimeFromMousePos({ y, targetHeight }: MousePosAndBounds, hourDivision: number): Time {\n\tconst sectionHeight = targetHeight / 24\n\tconst hour = y / sectionHeight\n\tconst hourRounded = Math.floor(hour)\n\tconst minutesInc = 60 / hourDivision\n\tconst minute = Math.floor((hour - hourRounded) * hourDivision) * minutesInc\n\treturn new Time(hourRounded, minute)\n}\n\nexport const SELECTED_DATE_INDICATOR_THICKNESS = 4\n","import m, { Children, ClassComponent, Component, Vnode, VnodeDOM } from \"mithril\"\nimport { px, size } from \"../../gui/size\"\nimport { EventTextTimeOption, WeekStart } from \"../../api/common/TutanotaConstants\"\nimport type { CalendarDay, CalendarMonth } from \"../date/CalendarUtils\"\nimport {\n\tCALENDAR_EVENT_HEIGHT,\n\tEventLayoutMode,\n\tgetAllDayDateForTimezone,\n\tgetCalendarMonth,\n\tgetDateIndicator,\n\tgetDiffIn24hIntervals,\n\tgetEventColor,\n\tgetEventEnd,\n\tgetFirstDayOfMonth,\n\tgetStartOfDayWithZone,\n\tgetStartOfNextDayWithZone,\n\tgetStartOfTheWeekOffset,\n\tgetTimeZone,\n\tgetWeekNumber,\n\tlayOutEvents,\n\tTEMPORARY_EVENT_OPACITY,\n} from \"../date/CalendarUtils\"\nimport { flat, incrementDate, incrementMonth, isSameDay, lastThrow, neverNull, ofClass } from \"@tutao/tutanota-utils\"\nimport { ContinuingCalendarEventBubble } from \"./ContinuingCalendarEventBubble\"\nimport { styles } from \"../../gui/styles\"\nimport { formatMonthWithFullYear } from \"../../misc/Formatter\"\nimport { isAllDayEvent, isAllDayEventByTimes } from \"../../api/common/utils/CommonCalendarUtils\"\nimport { windowFacade } from \"../../misc/WindowFacade\"\nimport { PageView } from \"../../gui/base/PageView\"\nimport type { CalendarEvent } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport type { GroupColors } from \"./CalendarView\"\nimport type { EventDragHandlerCallbacks, MousePos } from \"./EventDragHandler\"\nimport { EventDragHandler } from \"./EventDragHandler\"\nimport { getPosAndBoundsFromMouseEvent } from \"../../gui/base/GuiUtils\"\nimport { UserError } from \"../../api/main/UserError\"\nimport { showUserError } from \"../../misc/ErrorHandlerImpl\"\nimport { theme } from \"../../gui/theme\"\nimport {\n\tCalendarViewType,\n\tgetDateFromMousePos,\n\trenderCalendarSwitchLeftButton,\n\trenderCalendarSwitchRightButton,\n\tSELECTED_DATE_INDICATOR_THICKNESS,\n} from \"./CalendarGuiUtils\"\nimport type { CalendarEventBubbleClickHandler, EventsOnDays } from \"./CalendarViewModel\"\nimport { Time } from \"../date/Time.js\"\nimport { client } from \"../../misc/ClientDetector\"\nimport { locator } from \"../../api/main/MainLocator.js\"\n\ntype CalendarMonthAttrs = {\n\tselectedDate: Date\n\tonDateSelected: (date: Date, calendarViewTypeToShow: CalendarViewType) => unknown\n\teventsForDays: Map<number, Array<CalendarEvent>>\n\tgetEventsOnDays: (range: Array<Date>) => EventsOnDays\n\tonNewEvent: (date: Date | null) => unknown\n\tonEventClicked: CalendarEventBubbleClickHandler\n\tonChangeMonth: (next: boolean) => unknown\n\tamPmFormat: boolean\n\tstartOfTheWeek: WeekStart\n\tgroupColors: GroupColors\n\thiddenCalendars: ReadonlySet<Id>\n\ttemporaryEvents: Array<CalendarEvent>\n\tdragHandlerCallbacks: EventDragHandlerCallbacks\n}\ntype SimplePosRect = {\n\ttop: number\n\tleft: number\n\tright: number\n}\n\n/** height of the day number indicator at the top of the day square */\nconst dayHeight = () => (styles.isDesktopLayout() ? 32 : 24)\n\nconst spaceBetweenEvents = () => (styles.isDesktopLayout() ? 2 : 1)\n\nconst EVENT_BUBBLE_VERTICAL_OFFSET = 5\n\nexport class CalendarMonthView implements Component<CalendarMonthAttrs>, ClassComponent<CalendarMonthAttrs> {\n\tprivate _monthDom: HTMLElement | null = null\n\tprivate _resizeListener: () => unknown\n\tprivate _zone: string\n\tprivate _lastWidth: number\n\tprivate _lastHeight: number\n\tprivate _eventDragHandler: EventDragHandler\n\tprivate _dayUnderMouse: Date | null = null\n\tprivate _lastMousePos: MousePos | null = null\n\n\tconstructor({ attrs }: Vnode<CalendarMonthAttrs>) {\n\t\tthis._resizeListener = m.redraw\n\t\tthis._zone = getTimeZone()\n\t\tthis._lastWidth = 0\n\t\tthis._lastHeight = 0\n\t\tthis._eventDragHandler = new EventDragHandler(neverNull(document.body as HTMLBodyElement), attrs.dragHandlerCallbacks)\n\t}\n\n\toncreate() {\n\t\twindowFacade.addResizeListener(this._resizeListener)\n\t}\n\n\tonremove() {\n\t\twindowFacade.removeResizeListener(this._resizeListener)\n\t}\n\n\tview({ attrs }: Vnode<CalendarMonthAttrs>): Children {\n\t\tconst startOfTheWeekOffset = getStartOfTheWeekOffset(attrs.startOfTheWeek)\n\t\tconst thisMonth = getCalendarMonth(attrs.selectedDate, startOfTheWeekOffset, false)\n\t\tconst previousMonthDate = new Date(attrs.selectedDate)\n\t\tpreviousMonthDate.setMonth(previousMonthDate.getMonth() - 1)\n\t\tconst previousMonth = getCalendarMonth(previousMonthDate, startOfTheWeekOffset, false)\n\t\tconst nextMonthDate = new Date(attrs.selectedDate)\n\t\tnextMonthDate.setMonth(nextMonthDate.getMonth() + 1)\n\t\tconst nextMonth = getCalendarMonth(nextMonthDate, startOfTheWeekOffset, false)\n\t\tlet lastMontDate = incrementMonth(attrs.selectedDate, -1)\n\t\tlet nextMontDate = incrementMonth(attrs.selectedDate, 1)\n\t\treturn m(PageView, {\n\t\t\tpreviousPage: {\n\t\t\t\tkey: getFirstDayOfMonth(lastMontDate).getTime(),\n\t\t\t\tnodes: this._monthDom ? this._renderCalendar(attrs, previousMonth, thisMonth, lastMontDate, this._zone) : null,\n\t\t\t},\n\t\t\tcurrentPage: {\n\t\t\t\tkey: getFirstDayOfMonth(attrs.selectedDate).getTime(),\n\t\t\t\tnodes: this._renderCalendar(attrs, thisMonth, thisMonth, attrs.selectedDate, this._zone),\n\t\t\t},\n\t\t\tnextPage: {\n\t\t\t\tkey: getFirstDayOfMonth(nextMontDate).getTime(),\n\t\t\t\tnodes: this._monthDom ? this._renderCalendar(attrs, nextMonth, thisMonth, nextMontDate, this._zone) : null,\n\t\t\t},\n\t\t\tonChangePage: (next) => attrs.onChangeMonth(next),\n\t\t})\n\t}\n\n\tonbeforeupdate(newVnode: Vnode<CalendarMonthAttrs>, oldVnode: VnodeDOM<CalendarMonthAttrs>): boolean {\n\t\tconst dom = this._monthDom\n\t\tconst different =\n\t\t\t!dom ||\n\t\t\toldVnode.attrs.eventsForDays !== newVnode.attrs.eventsForDays ||\n\t\t\toldVnode.attrs.selectedDate !== newVnode.attrs.selectedDate ||\n\t\t\toldVnode.attrs.amPmFormat !== newVnode.attrs.amPmFormat ||\n\t\t\toldVnode.attrs.groupColors !== newVnode.attrs.groupColors ||\n\t\t\toldVnode.attrs.hiddenCalendars !== newVnode.attrs.hiddenCalendars ||\n\t\t\tdom.offsetWidth !== this._lastWidth ||\n\t\t\tdom.offsetHeight !== this._lastHeight\n\n\t\tif (dom) {\n\t\t\tthis._lastWidth = dom.offsetWidth\n\t\t\tthis._lastHeight = dom.offsetHeight\n\t\t}\n\n\t\treturn different || this._eventDragHandler.queryHasChanged()\n\t}\n\n\t_renderCalendar(attrs: CalendarMonthAttrs, month: CalendarMonth, currentlyVisibleMonth: CalendarMonth, date: Date, zone: string): Children {\n\t\tconst { weekdays, weeks } = month\n\t\tconst firstDay = getFirstDayOfMonth(date)\n\t\tconst today = getStartOfDayWithZone(new Date(), getTimeZone())\n\t\treturn m(\".fill-absolute.flex.col.mlr-safe-inset.content-bg\", [\n\t\t\tstyles.isDesktopLayout() ? null : m(\".pt-s\"),\n\t\t\tm(\n\t\t\t\t\".flex.mb-s\",\n\t\t\t\tweekdays.map((wd) => m(\".flex-grow\", m(\".calendar-day-indicator.b\", wd))),\n\t\t\t),\n\t\t\tm(\n\t\t\t\t\".flex.col.flex-grow\",\n\t\t\t\t{\n\t\t\t\t\toncreate: (vnode) => {\n\t\t\t\t\t\tif (month === currentlyVisibleMonth) {\n\t\t\t\t\t\t\tthis._monthDom = vnode.dom as HTMLElement\n\t\t\t\t\t\t\tm.redraw()\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tonupdate: (vnode) => {\n\t\t\t\t\t\tif (month === currentlyVisibleMonth) {\n\t\t\t\t\t\t\tthis._monthDom = vnode.dom as HTMLElement\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tonmousemove: (mouseEvent: MouseEvent & { redraw?: boolean }) => {\n\t\t\t\t\t\tmouseEvent.redraw = false\n\t\t\t\t\t\tconst posAndBoundsFromMouseEvent = getPosAndBoundsFromMouseEvent(mouseEvent)\n\t\t\t\t\t\tthis._lastMousePos = posAndBoundsFromMouseEvent\n\t\t\t\t\t\tthis._dayUnderMouse = getDateFromMousePos(\n\t\t\t\t\t\t\tposAndBoundsFromMouseEvent,\n\t\t\t\t\t\t\tweeks.map((week) => week.map((day) => day.date)),\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\tthis._eventDragHandler.handleDrag(this._dayUnderMouse, posAndBoundsFromMouseEvent)\n\t\t\t\t\t},\n\t\t\t\t\tonmouseup: (mouseEvent: MouseEvent & { redraw?: boolean }) => {\n\t\t\t\t\t\tmouseEvent.redraw = false\n\n\t\t\t\t\t\tthis._endDrag()\n\t\t\t\t\t},\n\t\t\t\t\tonmouseleave: (mouseEvent: MouseEvent & { redraw?: boolean }) => {\n\t\t\t\t\t\tmouseEvent.redraw = false\n\n\t\t\t\t\t\tthis._endDrag()\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tweeks.map((week) => {\n\t\t\t\t\treturn m(\n\t\t\t\t\t\t\".flex.flex-grow.rel\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkey: week[0].date.getTime(),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t[week.map((day, i) => this._renderDay(attrs, day, today, i)), this._monthDom ? this._renderWeekEvents(attrs, week, zone) : null],\n\t\t\t\t\t)\n\t\t\t\t}),\n\t\t\t),\n\t\t])\n\t}\n\n\t_endDrag() {\n\t\tconst dayUnderMouse = this._dayUnderMouse\n\t\tconst originalDate = this._eventDragHandler.originalEvent?.startTime\n\n\t\tif (dayUnderMouse && originalDate) {\n\t\t\t//make sure the date we move to also gets a time\n\t\t\tconst dateUnderMouse = Time.fromDate(originalDate).toDate(dayUnderMouse)\n\n\t\t\tthis._eventDragHandler.endDrag(dateUnderMouse).catch(ofClass(UserError, showUserError))\n\t\t}\n\t}\n\n\t_renderDay(attrs: CalendarMonthAttrs, day: CalendarDay, today: Date, weekDayNumber: number): Children {\n\t\tconst { selectedDate } = attrs\n\t\tconst isSelectedDate = isSameDay(selectedDate, day.date)\n\t\treturn m(\n\t\t\t\".calendar-day.calendar-column-border.flex-grow.rel.overflow-hidden.fill-absolute.cursor-pointer\" +\n\t\t\t\t(day.paddingDay ? \".calendar-alternate-background\" : \"\"),\n\t\t\t{\n\t\t\t\tkey: day.date.getTime(),\n\t\t\t\tonclick: (e: MouseEvent) => {\n\t\t\t\t\tif (client.isDesktopDevice()) {\n\t\t\t\t\t\tconst newDate = new Date(day.date)\n\t\t\t\t\t\tlet hour = new Date().getHours()\n\n\t\t\t\t\t\tif (hour < 23) {\n\t\t\t\t\t\t\thour++\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tnewDate.setHours(hour, 0)\n\t\t\t\t\t\tattrs.onDateSelected(new Date(day.date), CalendarViewType.MONTH)\n\t\t\t\t\t\tattrs.onNewEvent(newDate)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tattrs.onDateSelected(new Date(day.date), CalendarViewType.DAY)\n\t\t\t\t\t}\n\n\t\t\t\t\te.preventDefault()\n\t\t\t\t},\n\t\t\t},\n\t\t\t[\n\t\t\t\tm(\".mb-xs\", {\n\t\t\t\t\tstyle: {\n\t\t\t\t\t\theight: px(SELECTED_DATE_INDICATOR_THICKNESS),\n\t\t\t\t\t\tbackground: isSelectedDate ? theme.content_accent : \"none\",\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t\tthis._renderDayHeader(day, today, attrs.onDateSelected), // According to ISO 8601, weeks always start on Monday. Week numbering systems for\n\t\t\t\t// weeks that do not start on Monday are not strictly defined, so we only display\n\t\t\t\t// a week number if the user's client is configured to start weeks on Monday\n\t\t\t\tweekDayNumber === 0 && attrs.startOfTheWeek === WeekStart.MONDAY ? m(\".calendar-month-week-number.abs\", getWeekNumber(day.date)) : null,\n\t\t\t],\n\t\t)\n\t}\n\n\t_renderDayHeader({ date, day }: CalendarDay, today: Date, onDateSelected: (date: Date, calendarViewTypeToShow: CalendarViewType) => unknown): Children {\n\t\treturn m(\".flex-center\", [\n\t\t\tm(\n\t\t\t\t\".calendar-day-indicator.circle\" + getDateIndicator(date, today),\n\t\t\t\t{\n\t\t\t\t\tonclick: (e: MouseEvent) => {\n\t\t\t\t\t\tonDateSelected(new Date(date), CalendarViewType.DAY)\n\t\t\t\t\t\te.stopPropagation()\n\t\t\t\t\t},\n\t\t\t\t\tstyle: {\n\t\t\t\t\t\twidth: px(22),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tString(day),\n\t\t\t),\n\t\t])\n\t}\n\n\t_renderWeekEvents(attrs: CalendarMonthAttrs, week: Array<CalendarDay>, zone: string): Children {\n\t\tconst eventsOnDays = attrs.getEventsOnDays(week.map((day) => day.date))\n\t\tconst events = new Set(eventsOnDays.longEvents.concat(flat(eventsOnDays.shortEvents)))\n\t\tconst firstDayOfWeek = week[0].date\n\t\tconst lastDayOfWeek = lastThrow(week)\n\n\t\tconst dayWidth = this._getWidthForDay()\n\n\t\tconst weekHeight = this._getHeightForWeek()\n\n\t\tconst eventHeight = size.calendar_line_height + spaceBetweenEvents() // height + border\n\n\t\tconst maxEventsPerDay = (weekHeight - dayHeight()) / eventHeight\n\t\tconst eventsPerDay = Math.floor(maxEventsPerDay) - 1 // preserve some space for the more events indicator\n\n\t\tconst moreEventsForDay = [0, 0, 0, 0, 0, 0, 0]\n\t\tconst eventMargin = styles.isDesktopLayout() ? size.calendar_event_margin : size.calendar_event_margin_mobile\n\t\tconst firstDayOfNextWeek = getStartOfNextDayWithZone(lastDayOfWeek.date, zone)\n\t\treturn layOutEvents(\n\t\t\tArray.from(events),\n\t\t\tzone,\n\t\t\t(columns) => {\n\t\t\t\treturn columns\n\t\t\t\t\t.map((events, columnIndex) => {\n\t\t\t\t\t\treturn events.map((event) => {\n\t\t\t\t\t\t\tif (columnIndex < eventsPerDay) {\n\t\t\t\t\t\t\t\tconst eventIsAllDay = isAllDayEventByTimes(event.startTime, event.endTime)\n\t\t\t\t\t\t\t\tconst eventStart = eventIsAllDay ? getAllDayDateForTimezone(event.startTime, zone) : event.startTime\n\t\t\t\t\t\t\t\tconst eventEnd = eventIsAllDay ? incrementDate(getEventEnd(event, zone), -1) : event.endTime\n\n\t\t\t\t\t\t\t\tconst position = this._getEventPosition(\n\t\t\t\t\t\t\t\t\teventStart,\n\t\t\t\t\t\t\t\t\teventEnd,\n\t\t\t\t\t\t\t\t\tfirstDayOfWeek,\n\t\t\t\t\t\t\t\t\tfirstDayOfNextWeek,\n\t\t\t\t\t\t\t\t\tdayWidth,\n\t\t\t\t\t\t\t\t\tdayHeight(),\n\t\t\t\t\t\t\t\t\tcolumnIndex,\n\t\t\t\t\t\t\t\t)\n\n\t\t\t\t\t\t\t\treturn this.renderEvent(event, position, eventStart, firstDayOfWeek, firstDayOfNextWeek, eventEnd, attrs)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tweek.forEach((dayInWeek, index) => {\n\t\t\t\t\t\t\t\t\tconst eventsForDay = attrs.eventsForDays.get(dayInWeek.date.getTime())\n\n\t\t\t\t\t\t\t\t\tif (eventsForDay && eventsForDay.indexOf(event) !== -1) {\n\t\t\t\t\t\t\t\t\t\tmoreEventsForDay[index]++\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\treturn null\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t\t.concat(\n\t\t\t\t\t\tmoreEventsForDay.map((moreEventsCount, weekday) => {\n\t\t\t\t\t\t\tconst day = week[weekday]\n\t\t\t\t\t\t\tconst isPadding = day.paddingDay\n\n\t\t\t\t\t\t\tif (moreEventsCount > 0) {\n\t\t\t\t\t\t\t\treturn m(\n\t\t\t\t\t\t\t\t\t\".abs.small\" + (isPadding ? \".calendar-bubble-more-padding-day\" : \"\"),\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\t\t\t\tbottom: 0,\n\t\t\t\t\t\t\t\t\t\t\theight: px(CALENDAR_EVENT_HEIGHT),\n\t\t\t\t\t\t\t\t\t\t\tleft: px(weekday * dayWidth + eventMargin),\n\t\t\t\t\t\t\t\t\t\t\twidth: px(dayWidth - 2 - eventMargin * 2),\n\t\t\t\t\t\t\t\t\t\t\tpointerEvents: \"none\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\t\t\t\t\t\"font-weight\": \"600\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"+\" + moreEventsCount,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\treturn null\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t},\n\t\t\tEventLayoutMode.DayBasedColumn,\n\t\t)\n\t}\n\n\trenderEvent(\n\t\tevent: CalendarEvent,\n\t\tposition: SimplePosRect,\n\t\teventStart: Date,\n\t\tfirstDayOfWeek: Date,\n\t\tfirstDayOfNextWeek: Date,\n\t\teventEnd: Date,\n\t\tattrs: CalendarMonthAttrs,\n\t): Children {\n\t\tconst isTemporary = attrs.temporaryEvents.includes(event)\n\t\treturn m(\n\t\t\t\".abs.overflow-hidden\",\n\t\t\t{\n\t\t\t\tkey: event._id[0] + event._id[1] + event.startTime.getTime(),\n\t\t\t\tstyle: {\n\t\t\t\t\ttop: px(position.top),\n\t\t\t\t\theight: px(CALENDAR_EVENT_HEIGHT),\n\t\t\t\t\tleft: px(position.left),\n\t\t\t\t\tright: px(position.right),\n\t\t\t\t\tpointerEvents: !styles.isUsingBottomNavigation() ? \"auto\" : \"none\",\n\t\t\t\t},\n\t\t\t\tonmousedown: () => {\n\t\t\t\t\tlet dayUnderMouse = this._dayUnderMouse\n\t\t\t\t\tlet lastMousePos = this._lastMousePos\n\n\t\t\t\t\tif (dayUnderMouse && lastMousePos && !isTemporary) {\n\t\t\t\t\t\tthis._eventDragHandler.prepareDrag(event, dayUnderMouse, lastMousePos, true)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\tm(ContinuingCalendarEventBubble, {\n\t\t\t\tevent: event,\n\t\t\t\tstartsBefore: eventStart < firstDayOfWeek,\n\t\t\t\tendsAfter: firstDayOfNextWeek < eventEnd,\n\t\t\t\tcolor: getEventColor(event, attrs.groupColors),\n\t\t\t\tshowTime: styles.isDesktopLayout() && !isAllDayEvent(event) ? EventTextTimeOption.START_TIME : null,\n\t\t\t\tuser: locator.logins.getUserController().user,\n\t\t\t\tonEventClicked: (e, domEvent) => {\n\t\t\t\t\tattrs.onEventClicked(event, domEvent)\n\t\t\t\t},\n\t\t\t\tfadeIn: !this._eventDragHandler.isDragging,\n\t\t\t\topacity: isTemporary ? TEMPORARY_EVENT_OPACITY : 1,\n\t\t\t\tenablePointerEvents: !this._eventDragHandler.isDragging && !isTemporary && client.isDesktopDevice(),\n\t\t\t}),\n\t\t)\n\t}\n\n\t_getEventPosition(\n\t\teventStart: Date,\n\t\teventEnd: Date,\n\t\tfirstDayOfWeek: Date,\n\t\tfirstDayOfNextWeek: Date,\n\t\tcalendarDayWidth: number,\n\t\tcalendarDayHeight: number,\n\t\tcolumnIndex: number,\n\t): SimplePosRect {\n\t\tconst top = (size.calendar_line_height + spaceBetweenEvents()) * columnIndex + calendarDayHeight + EVENT_BUBBLE_VERTICAL_OFFSET\n\t\tconst dayOfStartDateInWeek = getDiffIn24IntervalsFast(eventStart, firstDayOfWeek)\n\t\tconst dayOfEndDateInWeek = getDiffIn24IntervalsFast(eventEnd, firstDayOfWeek)\n\t\tconst calendarEventMargin = styles.isDesktopLayout() ? size.calendar_event_margin : size.calendar_event_margin_mobile\n\t\tconst left = (eventStart < firstDayOfWeek ? 0 : dayOfStartDateInWeek * calendarDayWidth) + calendarEventMargin\n\t\tconst right = (eventEnd > firstDayOfNextWeek ? 0 : (6 - dayOfEndDateInWeek) * calendarDayWidth) + calendarEventMargin\n\t\treturn {\n\t\t\ttop,\n\t\t\tleft,\n\t\t\tright,\n\t\t}\n\t}\n\n\t_getHeightForWeek(): number {\n\t\tif (!this._monthDom) {\n\t\t\treturn 1\n\t\t}\n\n\t\tconst monthDomHeight = this._monthDom.offsetHeight\n\t\treturn monthDomHeight / 6\n\t}\n\n\t_getWidthForDay(): number {\n\t\tif (!this._monthDom) {\n\t\t\treturn 1\n\t\t}\n\n\t\tconst monthDomWidth = this._monthDom.offsetWidth\n\t\treturn monthDomWidth / 7\n\t}\n}\n\n/**\n * Optimization to not create luxon's DateTime in simple case.\n * May not work if we allow override time zones.\n */\nfunction getDiffIn24IntervalsFast(left: Date, right: Date): number {\n\tif (left.getMonth() === right.getMonth()) {\n\t\treturn left.getDate() - right.getDate()\n\t} else {\n\t\treturn getDiffIn24hIntervals(right, left)\n\t}\n}\n","import m, { Children, Component, Vnode } from \"mithril\"\nimport { CalendarEventBubble } from \"./CalendarEventBubble\"\nimport { incrementDate, lastThrow, neverNull } from \"@tutao/tutanota-utils\"\nimport { lang } from \"../../misc/LanguageViewModel\"\nimport { formatDate, formatDateWithWeekday } from \"../../misc/Formatter\"\nimport {\n\teventStartsBefore,\n\tformatEventTime,\n\tgetEventColor,\n\tgetStartOfDayWithZone,\n\tgetTimeTextFormatForLongEvent,\n\tgetTimeZone,\n\thasAlarmsForTheUser,\n} from \"../date/CalendarUtils\"\nimport { isAllDayEvent } from \"../../api/common/utils/CommonCalendarUtils\"\nimport type { CalendarEvent } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport type { GroupColors } from \"./CalendarView\"\nimport type { CalendarEventBubbleClickHandler } from \"./CalendarViewModel\"\nimport { locator } from \"../../api/main/MainLocator.js\"\nimport { getNextFourteenDays } from \"./CalendarGuiUtils.js\"\n\ntype Attrs = {\n\t/**\n\t * maps start of day timestamp to events on that day\n\t */\n\teventsForDays: Map<number, Array<CalendarEvent>>\n\tamPmFormat: boolean\n\tonEventClicked: CalendarEventBubbleClickHandler\n\tgroupColors: GroupColors\n\thiddenCalendars: ReadonlySet<Id>\n\tonDateSelected: (date: Date) => unknown\n}\n\nexport class CalendarAgendaView implements Component<Attrs> {\n\tview({ attrs }: Vnode<Attrs>): Children {\n\t\tconst now = new Date()\n\t\tconst zone = getTimeZone()\n\t\tconst today = getStartOfDayWithZone(now, zone)\n\t\tconst tomorrow = incrementDate(new Date(today), 1)\n\t\tconst days = getNextFourteenDays(today)\n\t\tconst lastDay = lastThrow(days)\n\n\t\tconst lastDayFormatted = formatDate(lastDay)\n\t\treturn m(\".fill-absolute.flex.col.mlr-safe-inset.content-bg\", [\n\t\t\tm(\".mt-s.pr-l\", []),\n\t\t\tm(\n\t\t\t\t\".scroll.pt-s\",\n\t\t\t\tdays\n\t\t\t\t\t.map((day: Date) => {\n\t\t\t\t\t\tlet events = (attrs.eventsForDays.get(day.getTime()) || []).filter((e) => !attrs.hiddenCalendars.has(neverNull(e._ownerGroup)))\n\n\t\t\t\t\t\tif (day === today) {\n\t\t\t\t\t\t\t// only show future and currently running events\n\t\t\t\t\t\t\tevents = events.filter((ev) => isAllDayEvent(ev) || now < ev.endTime)\n\t\t\t\t\t\t} else if (day.getTime() > tomorrow.getTime() && events.length === 0) {\n\t\t\t\t\t\t\treturn null\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst dateDescription =\n\t\t\t\t\t\t\tday.getTime() === today.getTime()\n\t\t\t\t\t\t\t\t? lang.get(\"today_label\")\n\t\t\t\t\t\t\t\t: day.getTime() === tomorrow.getTime()\n\t\t\t\t\t\t\t\t? lang.get(\"tomorrow_label\")\n\t\t\t\t\t\t\t\t: formatDateWithWeekday(day)\n\t\t\t\t\t\treturn m(\n\t\t\t\t\t\t\t\".flex.mlr-l.calendar-agenda-row.mb-s.col\",\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tkey: day.getTime(),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\t\t\"button.pb-s.b\",\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tonclick: () => attrs.onDateSelected(new Date(day)),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tdateDescription,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\t\t\".flex-grow\",\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\t\t\t\t\"max-width\": \"600px\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tevents.length === 0\n\t\t\t\t\t\t\t\t\t\t? m(\".mb-s\", lang.get(\"noEntries_msg\"))\n\t\t\t\t\t\t\t\t\t\t: events.map((ev) => {\n\t\t\t\t\t\t\t\t\t\t\t\tconst startsBefore = eventStartsBefore(day, zone, ev)\n\t\t\t\t\t\t\t\t\t\t\t\tconst timeFormat = getTimeTextFormatForLongEvent(ev, day, day, zone)\n\t\t\t\t\t\t\t\t\t\t\t\tconst formattedEventTime = timeFormat ? formatEventTime(ev, timeFormat) : \"\"\n\t\t\t\t\t\t\t\t\t\t\t\tconst eventLocation = ev.location ? (formattedEventTime ? \", \" : \"\") + ev.location : \"\"\n\t\t\t\t\t\t\t\t\t\t\t\treturn m(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\".darker-hover.mb-s\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tkey: ev._id.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tm(CalendarEventBubble, {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttext: ev.summary,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsecondLineText: formattedEventTime + eventLocation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolor: getEventColor(ev, attrs.groupColors),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\thasAlarm: !startsBefore && hasAlarmsForTheUser(locator.logins.getUserController().user, ev),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclick: (domEvent) => attrs.onEventClicked(ev, domEvent),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\theight: 38,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tverticalPadding: 2,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tfadeIn: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tenablePointerEvents: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t  }),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t)\n\t\t\t\t\t})\n\t\t\t\t\t.filter(Boolean) // mithril doesn't allow mixing keyed elements with null (for perf reasons it seems)\n\t\t\t\t\t.concat(\n\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\".mlr-l\",\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tkey: \"events_until\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tlang.get(\"showingEventsUntil_msg\", {\n\t\t\t\t\t\t\t\t\"{untilDay}\": lastDayFormatted,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t),\n\t\t])\n\t}\n}\n","import { Dialog } from \"../../gui/base/Dialog\"\nimport m, { Children } from \"mithril\"\nimport stream from \"mithril/stream\"\nimport { TextField } from \"../../gui/base/TextField.js\"\nimport { lang } from \"../../misc/LanguageViewModel\"\nimport type { TranslationKeyType } from \"../../misc/TranslationKey\"\nimport { downcast } from \"@tutao/tutanota-utils\"\n\ntype CalendarProperties = {\n\tname: string\n\tcolor: string\n}\n\nexport function showEditCalendarDialog(\n\t{ name, color }: CalendarProperties,\n\ttitleTextId: TranslationKeyType,\n\tshared: boolean,\n\tokAction: (arg0: Dialog, arg1: CalendarProperties) => unknown,\n\tokTextId: TranslationKeyType,\n\twarningMessage?: () => Children,\n) {\n\tconst nameStream = stream(name)\n\tlet colorPickerDom: HTMLInputElement | null\n\tconst colorStream = stream(\"#\" + color)\n\tDialog.showActionDialog({\n\t\ttitle: () => lang.get(titleTextId),\n\t\tallowOkWithReturn: true,\n\t\tchild: {\n\t\t\tview: () =>\n\t\t\t\tm(\".flex.col\", [\n\t\t\t\t\twarningMessage ? warningMessage() : null,\n\t\t\t\t\tm(TextField, {\n\t\t\t\t\t\tvalue: nameStream(),\n\t\t\t\t\t\toninput: nameStream,\n\t\t\t\t\t\tlabel: \"calendarName_label\",\n\t\t\t\t\t}),\n\t\t\t\t\tm(\".small.mt.mb-xs\", lang.get(\"color_label\")),\n\t\t\t\t\tm(\"input.color-picker\", {\n\t\t\t\t\t\toncreate: ({ dom }) => (colorPickerDom = downcast<HTMLInputElement>(dom)),\n\t\t\t\t\t\ttype: \"color\",\n\t\t\t\t\t\tvalue: colorStream(),\n\t\t\t\t\t\toninput: (inputEvent: InputEvent) => {\n\t\t\t\t\t\t\tconst target = inputEvent.target as HTMLInputElement\n\t\t\t\t\t\t\tcolorStream(target.value)\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t]),\n\t\t},\n\t\tokActionTextId: okTextId,\n\t\tokAction: (dialog: Dialog) => {\n\t\t\tokAction(dialog, {\n\t\t\t\tname: nameStream(),\n\t\t\t\tcolor: colorStream().substring(1),\n\t\t\t})\n\t\t},\n\t})\n}\n","import m, { ChildArray, Children, Component, Vnode } from \"mithril\"\nimport { theme } from \"../../gui/theme\"\nimport { px, size } from \"../../gui/size\"\nimport { DAY_IN_MILLIS, downcast, getEndOfDay, getStartOfDay, mapNullable, neverNull, numberRange } from \"@tutao/tutanota-utils\"\nimport {\n\teventEndsAfterDay,\n\tEventLayoutMode,\n\teventStartsBefore,\n\texpandEvent,\n\tformatEventTime,\n\tgetEventColor,\n\tgetTimeTextFormatForLongEvent,\n\tgetTimeZone,\n\thasAlarmsForTheUser,\n\tlayOutEvents,\n\tTEMPORARY_EVENT_OPACITY,\n} from \"../date/CalendarUtils\"\nimport { CalendarEventBubble } from \"./CalendarEventBubble\"\nimport type { CalendarEvent } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { Time } from \"../date/Time.js\"\nimport { getPosAndBoundsFromMouseEvent } from \"../../gui/base/GuiUtils\"\nimport { getTimeFromMousePos } from \"./CalendarGuiUtils\"\nimport type { CalendarEventBubbleClickHandler } from \"./CalendarViewModel\"\nimport type { GroupColors } from \"./CalendarView\"\nimport { styles } from \"../../gui/styles\"\nimport { locator } from \"../../api/main/MainLocator.js\"\n\nexport type Attrs = {\n\tonEventClicked: CalendarEventBubbleClickHandler\n\tgroupColors: GroupColors\n\tevents: Array<CalendarEvent>\n\tdisplayTimeIndicator: boolean\n\tonTimePressed: (hours: number, minutes: number) => unknown\n\tonTimeContextPressed: (hours: number, minutes: number) => unknown\n\tday: Date\n\tsetCurrentDraggedEvent: (ev: CalendarEvent) => unknown\n\tsetTimeUnderMouse: (time: Time) => unknown\n\tisTemporaryEvent: (event: CalendarEvent) => boolean\n\tisDragging: boolean\n\tfullViewWidth?: number\n}\nexport const calendarDayTimes: Array<Time> = numberRange(0, 23).map((number) => new Time(number, 0))\nconst allHoursHeight = size.calendar_hour_height * calendarDayTimes.length\n\nexport class CalendarDayEventsView implements Component<Attrs> {\n\tprivate _dayDom: HTMLElement | null = null\n\n\tview({ attrs }: Vnode<Attrs>): Children {\n\t\treturn m(\n\t\t\t\".col.rel\",\n\t\t\t{\n\t\t\t\toncreate: (vnode) => {\n\t\t\t\t\tthis._dayDom = vnode.dom as HTMLElement\n\t\t\t\t\tm.redraw()\n\t\t\t\t},\n\t\t\t\tonmousemove: (mouseEvent: MouseEvent) => {\n\t\t\t\t\tdowncast(mouseEvent).redraw = false\n\t\t\t\t\tconst time = getTimeFromMousePos(getPosAndBoundsFromMouseEvent(mouseEvent), 4)\n\t\t\t\t\tattrs.setTimeUnderMouse(time)\n\t\t\t\t},\n\t\t\t},\n\t\t\t[\n\t\t\t\tcalendarDayTimes.map((time) =>\n\t\t\t\t\tm(\".calendar-hour.flex.cursor-pointer\", {\n\t\t\t\t\t\tonclick: (e: MouseEvent) => {\n\t\t\t\t\t\t\te.stopPropagation()\n\t\t\t\t\t\t\tattrs.onTimePressed(time.hour, time.minute)\n\t\t\t\t\t\t},\n\t\t\t\t\t\toncontextmenu: (e: MouseEvent) => {\n\t\t\t\t\t\t\tattrs.onTimeContextPressed(time.hour, time.minute)\n\t\t\t\t\t\t\te.preventDefault()\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t\tthis._dayDom ? this._renderEvents(attrs, attrs.events) : null,\n\t\t\t\tthis._renderTimeIndicator(attrs),\n\t\t\t],\n\t\t)\n\t}\n\n\t_renderTimeIndicator(attrs: Attrs): Children {\n\t\tconst now = new Date()\n\n\t\tif (!attrs.displayTimeIndicator) {\n\t\t\treturn null\n\t\t}\n\n\t\tconst top = getTimeIndicatorPosition(now)\n\t\treturn [\n\t\t\tm(\".abs\", {\n\t\t\t\t\"aria-hidden\": \"true\",\n\t\t\t\tstyle: {\n\t\t\t\t\ttop: px(top),\n\t\t\t\t\tleft: 0,\n\t\t\t\t\tright: 0,\n\t\t\t\t\theight: \"2px\",\n\t\t\t\t\tbackground: theme.content_accent,\n\t\t\t\t},\n\t\t\t}),\n\t\t\tm(\".abs\", {\n\t\t\t\t\"aria-hidden\": \"true\",\n\t\t\t\tstyle: {\n\t\t\t\t\ttop: px(top),\n\t\t\t\t\tleft: 0,\n\t\t\t\t\theight: \"12px\",\n\t\t\t\t\twidth: \"12px\",\n\t\t\t\t\t\"border-radius\": \"50%\",\n\t\t\t\t\tbackground: theme.content_accent,\n\t\t\t\t\t\"margin-top\": \"-5px\",\n\t\t\t\t\t\"margin-left\": \"-7px\",\n\t\t\t\t},\n\t\t\t}),\n\t\t]\n\t}\n\n\t_renderEvents(attrs: Attrs, events: Array<CalendarEvent>): Children {\n\t\treturn layOutEvents(events, getTimeZone(), (columns) => this._renderColumns(attrs, columns), EventLayoutMode.TimeBasedColumn)\n\t}\n\n\t_renderEvent(attrs: Attrs, ev: CalendarEvent, columnIndex: number, columns: Array<Array<CalendarEvent>>, columnWidth: number): Children {\n\t\t// If an event starts in the previous day or ends in the next, we want to clamp top/height to fit within just this day\n\t\tconst zone = getTimeZone()\n\t\tconst startOfEvent = eventStartsBefore(attrs.day, zone, ev) ? getStartOfDay(attrs.day) : ev.startTime\n\t\tconst endOfEvent = eventEndsAfterDay(attrs.day, zone, ev) ? getEndOfDay(attrs.day) : ev.endTime\n\t\tconst startTime = (startOfEvent.getHours() * 60 + startOfEvent.getMinutes()) * 60 * 1000\n\t\tconst height = ((endOfEvent.getTime() - startOfEvent.getTime()) / (1000 * 60 * 60)) * size.calendar_hour_height - size.calendar_event_border\n\t\tconst fullViewWidth = attrs.fullViewWidth\n\t\tconst maxWidth = fullViewWidth != null ? px(styles.isDesktopLayout() ? fullViewWidth / 2 : fullViewWidth) : \"none\"\n\t\tconst colSpan = expandEvent(ev, columnIndex, columns)\n\t\treturn m(\n\t\t\t\".abs.darker-hover\",\n\t\t\t{\n\t\t\t\tstyle: {\n\t\t\t\t\tmaxWidth,\n\t\t\t\t\tleft: px(columnWidth * columnIndex),\n\t\t\t\t\twidth: px(columnWidth * colSpan),\n\t\t\t\t\ttop: px((startTime / DAY_IN_MILLIS) * allHoursHeight),\n\t\t\t\t\theight: px(height),\n\t\t\t\t},\n\t\t\t\tonmousedown: () => {\n\t\t\t\t\tif (!attrs.isTemporaryEvent(ev)) {\n\t\t\t\t\t\tattrs.setCurrentDraggedEvent(ev)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\tm(CalendarEventBubble, {\n\t\t\t\ttext: ev.summary,\n\t\t\t\tsecondLineText: mapNullable(getTimeTextFormatForLongEvent(ev, attrs.day, attrs.day, zone), (option) => formatEventTime(ev, option)),\n\t\t\t\tcolor: getEventColor(ev, attrs.groupColors),\n\t\t\t\tclick: (domEvent) => attrs.onEventClicked(ev, domEvent),\n\t\t\t\theight: height - size.calendar_day_event_padding,\n\t\t\t\thasAlarm: hasAlarmsForTheUser(locator.logins.getUserController().user, ev),\n\t\t\t\tverticalPadding: size.calendar_day_event_padding,\n\t\t\t\tfadeIn: !attrs.isTemporaryEvent(ev),\n\t\t\t\topacity: attrs.isTemporaryEvent(ev) ? TEMPORARY_EVENT_OPACITY : 1,\n\t\t\t\tenablePointerEvents: !attrs.isTemporaryEvent(ev) && !attrs.isDragging,\n\t\t\t}),\n\t\t)\n\t}\n\n\t_renderColumns(attrs: Attrs, columns: Array<Array<CalendarEvent>>): ChildArray {\n\t\tconst columnWidth = neverNull(this._dayDom).clientWidth / columns.length\n\t\treturn columns.map((column, index) => {\n\t\t\treturn column.map((event) => {\n\t\t\t\treturn this._renderEvent(attrs, event, index, columns, Math.floor(columnWidth))\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunction getTimeIndicatorPosition(now: Date): number {\n\tconst passedMillisInDay = (now.getHours() * 60 + now.getMinutes()) * 60 * 1000\n\treturn (passedMillisInDay / DAY_IN_MILLIS) * allHoursHeight\n}\n","import m, { Children, Component, Vnode, VnodeDOM } from \"mithril\"\nimport { getStartOfDay, incrementDate, isSameDay, lastThrow, neverNull, ofClass } from \"@tutao/tutanota-utils\"\nimport { formatTime } from \"../../misc/Formatter\"\nimport {\n\tCALENDAR_EVENT_HEIGHT,\n\tcombineDateWithTime,\n\tDEFAULT_HOUR_OF_DAY,\n\teventEndsAfterDay,\n\tEventLayoutMode,\n\teventStartsBefore,\n\tgetDiffIn24hIntervals,\n\tgetEventColor,\n\tgetEventEnd,\n\tgetEventStart,\n\tgetRangeOfDays,\n\tgetStartOfTheWeekOffset,\n\tgetStartOfWeek,\n\tgetTimeTextFormatForLongEvent,\n\tgetTimeZone,\n\tlayOutEvents,\n\tTEMPORARY_EVENT_OPACITY,\n} from \"../date/CalendarUtils\"\nimport { CalendarDayEventsView, calendarDayTimes } from \"./CalendarDayEventsView\"\nimport { theme } from \"../../gui/theme\"\nimport { px, size } from \"../../gui/size\"\nimport { EventTextTimeOption, WeekStart } from \"../../api/common/TutanotaConstants\"\nimport { lang } from \"../../misc/LanguageViewModel\"\nimport { PageView } from \"../../gui/base/PageView\"\nimport type { CalendarEvent } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport type { GroupColors } from \"./CalendarView\"\nimport type { EventDragHandlerCallbacks, MousePos } from \"./EventDragHandler\"\nimport { EventDragHandler } from \"./EventDragHandler\"\nimport { getPosAndBoundsFromMouseEvent } from \"../../gui/base/GuiUtils\"\nimport { UserError } from \"../../api/main/UserError\"\nimport { showUserError } from \"../../misc/ErrorHandlerImpl\"\nimport { styles } from \"../../gui/styles\"\nimport { CalendarViewType, SELECTED_DATE_INDICATOR_THICKNESS } from \"./CalendarGuiUtils\"\nimport type { CalendarEventBubbleClickHandler, EventsOnDays } from \"./CalendarViewModel\"\nimport { ContinuingCalendarEventBubble } from \"./ContinuingCalendarEventBubble\"\nimport { isAllDayEvent } from \"../../api/common/utils/CommonCalendarUtils\"\nimport { locator } from \"../../api/main/MainLocator.js\"\n\nexport type Attrs = {\n\tselectedDate: Date\n\tdaysInPeriod: number\n\tonDateSelected: (date: Date, calendarViewTypeToShow?: CalendarViewType) => unknown\n\tgetEventsOnDays: (range: Array<Date>) => EventsOnDays\n\tonNewEvent: (date: Date | null) => unknown\n\tonEventClicked: CalendarEventBubbleClickHandler\n\tgroupColors: GroupColors\n\thiddenCalendars: ReadonlySet<Id>\n\tstartOfTheWeek: WeekStart\n\tonChangeViewPeriod: (next: boolean) => unknown\n\ttemporaryEvents: Array<CalendarEvent>\n\tdragHandlerCallbacks: EventDragHandlerCallbacks\n}\n\nexport class MultiDayCalendarView implements Component<Attrs> {\n\tprivate _redrawIntervalId: NodeJS.Timer | null = null\n\tprivate _longEventsDom: HTMLElement | null = null\n\tprivate _domElements: HTMLElement[] = []\n\tprivate _scrollPosition: number\n\tprivate _eventDragHandler: EventDragHandler\n\tprivate _dateUnderMouse: Date | null = null\n\tprivate _viewDom: HTMLElement | null = null\n\tprivate _lastMousePos: MousePos | null = null\n\tprivate _isHeaderEventBeingDragged: boolean = false\n\n\tconstructor({ attrs }: Vnode<Attrs>) {\n\t\tthis._scrollPosition = size.calendar_hour_height * DEFAULT_HOUR_OF_DAY\n\t\tthis._eventDragHandler = new EventDragHandler(neverNull(document.body as HTMLBodyElement), attrs.dragHandlerCallbacks)\n\t}\n\n\toncreate(vnode: VnodeDOM<Attrs>) {\n\t\tthis._viewDom = vnode.dom as HTMLElement\n\t}\n\n\tonupdate(vnode: VnodeDOM<Attrs>) {\n\t\tthis._viewDom = vnode.dom as HTMLElement\n\t}\n\n\tview({ attrs }: Vnode<Attrs>): Children {\n\t\t// Special case for week view\n\t\tconst startOfThisPeriod =\n\t\t\tattrs.daysInPeriod === 7 ? getStartOfWeek(attrs.selectedDate, getStartOfTheWeekOffset(attrs.startOfTheWeek)) : attrs.selectedDate\n\t\tconst startOfPreviousPeriod = incrementDate(new Date(startOfThisPeriod), -attrs.daysInPeriod)\n\t\tconst startOfNextPeriod = incrementDate(new Date(startOfThisPeriod), attrs.daysInPeriod)\n\t\tconst previousRange = getRangeOfDays(startOfPreviousPeriod, attrs.daysInPeriod)\n\t\tconst currentRange = getRangeOfDays(startOfThisPeriod, attrs.daysInPeriod)\n\t\tconst nextRange = getRangeOfDays(startOfNextPeriod, attrs.daysInPeriod)\n\t\tconst previousPageEvents = attrs.getEventsOnDays(previousRange)\n\t\tconst currentPageEvents = attrs.getEventsOnDays(currentRange)\n\t\tconst nextPageEvents = attrs.getEventsOnDays(nextRange)\n\t\treturn m(PageView, {\n\t\t\tpreviousPage: {\n\t\t\t\tkey: previousRange[0].getTime(),\n\t\t\t\tnodes: this._renderWeek(attrs, previousPageEvents, currentPageEvents),\n\t\t\t},\n\t\t\tcurrentPage: {\n\t\t\t\tkey: currentRange[0].getTime(),\n\t\t\t\tnodes: this._renderWeek(attrs, currentPageEvents, currentPageEvents),\n\t\t\t},\n\t\t\tnextPage: {\n\t\t\t\tkey: nextRange[0].getTime(),\n\t\t\t\tnodes: this._renderWeek(attrs, nextPageEvents, currentPageEvents),\n\t\t\t},\n\t\t\tonChangePage: (next) => attrs.onChangeViewPeriod(next),\n\t\t})\n\t}\n\n\t_getTodayTimestamp(): number {\n\t\treturn getStartOfDay(new Date()).getTime()\n\t}\n\n\t_renderWeek(attrs: Attrs, thisWeek: EventsOnDays, mainWeek: EventsOnDays): Children {\n\t\treturn m(\n\t\t\t\".fill-absolute.flex.col.calendar-column-border.mlr-safe-inset.content-bg\",\n\t\t\t{\n\t\t\t\toncreate: (vnode) => {\n\t\t\t\t\tthis._redrawIntervalId = setInterval(m.redraw, 1000 * 60)\n\t\t\t\t},\n\t\t\t\tonremove: () => {\n\t\t\t\t\tif (this._redrawIntervalId != null) {\n\t\t\t\t\t\tclearInterval(this._redrawIntervalId)\n\t\t\t\t\t\tthis._redrawIntervalId = null\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tonmousemove: (mouseEvent: EventRedraw<MouseEvent>) => {\n\t\t\t\t\tmouseEvent.redraw = false\n\t\t\t\t\tthis._lastMousePos = getPosAndBoundsFromMouseEvent(mouseEvent)\n\n\t\t\t\t\tif (this._dateUnderMouse) {\n\t\t\t\t\t\treturn this._eventDragHandler.handleDrag(this._dateUnderMouse, this._lastMousePos)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tonmouseup: (mouseEvent: EventRedraw<MouseEvent>) => {\n\t\t\t\t\tmouseEvent.redraw = false\n\n\t\t\t\t\tthis._endDrag()\n\t\t\t\t},\n\t\t\t\tonmouseleave: (mouseEvent: EventRedraw<MouseEvent>) => {\n\t\t\t\t\tmouseEvent.redraw = false\n\n\t\t\t\t\tthis._endDrag()\n\t\t\t\t},\n\t\t\t},\n\t\t\t[\n\t\t\t\tstyles.isDesktopLayout()\n\t\t\t\t\t? this.renderHeaderDesktop(attrs, thisWeek, mainWeek)\n\t\t\t\t\t: this.renderHeaderMobile(thisWeek, mainWeek, attrs.groupColors, attrs.onEventClicked, attrs.temporaryEvents),\n\t\t\t\t// using .scroll-no-overlay because of a browser bug in Chromium where scroll wouldn't work at all\n\t\t\t\t// see https://github.com/tutao/tutanota/issues/4846\n\t\t\t\tm(\n\t\t\t\t\t\".flex.scroll-no-overlay\",\n\t\t\t\t\t{\n\t\t\t\t\t\toncreate: (vnode) => {\n\t\t\t\t\t\t\tvnode.dom.scrollTop = this._scrollPosition\n\n\t\t\t\t\t\t\tthis._domElements.push(vnode.dom as HTMLElement)\n\t\t\t\t\t\t},\n\t\t\t\t\t\tonscroll: (event: Event) => {\n\t\t\t\t\t\t\tif (thisWeek === mainWeek) {\n\t\t\t\t\t\t\t\tthis._domElements.forEach((dom) => {\n\t\t\t\t\t\t\t\t\tif (dom !== event.target) {\n\t\t\t\t\t\t\t\t\t\tdom.scrollTop = (event.target as HTMLElement).scrollTop\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t})\n\n\t\t\t\t\t\t\t\tthis._scrollPosition = (event.target as HTMLElement).scrollTop\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t[\n\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\".flex.col\",\n\t\t\t\t\t\t\tcalendarDayTimes.map((time) => {\n\t\t\t\t\t\t\t\tconst width = styles.isDesktopLayout() ? size.calendar_hour_width : size.calendar_hour_width_mobile\n\t\t\t\t\t\t\t\treturn m(\n\t\t\t\t\t\t\t\t\t\".calendar-hour.flex.cursor-pointer\",\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tonclick: (e: MouseEvent) => {\n\t\t\t\t\t\t\t\t\t\t\te.stopPropagation()\n\t\t\t\t\t\t\t\t\t\t\tattrs.onNewEvent(time.toDate(attrs.selectedDate))\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\t\t\t\".pl-s.pr-s.center.small\",\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\t\t\t\t\t\"line-height\": styles.isDesktopLayout() ? px(size.calendar_hour_height) : \"unset\",\n\t\t\t\t\t\t\t\t\t\t\t\twidth: px(width),\n\t\t\t\t\t\t\t\t\t\t\t\theight: px(size.calendar_hour_height),\n\t\t\t\t\t\t\t\t\t\t\t\t\"border-right\": `2px solid ${theme.content_border}`,\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tformatTime(time.toDate()),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\".flex.flex-grow\",\n\t\t\t\t\t\t\tthisWeek.days.map((weekday, i) => {\n\t\t\t\t\t\t\t\tconst events = thisWeek.shortEvents[i]\n\n\t\t\t\t\t\t\t\tconst newEventHandler = (hours: number, minutes: number) => {\n\t\t\t\t\t\t\t\t\tconst newDate = new Date(weekday)\n\t\t\t\t\t\t\t\t\tnewDate.setHours(hours, minutes)\n\t\t\t\t\t\t\t\t\tattrs.onNewEvent(newDate)\n\t\t\t\t\t\t\t\t\tattrs.onDateSelected(new Date(weekday))\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn m(\n\t\t\t\t\t\t\t\t\t\".flex-grow.calendar-column-border\",\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\t\t\t\theight: px(calendarDayTimes.length * size.calendar_hour_height),\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tm(CalendarDayEventsView, {\n\t\t\t\t\t\t\t\t\t\tonEventClicked: attrs.onEventClicked,\n\t\t\t\t\t\t\t\t\t\tgroupColors: attrs.groupColors,\n\t\t\t\t\t\t\t\t\t\tevents: events,\n\t\t\t\t\t\t\t\t\t\tdisplayTimeIndicator: weekday.getTime() === this._getTodayTimestamp(),\n\t\t\t\t\t\t\t\t\t\tonTimePressed: newEventHandler,\n\t\t\t\t\t\t\t\t\t\tonTimeContextPressed: newEventHandler,\n\t\t\t\t\t\t\t\t\t\tday: weekday,\n\t\t\t\t\t\t\t\t\t\tsetCurrentDraggedEvent: (event) => this.startEventDrag(event),\n\t\t\t\t\t\t\t\t\t\tsetTimeUnderMouse: (time) => (this._dateUnderMouse = combineDateWithTime(weekday, time)),\n\t\t\t\t\t\t\t\t\t\tisTemporaryEvent: (event) => attrs.temporaryEvents.includes(event),\n\t\t\t\t\t\t\t\t\t\tisDragging: this._eventDragHandler.isDragging,\n\t\t\t\t\t\t\t\t\t\tfullViewWidth: this._viewDom?.getBoundingClientRect().width,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t],\n\t\t\t\t),\n\t\t\t],\n\t\t)\n\t}\n\n\tstartEventDrag(event: CalendarEvent) {\n\t\tconst lastMousePos = this._lastMousePos\n\n\t\tif (this._dateUnderMouse && lastMousePos) {\n\t\t\tthis._eventDragHandler.prepareDrag(event, this._dateUnderMouse, lastMousePos, this._isHeaderEventBeingDragged)\n\t\t}\n\t}\n\n\tprivate renderHeaderMobile(\n\t\tthisPageEvents: EventsOnDays,\n\t\tmainPageEvents: EventsOnDays,\n\t\tgroupColors: GroupColors,\n\t\tonEventClicked: CalendarEventBubbleClickHandler,\n\t\ttemporaryEvents: Array<CalendarEvent>,\n\t): Children {\n\t\t// We calculate the height manually because we want the header to transition between heights when swiping left and right\n\t\t// Hardcoding some styles instead of classes so that we can avoid nasty magic numbers\n\t\tconst mainPageEventsCount = mainPageEvents.longEvents.length\n\t\tconst padding = mainPageEventsCount !== 0 ? size.vpad_small : 0\n\t\t// Set bottom padding in height, because it will be ignored in the style\n\t\tconst heightAdjustForPadding = 2 * padding\n\t\tconst height = mainPageEventsCount * CALENDAR_EVENT_HEIGHT + heightAdjustForPadding\n\t\treturn m(\n\t\t\t\".calendar-long-events-header.flex-fixed.calendar-hour-margin.pr-l\",\n\t\t\t{\n\t\t\t\tstyle: {\n\t\t\t\t\theight: px(height),\n\t\t\t\t\tpaddingTop: px(padding),\n\t\t\t\t\ttransition: \"height 200ms ease-in-out\",\n\t\t\t\t},\n\t\t\t\toncreate: (vnode) => {\n\t\t\t\t\tif (mainPageEvents === thisPageEvents) {\n\t\t\t\t\t\tthis._longEventsDom = vnode.dom as HTMLElement\n\t\t\t\t\t}\n\n\t\t\t\t\tm.redraw()\n\t\t\t\t},\n\t\t\t\tonupdate: (vnode) => {\n\t\t\t\t\tif (mainPageEvents === thisPageEvents) {\n\t\t\t\t\t\tthis._longEventsDom = vnode.dom as HTMLElement\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\tthis.renderLongEvents(thisPageEvents.days, thisPageEvents.longEvents, groupColors, onEventClicked, temporaryEvents).children,\n\t\t)\n\t}\n\n\tprivate renderHeaderDesktop(attrs: Attrs, thisPageEvents: EventsOnDays, mainPageEvents: EventsOnDays): Children {\n\t\tconst { selectedDate, groupColors, onEventClicked } = attrs\n\t\treturn m(\".calendar-long-events-header.flex-fixed\", [\n\t\t\tm(\n\t\t\t\t\".calendar-hour-margin\",\n\t\t\t\t{\n\t\t\t\t\tonmousemove: (mouseEvent: MouseEvent) => {\n\t\t\t\t\t\tconst { x, targetWidth } = getPosAndBoundsFromMouseEvent(mouseEvent)\n\t\t\t\t\t\tconst dayWidth = targetWidth / attrs.daysInPeriod\n\t\t\t\t\t\tconst dayNumber = Math.floor(x / dayWidth)\n\t\t\t\t\t\tconst date = new Date(thisPageEvents.days[dayNumber])\n\t\t\t\t\t\tconst dateUnderMouse = this._dateUnderMouse\n\n\t\t\t\t\t\t// When dragging short events, dont cause the mouse position date to drop to 00:00 when dragging over the header\n\t\t\t\t\t\tif (dateUnderMouse && this._eventDragHandler.isDragging && !this._isHeaderEventBeingDragged) {\n\t\t\t\t\t\t\tdate.setHours(dateUnderMouse.getHours())\n\t\t\t\t\t\t\tdate.setMinutes(dateUnderMouse.getMinutes())\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis._dateUnderMouse = date\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t// this section is tricky with margins. We use this view for both week and day view.\n\t\t\t\t// in day view there's no days row and no selection indicator.\n\t\t\t\t// it all must work with and without long events.\n\t\t\t\t// thread carefully and test all the cases.\n\t\t\t\t[\n\t\t\t\t\tthis.renderDayNamesRow(thisPageEvents.days, attrs.onDateSelected),\n\t\t\t\t\tthis.renderLongEventsSection(thisPageEvents, mainPageEvents, groupColors, onEventClicked, attrs.temporaryEvents),\n\t\t\t\t\tthis.renderSelectedDateIndicatorRow(selectedDate, thisPageEvents.days),\n\t\t\t\t],\n\t\t\t),\n\t\t])\n\t}\n\n\tprivate renderLongEventsSection(\n\t\tthisPageEvents: EventsOnDays,\n\t\tmainPageEvents: EventsOnDays,\n\t\tgroupColors: GroupColors,\n\t\tonEventClicked: CalendarEventBubbleClickHandler,\n\t\ttemporayEvents: Array<CalendarEvent>,\n\t): Children {\n\t\tconst thisPageLongEvents = this.renderLongEvents(thisPageEvents.days, thisPageEvents.longEvents, groupColors, onEventClicked, temporayEvents)\n\t\tconst mainPageLongEvents = this.renderLongEvents(mainPageEvents.days, mainPageEvents.longEvents, groupColors, onEventClicked, temporayEvents)\n\t\treturn m(\n\t\t\t\".rel.mb-xs\",\n\t\t\t{\n\t\t\t\toncreate: (vnode) => {\n\t\t\t\t\tif (mainPageEvents === thisPageEvents) {\n\t\t\t\t\t\tthis._longEventsDom = vnode.dom as HTMLElement\n\t\t\t\t\t}\n\n\t\t\t\t\tm.redraw()\n\t\t\t\t},\n\t\t\t\tonupdate: (vnode) => {\n\t\t\t\t\tif (mainPageEvents === thisPageEvents) {\n\t\t\t\t\t\tthis._longEventsDom = vnode.dom as HTMLElement\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tstyle: {\n\t\t\t\t\theight: px(mainPageLongEvents.maxEventsInColumn * CALENDAR_EVENT_HEIGHT),\n\t\t\t\t\twidth: \"100%\",\n\t\t\t\t\ttransition: \"height 200ms ease-in-out\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tthisPageLongEvents.children,\n\t\t)\n\t}\n\n\tprivate renderSelectedDateIndicatorRow(selectedDate: Date, dates: Array<Date>): Children {\n\t\tif (dates.length === 1) return null\n\n\t\treturn m(\n\t\t\t\".flex\",\n\t\t\tdates.map((day) =>\n\t\t\t\tm(\n\t\t\t\t\t\".flex-grow.flex.col\",\n\t\t\t\t\t{\n\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\tjustifyContent: \"flex-end\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tm(\"\", {\n\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t// Don't render the selected date if there is only one day shown, since it's obvious\n\t\t\t\t\t\t\tbackground: isSameDay(selectedDate, day) ? theme.content_accent : \"none\",\n\t\t\t\t\t\t\t// Browsers which don't support overflow:overlay (looking at you, FF) will shrink the contents of the event grid and it\n\t\t\t\t\t\t\t// will shift relative to the header (with indicator). It's noticeable when it's close to borders but it's not as bad\n\t\t\t\t\t\t\t// when it's smaller than the grid.\n\t\t\t\t\t\t\twidth: \"50%\",\n\t\t\t\t\t\t\t// The calendar-long-events-header has a 1px border on the bottom that overlaps this selection indicator\n\t\t\t\t\t\t\t// therefore we need to make it +1px thicker so that it looks correct (consistent with the indicator in month view)\n\t\t\t\t\t\t\theight: px(SELECTED_DATE_INDICATOR_THICKNESS + 1),\n\t\t\t\t\t\t\talignSelf: \"center\",\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t}\n\n\t/**\n\t *\n\t * @returns the rendered calendar bubble children, and the maximum number of events that occur on a day (out of all days)\n\t */\n\trenderLongEvents(\n\t\tdayRange: Array<Date>,\n\t\tevents: Array<CalendarEvent>,\n\t\tgroupColors: GroupColors,\n\t\tonEventClicked: CalendarEventBubbleClickHandler,\n\t\ttemporaryEvents: Array<CalendarEvent>,\n\t): {\n\t\tchildren: Children\n\t\tmaxEventsInColumn: number\n\t} {\n\t\treturn dayRange.length === 1\n\t\t\t? {\n\t\t\t\t\tchildren: this.renderLongEventsForSingleDay(dayRange[0], events, groupColors, onEventClicked, temporaryEvents),\n\t\t\t\t\tmaxEventsInColumn: events.length,\n\t\t\t  }\n\t\t\t: this.renderLongEventsForMultipleDays(dayRange, events, groupColors, onEventClicked, temporaryEvents)\n\t}\n\n\t/**\n\t *Only called from day view where header events are not draggable\n\t */\n\trenderLongEventsForSingleDay(\n\t\tday: Date,\n\t\tevents: Array<CalendarEvent>,\n\t\tgroupColors: GroupColors,\n\t\tonEventClicked: CalendarEventBubbleClickHandler,\n\t\ttemporaryEvents: Array<CalendarEvent>,\n\t): Children {\n\t\tconst zone = getTimeZone()\n\t\treturn [\n\t\t\tm(\n\t\t\t\t\"\",\n\t\t\t\tevents.map((event) => {\n\t\t\t\t\treturn this.renderLongEventBubble(\n\t\t\t\t\t\tevent,\n\t\t\t\t\t\tgetTimeTextFormatForLongEvent(event, day, day, zone),\n\t\t\t\t\t\teventStartsBefore(day, zone, event),\n\t\t\t\t\t\teventEndsAfterDay(day, zone, event),\n\t\t\t\t\t\tgroupColors,\n\t\t\t\t\t\t(_, domEvent) => onEventClicked(event, domEvent),\n\t\t\t\t\t\ttemporaryEvents.includes(event),\n\t\t\t\t\t)\n\t\t\t\t}),\n\t\t\t),\n\t\t]\n\t}\n\n\trenderLongEventsForMultipleDays(\n\t\tdayRange: Array<Date>,\n\t\tevents: Array<CalendarEvent>,\n\t\tgroupColors: GroupColors,\n\t\tonEventClicked: CalendarEventBubbleClickHandler,\n\t\ttemporaryEvents: Array<CalendarEvent>,\n\t): {\n\t\tchildren: Children\n\t\tmaxEventsInColumn: number\n\t} {\n\t\tif (this._longEventsDom == null) {\n\t\t\treturn {\n\t\t\t\tchildren: null,\n\t\t\t\tmaxEventsInColumn: 0,\n\t\t\t}\n\t\t}\n\n\t\tconst dayWidth = this._longEventsDom.offsetWidth / dayRange.length\n\t\tlet maxEventsInColumn = 0\n\t\tconst firstDay = dayRange[0]\n\t\tconst lastDay = lastThrow(dayRange)\n\t\tconst zone = getTimeZone()\n\t\tconst children = layOutEvents(\n\t\t\tevents,\n\t\t\tzone,\n\t\t\t(columns) => {\n\t\t\t\tmaxEventsInColumn = Math.max(maxEventsInColumn, columns.length)\n\t\t\t\treturn columns.map((rows, c) =>\n\t\t\t\t\trows.map((event) => {\n\t\t\t\t\t\tconst isAllDay = isAllDayEvent(event)\n\t\t\t\t\t\tconst eventEnd = isAllDay ? incrementDate(getEventEnd(event, zone), -1) : event.endTime\n\t\t\t\t\t\tconst dayOfStartDate = getDiffIn24hIntervals(firstDay, getEventStart(event, zone))\n\t\t\t\t\t\tconst dayOfEndDate = getDiffIn24hIntervals(firstDay, eventEnd)\n\t\t\t\t\t\tconst startsBefore = eventStartsBefore(firstDay, zone, event)\n\t\t\t\t\t\tconst endsAfter = eventEndsAfterDay(lastDay, zone, event)\n\t\t\t\t\t\tconst left = startsBefore ? 0 : dayOfStartDate * dayWidth\n\t\t\t\t\t\tconst right = endsAfter ? 0 : (dayRange.length - 1 - dayOfEndDate) * dayWidth\n\t\t\t\t\t\treturn m(\n\t\t\t\t\t\t\t\".abs\",\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\t\ttop: px(c * CALENDAR_EVENT_HEIGHT),\n\t\t\t\t\t\t\t\t\tleft: px(left),\n\t\t\t\t\t\t\t\t\tright: px(right),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tkey: event._id[0] + event._id[1] + event.startTime.getTime(),\n\t\t\t\t\t\t\t\tonmousedown: () => {\n\t\t\t\t\t\t\t\t\tthis._isHeaderEventBeingDragged = true\n\t\t\t\t\t\t\t\t\tthis.startEventDrag(event)\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tthis.renderLongEventBubble(\n\t\t\t\t\t\t\t\tevent,\n\t\t\t\t\t\t\t\tisAllDay ? null : EventTextTimeOption.START_END_TIME,\n\t\t\t\t\t\t\t\tstartsBefore,\n\t\t\t\t\t\t\t\tendsAfter,\n\t\t\t\t\t\t\t\tgroupColors,\n\t\t\t\t\t\t\t\tonEventClicked,\n\t\t\t\t\t\t\t\ttemporaryEvents.includes(event),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t)\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t},\n\t\t\tEventLayoutMode.DayBasedColumn,\n\t\t)\n\t\treturn {\n\t\t\tchildren,\n\t\t\tmaxEventsInColumn: maxEventsInColumn,\n\t\t}\n\t}\n\n\trenderLongEventBubble(\n\t\tevent: CalendarEvent,\n\t\tshowTime: EventTextTimeOption | null,\n\t\tstartsBefore: boolean,\n\t\tendsAfter: boolean,\n\t\tgroupColors: GroupColors,\n\t\tonEventClicked: CalendarEventBubbleClickHandler,\n\t\tisTemporary: boolean,\n\t): Children {\n\t\tconst fadeIn = !isTemporary\n\t\tconst opacity = isTemporary ? TEMPORARY_EVENT_OPACITY : 1\n\t\tconst enablePointerEvents = !this._eventDragHandler.isDragging && !isTemporary\n\t\treturn m(ContinuingCalendarEventBubble, {\n\t\t\tevent,\n\t\t\tstartsBefore,\n\t\t\tendsAfter,\n\t\t\tcolor: getEventColor(event, groupColors),\n\t\t\tonEventClicked,\n\t\t\tshowTime,\n\t\t\tuser: locator.logins.getUserController().user,\n\t\t\tfadeIn,\n\t\t\topacity,\n\t\t\tenablePointerEvents,\n\t\t})\n\t}\n\n\tprivate renderDayNamesRow(days: Array<Date>, onDateSelected: (arg0: Date, arg1: CalendarViewType) => unknown): Children {\n\t\tif (days.length === 1) return null\n\n\t\treturn m(\n\t\t\t\".flex.mb-s\",\n\t\t\tdays.map((day) => {\n\t\t\t\tconst dayNumberClass =\n\t\t\t\t\t\".calendar-day-indicator.calendar-day-number.clickable.circle\" + (this._getTodayTimestamp() === day.getTime() ? \".accent-bg\" : \"\")\n\n\t\t\t\t// the click handler is set on each child individually so as to not make the entire flex container clickable, only the text\n\t\t\t\tconst onclick = () => onDateSelected(day, CalendarViewType.DAY)\n\n\t\t\t\treturn m(\".flex.center-horizontally.flex-grow.center.b\", [\n\t\t\t\t\tm(\n\t\t\t\t\t\t\".calendar-day-indicator.clickable\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tonclick,\n\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\t\"padding-right\": \"4px\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tlang.formats.weekdayShort.format(day) + \" \",\n\t\t\t\t\t),\n\t\t\t\t\tm(\n\t\t\t\t\t\tdayNumberClass,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tonclick,\n\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\tmargin: \"0\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tday.getDate(),\n\t\t\t\t\t),\n\t\t\t\t])\n\t\t\t}),\n\t\t)\n\t}\n\n\t_endDrag() {\n\t\tthis._isHeaderEventBeingDragged = false\n\n\t\tif (this._dateUnderMouse) {\n\t\t\tthis._eventDragHandler.endDrag(this._dateUnderMouse).catch(ofClass(UserError, showUserError))\n\t\t}\n\t}\n}\n","import m, { Children, Component, Vnode } from \"mithril\"\nimport { MailRecipientsTextField } from \"../../../gui/MailRecipientsTextField.js\"\nimport { RecipientType } from \"../../../api/common/recipients/Recipient.js\"\nimport { ToggleButton } from \"../../../gui/base/buttons/ToggleButton.js\"\nimport { Icons } from \"../../../gui/base/icons/Icons.js\"\nimport { ButtonSize } from \"../../../gui/base/ButtonSize.js\"\nimport { Checkbox } from \"../../../gui/base/Checkbox.js\"\nimport { lang } from \"../../../misc/LanguageViewModel.js\"\nimport { CalendarAttendeeStatus } from \"../../../api/common/TutanotaConstants.js\"\nimport { Autocomplete, TextField, TextFieldType } from \"../../../gui/base/TextField.js\"\nimport { CompletenessIndicator } from \"../../../gui/CompletenessIndicator.js\"\nimport { RecipientsSearchModel } from \"../../../misc/RecipientsSearchModel.js\"\nimport { noOp } from \"@tutao/tutanota-utils\"\nimport { Guest } from \"../../date/CalendarInvites.js\"\nimport { createAttendingItems, iconForAttendeeStatus } from \"../../date/CalendarUtils.js\"\nimport { Icon } from \"../../../gui/base/Icon.js\"\nimport { theme } from \"../../../gui/theme.js\"\nimport { IconButton } from \"../../../gui/base/IconButton.js\"\nimport { BootIcons } from \"../../../gui/base/icons/BootIcons.js\"\nimport { px, size } from \"../../../gui/size.js\"\nimport { createDropdown } from \"../../../gui/base/Dropdown.js\"\nimport { CalendarEventWhoModel } from \"../../date/eventeditor/CalendarEventWhoModel.js\"\nimport { LoginController } from \"../../../api/main/LoginController.js\"\nimport { CalendarEventModel } from \"../../date/eventeditor/CalendarEventModel.js\"\nimport { DropDownSelector } from \"../../../gui/base/DropDownSelector.js\"\nimport { showPlanUpgradeRequiredDialog } from \"../../../misc/SubscriptionDialogs.js\"\n\nexport type AttendeeListEditorAttrs = {\n\t/** the event that is currently being edited */\n\tmodel: CalendarEventModel\n\n\t/** these are needed to show suggestions and external passwords. */\n\trecipientsSearch: RecipientsSearchModel\n\tlogins: LoginController\n}\n\n/**\n * an editor that can edit the attendees list of a calendar event with suggestions,\n * including the own attendance, the own organizer address and external passwords.\n */\nexport class AttendeeListEditor implements Component<AttendeeListEditorAttrs> {\n\tprivate text: string = \"\"\n\tprivate hasPlanWithInvites: boolean = false\n\tprivate externalPasswordVisibility: Map<string, boolean> = new Map()\n\n\tview({ attrs }: Vnode<AttendeeListEditorAttrs>): Children {\n\t\treturn [m(\".flex-grow\", this.renderInvitationField(attrs)), m(\".flex-grow\", this.renderGuestList(attrs))]\n\t}\n\n\tprivate renderInvitationField(attrs: AttendeeListEditorAttrs): Children {\n\t\tconst { model, recipientsSearch } = attrs\n\n\t\treturn m(\".flex.flex-column.flex-grow\", [\n\t\t\tm(MailRecipientsTextField, {\n\t\t\t\tlabel: \"addGuest_label\",\n\t\t\t\ttext: this.text,\n\t\t\t\tonTextChanged: (v) => (this.text = v),\n\t\t\t\t// we don't show bubbles, we just want the search dropdown\n\t\t\t\trecipients: [],\n\t\t\t\tdisabled: false,\n\t\t\t\tonRecipientAdded: async (address, name, contact) => {\n\t\t\t\t\tif (!(this.hasPlanWithInvites || !model.shouldShowSendInviteNotAvailable())) {\n\t\t\t\t\t\tconst { getAvailablePlansWithEventInvites } = await import(\"../../../subscription/SubscriptionUtils.js\")\n\t\t\t\t\t\tconst plansWithEventInvites = await getAvailablePlansWithEventInvites()\n\t\t\t\t\t\t//entity event updates are too slow to call updateBusinessFeature()\n\t\t\t\t\t\tthis.hasPlanWithInvites = await showPlanUpgradeRequiredDialog(plansWithEventInvites)\n\t\t\t\t\t\tif (!this.hasPlanWithInvites) return\n\t\t\t\t\t}\n\t\t\t\t\tmodel.editModels.whoModel.addAttendee(address, contact)\n\t\t\t\t},\n\t\t\t\t// do nothing because we don't have any bubbles here\n\t\t\t\tonRecipientRemoved: noOp,\n\t\t\t\tinjectionsRight: this.renderIsConfidentialToggle(attrs),\n\t\t\t\tsearch: recipientsSearch,\n\t\t\t}),\n\t\t\tthis.renderSendUpdateCheckbox(attrs),\n\t\t])\n\t}\n\n\tprivate renderIsConfidentialToggle(attrs: AttendeeListEditorAttrs): Children {\n\t\tconst { whoModel } = attrs.model.editModels\n\t\tconst guests = whoModel.guests\n\t\tif (!guests.some((a) => a.type === RecipientType.EXTERNAL)) return null\n\t\treturn m(ToggleButton, {\n\t\t\ttitle: whoModel.isConfidential ? \"confidential_action\" : \"nonConfidential_action\",\n\t\t\tonToggled: (_, e) => {\n\t\t\t\twhoModel.isConfidential = !whoModel.isConfidential\n\t\t\t\te.stopPropagation()\n\t\t\t},\n\t\t\ticon: whoModel.isConfidential ? Icons.Lock : Icons.Unlock,\n\t\t\ttoggled: whoModel.isConfidential,\n\t\t\tsize: ButtonSize.Compact,\n\t\t})\n\t}\n\n\tprivate renderSendUpdateCheckbox({ model }: AttendeeListEditorAttrs): Children {\n\t\tconst { whoModel } = model.editModels\n\t\treturn !whoModel.initiallyHadOtherAttendees\n\t\t\t? null\n\t\t\t: m(\n\t\t\t\t\t\".mt-negative-s\",\n\t\t\t\t\tm(Checkbox, {\n\t\t\t\t\t\tlabel: () => lang.get(\"sendUpdates_label\"),\n\t\t\t\t\t\tonChecked: (v) => (model.shouldSendUpdates = v),\n\t\t\t\t\t\tchecked: model.shouldSendUpdates,\n\t\t\t\t\t}),\n\t\t\t  )\n\t}\n\n\t/**\n\t * render the list of guests, always putting the organizer on top, then the rest,\n\t * followed by the external passwords.\n\t *\n\t * in cases where we can see the event editor AND we have to render a guest list, we're guaranteed to be the organizer.\n\t * @private\n\t */\n\tprivate renderGuestList(attrs: AttendeeListEditorAttrs): Children {\n\t\tconst { whoModel } = attrs.model.editModels\n\t\tconst organizer = whoModel.organizer\n\t\tconst guests: Array<Guest> = whoModel.guests.slice()\n\t\tconst attendeeRenderers: Array<() => Children> = []\n\n\t\t// we're assuming that we're the organizer here, since\n\t\t// * no-one but the organizer can edit the attendees of an event\n\t\t// * only the owner of a calendar can add attendees and become the organizer.\n\t\tif (organizer != null) {\n\t\t\tattendeeRenderers.push(() => renderOrganizer(organizer, attrs))\n\t\t}\n\n\t\tfor (const guest of whoModel.guests) {\n\t\t\tattendeeRenderers.push(() => renderGuest(guest, attrs))\n\t\t}\n\n\t\tconst externalGuestPasswords = whoModel.isConfidential\n\t\t\t? guests\n\t\t\t\t\t.filter((a) => a.type === RecipientType.EXTERNAL)\n\t\t\t\t\t.map((guest) => {\n\t\t\t\t\t\tconst { address } = guest\n\t\t\t\t\t\tconst { password, strength } = whoModel.getPresharedPassword(address)\n\t\t\t\t\t\treturn m(TextField, {\n\t\t\t\t\t\t\tvalue: password,\n\t\t\t\t\t\t\tautocompleteAs: Autocomplete.off,\n\t\t\t\t\t\t\ttype: this.externalPasswordVisibility.get(address) === true ? TextFieldType.Text : TextFieldType.Password,\n\t\t\t\t\t\t\tlabel: () =>\n\t\t\t\t\t\t\t\tlang.get(\"passwordFor_label\", {\n\t\t\t\t\t\t\t\t\t\"{1}\": guest.address,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\thelpLabel: () => m(\".mt-s\", m(CompletenessIndicator, { percentageCompleted: strength })),\n\t\t\t\t\t\t\tkey: address,\n\t\t\t\t\t\t\toninput: (newValue) => whoModel.setPresharedPassword(address, newValue),\n\t\t\t\t\t\t\tinjectionsRight: () => this.renderRevealIcon(guest.address),\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t: []\n\n\t\treturn m(\"\", [...attendeeRenderers.map((r) => r()), externalGuestPasswords])\n\t}\n\n\tprivate renderRevealIcon(address: string): Children {\n\t\treturn m(IconButton, {\n\t\t\ttitle: this.externalPasswordVisibility.get(address) === true ? \"concealPassword_action\" : \"revealPassword_action\",\n\t\t\tclick: () => {\n\t\t\t\tthis.externalPasswordVisibility.set(address, !this.externalPasswordVisibility.get(address))\n\t\t\t},\n\t\t\ticon: this.externalPasswordVisibility.get(address) === true ? Icons.NoEye : Icons.Eye,\n\t\t\tsize: ButtonSize.Compact,\n\t\t})\n\t}\n}\n\n/**\n *\n * @param editModel the event to set the organizer on when a button in the dropdown is clicked\n * @param e\n */\nfunction showOrganizerDropdown(editModel: CalendarEventWhoModel, e: MouseEvent) {\n\tconst lazyButtons = () =>\n\t\teditModel.possibleOrganizers.map((organizer) => {\n\t\t\treturn {\n\t\t\t\tlabel: () => organizer.address,\n\t\t\t\tclick: () => editModel.addAttendee(organizer.address, null),\n\t\t\t}\n\t\t})\n\n\tcreateDropdown({ lazyButtons, width: 300 })(e, e.target as HTMLElement)\n}\n\nexport function renderOrganizer(organizer: Guest, { model }: Pick<AttendeeListEditorAttrs, \"model\">): Children {\n\tconst { whoModel } = model.editModels\n\tconst { address, name, status } = organizer\n\tconst isMe = organizer.address === whoModel.ownGuest?.address\n\tconst editableOrganizer = whoModel.possibleOrganizers.length > 1 && isMe\n\tconst roleLabel = isMe ? `${lang.get(\"organizer_label\")} | ${lang.get(\"you_label\")}` : lang.get(\"organizer_label\")\n\tconst statusLine = m(\".small.flex.center-vertically\", [renderStatusIcon(status), roleLabel])\n\tconst fullName = m(\"div.text-ellipsis\", { style: { lineHeight: px(24) } }, name.length > 0 ? `${name} ${address}` : address)\n\tconst nameAndAddress = editableOrganizer\n\t\t? m(\".flex.flex-grow.items-center.click\", { onclick: (e: MouseEvent) => showOrganizerDropdown(whoModel, e) }, [\n\t\t\t\tfullName,\n\t\t\t\tm(Icon, {\n\t\t\t\t\ticon: BootIcons.Expand,\n\t\t\t\t\tstyle: {\n\t\t\t\t\t\tfill: theme.content_fg,\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t  ])\n\t\t: m(\".flex.flex-grow.items-center\", fullName)\n\n\tconst rightContent = isMe\n\t\t? m(\n\t\t\t\t\"\",\n\t\t\t\t{ style: { minWidth: \"120px\" } },\n\t\t\t\tm(DropDownSelector, {\n\t\t\t\t\tlabel: \"attending_label\",\n\t\t\t\t\titems: createAttendingItems(),\n\t\t\t\t\tselectedValue: status,\n\t\t\t\t\tclass: \"\",\n\t\t\t\t\tselectionChangedHandler: (value: CalendarAttendeeStatus) => {\n\t\t\t\t\t\tif (value == null) return\n\t\t\t\t\t\twhoModel.setOwnAttendance(value)\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t  )\n\t\t: m(IconButton, {\n\t\t\t\ttitle: \"sendMail_alt\",\n\t\t\t\tclick: async () =>\n\t\t\t\t\t(await import(\"../../../contacts/view/ContactView.js\")).writeMail(\n\t\t\t\t\t\torganizer,\n\t\t\t\t\t\tlang.get(\"repliedToEventInvite_msg\", {\n\t\t\t\t\t\t\t\"{event}\": model.editModels.summary.content,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\ticon: Icons.PencilSquare,\n\t\t  })\n\n\treturn renderAttendee(nameAndAddress, statusLine, rightContent)\n}\n\nfunction renderGuest(guest: Guest, { model }: Pick<AttendeeListEditorAttrs, \"model\">): Children {\n\tconst { whoModel } = model.editModels\n\tconst { address, name, status } = guest\n\tconst statusLine = m(\".small.flex.center-vertically\", [renderStatusIcon(status), lang.get(\"guest_label\")])\n\tconst fullName = m(\"div.text-ellipsis\", { style: { lineHeight: px(24) } }, name.length > 0 ? `${name} ${address}` : address)\n\tconst nameAndAddress = m(\".flex.flex-grow.items-center\", fullName)\n\tconst rightContent = whoModel.canModifyGuests\n\t\t? m(IconButton, {\n\t\t\t\ttitle: \"remove_action\",\n\t\t\t\ticon: Icons.Cancel,\n\t\t\t\tclick: () => whoModel.removeAttendee(guest.address),\n\t\t  })\n\t\t: null\n\treturn renderAttendee(nameAndAddress, statusLine, rightContent)\n}\n\nfunction renderAttendee(nameAndAddress: Children, statusLine: Children, rightContent: Children): Children {\n\tconst spacer = m(\".flex-grow\")\n\treturn m(\n\t\t\".flex\",\n\t\t{\n\t\t\tstyle: {\n\t\t\t\theight: px(size.button_height),\n\t\t\t\tborderBottom: \"1px transparent\",\n\t\t\t\tmarginTop: px(size.vpad),\n\t\t\t},\n\t\t},\n\t\t[m(\".flex.col.flex-grow.overflow-hidden.flex-no-grow-shrink-auto\", [nameAndAddress, statusLine]), spacer, rightContent],\n\t)\n}\n\nfunction renderStatusIcon(status: CalendarAttendeeStatus): Children {\n\tconst icon = iconForAttendeeStatus[status]\n\treturn m(Icon, {\n\t\ticon,\n\t\tclass: \"mr-s\",\n\t\tstyle: {\n\t\t\tfill: theme.content_fg,\n\t\t},\n\t})\n}\n","import m, { Component, Vnode } from \"mithril\"\nimport { DatePicker } from \"../../../gui/date/DatePicker.js\"\nimport { TimePicker } from \"../../../gui/date/TimePicker.js\"\nimport { TimeFormat } from \"../../../api/common/TutanotaConstants.js\"\nimport { Checkbox } from \"../../../gui/base/Checkbox.js\"\nimport { lang } from \"../../../misc/LanguageViewModel.js\"\nimport { CalendarEventWhenModel } from \"../../date/eventeditor/CalendarEventWhenModel.js\"\n\nimport { renderTwoColumnsIfFits } from \"../../../gui/base/GuiUtils.js\"\n\nexport type EventTimeEditorAttrs = {\n\tstartOfTheWeekOffset: number\n\ttimeFormat: TimeFormat\n\teditModel: CalendarEventWhenModel\n\tdisabled: boolean\n}\n\n/**\n * an editor component to edit the start date and end date of a calendar event.\n * also allows to edit start time and end time for events where that makes sense (ie not all-day)\n */\nexport class EventTimeEditor implements Component<EventTimeEditorAttrs> {\n\tview(vnode: Vnode<EventTimeEditorAttrs>) {\n\t\tconst { attrs } = vnode\n\t\tconst { startOfTheWeekOffset, editModel, timeFormat } = attrs\n\n\t\treturn [\n\t\t\trenderTwoColumnsIfFits(\n\t\t\t\t[\n\t\t\t\t\tm(\n\t\t\t\t\t\t\".flex-grow\",\n\t\t\t\t\t\tm(DatePicker, {\n\t\t\t\t\t\t\tdate: editModel?.startDate,\n\t\t\t\t\t\t\tonDateSelected: (date) => date && (editModel.startDate = date),\n\t\t\t\t\t\t\tstartOfTheWeekOffset,\n\t\t\t\t\t\t\tlabel: \"dateFrom_label\",\n\t\t\t\t\t\t\tnullSelectionText: \"emptyString_msg\",\n\t\t\t\t\t\t\tdisabled: attrs.disabled,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\t!editModel.isAllDay\n\t\t\t\t\t\t? m(\n\t\t\t\t\t\t\t\t\".ml-s.time-field\",\n\t\t\t\t\t\t\t\tm(TimePicker, {\n\t\t\t\t\t\t\t\t\ttime: editModel.startTime,\n\t\t\t\t\t\t\t\t\tonTimeSelected: (time) => (editModel.startTime = time),\n\t\t\t\t\t\t\t\t\ttimeFormat,\n\t\t\t\t\t\t\t\t\tdisabled: attrs.disabled,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t  )\n\t\t\t\t\t\t: null,\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\tm(\n\t\t\t\t\t\t\".flex-grow\",\n\t\t\t\t\t\tm(DatePicker, {\n\t\t\t\t\t\t\tdate: editModel.endDate,\n\t\t\t\t\t\t\tonDateSelected: (date) => date && (editModel.endDate = date),\n\t\t\t\t\t\t\tstartOfTheWeekOffset,\n\t\t\t\t\t\t\tlabel: \"dateTo_label\",\n\t\t\t\t\t\t\tnullSelectionText: \"emptyString_msg\",\n\t\t\t\t\t\t\tdisabled: attrs.disabled,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\t!editModel.isAllDay\n\t\t\t\t\t\t? m(\n\t\t\t\t\t\t\t\t\".ml-s.time-field\",\n\t\t\t\t\t\t\t\tm(TimePicker, {\n\t\t\t\t\t\t\t\t\ttime: editModel.endTime,\n\t\t\t\t\t\t\t\t\tonTimeSelected: (time) => (editModel.endTime = time),\n\t\t\t\t\t\t\t\t\ttimeFormat,\n\t\t\t\t\t\t\t\t\tdisabled: attrs.disabled,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t  )\n\t\t\t\t\t\t: null,\n\t\t\t\t],\n\t\t\t),\n\t\t\tm(\".flex.items-center.mt-s\", [\n\t\t\t\tm(Checkbox, {\n\t\t\t\t\tchecked: editModel.isAllDay,\n\t\t\t\t\tonChecked: (value) => (editModel.isAllDay = value),\n\t\t\t\t\tdisabled: attrs.disabled,\n\t\t\t\t\tlabel: () => lang.get(\"allDay_label\"),\n\t\t\t\t}),\n\t\t\t\tm(\".flex-grow\"),\n\t\t\t]),\n\t\t]\n\t}\n}\n","import m, { Children, Component, Vnode } from \"mithril\"\nimport { CalendarEventWhenModel } from \"../../date/eventeditor/CalendarEventWhenModel.js\"\nimport { createIntervalValues, createRepeatRuleEndTypeValues, createRepeatRuleFrequencyValues } from \"../../date/CalendarUtils.js\"\nimport { TextField } from \"../../../gui/base/TextField.js\"\nimport { lang } from \"../../../misc/LanguageViewModel.js\"\nimport { DropDownSelector, DropDownSelectorAttrs, SelectorItemList } from \"../../../gui/base/DropDownSelector.js\"\nimport { EndType, RepeatPeriod } from \"../../../api/common/TutanotaConstants.js\"\nimport { BootIcons } from \"../../../gui/base/icons/BootIcons.js\"\nimport { DatePicker } from \"../../../gui/date/DatePicker.js\"\nimport { IconButton } from \"../../../gui/base/IconButton.js\"\nimport { Icons } from \"../../../gui/base/icons/Icons.js\"\n\nimport { renderTwoColumnsIfFits } from \"../../../gui/base/GuiUtils.js\"\n\nexport type RepeatRuleEditorAttrs = {\n\tmodel: CalendarEventWhenModel\n\tstartOfTheWeekOffset: number\n}\n\nexport class RepeatRuleEditor implements Component<RepeatRuleEditorAttrs> {\n\tview({ attrs }: Vnode<RepeatRuleEditorAttrs>): Children {\n\t\tconst { model } = attrs\n\t\treturn [\n\t\t\trenderTwoColumnsIfFits(\n\t\t\t\t[\n\t\t\t\t\tm(\".flex-grow.pr-s\", this.renderRepeatPeriod(model)),\n\t\t\t\t\tm(\".flex-grow.pl-s\" + (model.repeatPeriod != null ? \"\" : \".hidden\"), this.renderRepeatInterval(model)),\n\t\t\t\t],\n\t\t\t\tthis.renderEndCondition(attrs),\n\t\t\t),\n\t\t\trenderTwoColumnsIfFits(this.renderExclusionCount(model), null),\n\t\t]\n\t}\n\n\tprivate renderEndCondition(attrs: RepeatRuleEditorAttrs): Children {\n\t\tconst { model } = attrs\n\t\tif (model.repeatPeriod == null) {\n\t\t\treturn null\n\t\t}\n\t\treturn [m(\".flex-grow.pr-s\", this.renderEndType(model)), m(\".flex-grow.pl-s\", this.renderEndValue(attrs))]\n\t}\n\n\tprivate renderExclusionCount(model: CalendarEventWhenModel): Children {\n\t\tif (model.repeatPeriod == null || model.excludedDates.length === 0) {\n\t\t\treturn null\n\t\t}\n\t\treturn [\n\t\t\tm(\n\t\t\t\t\".flex-grow.pr-s\",\n\t\t\t\tm(TextField, {\n\t\t\t\t\tlabel: \"emptyString_msg\",\n\t\t\t\t\tvalue: lang.get(\"someRepetitionsDeleted_msg\"),\n\t\t\t\t\tinjectionsRight: () => this.renderDeleteExclusionButton(model),\n\t\t\t\t\tdisabled: true,\n\t\t\t\t}),\n\t\t\t),\n\t\t]\n\t}\n\n\t/**\n\t * how frequently the event repeats (Never, daily, annually etc)\n\t * @private\n\t */\n\tprivate renderRepeatPeriod(model: CalendarEventWhenModel) {\n\t\tconst repeatValues: SelectorItemList<RepeatPeriod | null> = createRepeatRuleFrequencyValues()\n\t\treturn m(DropDownSelector, {\n\t\t\tlabel: \"calendarRepeating_label\",\n\t\t\titems: repeatValues,\n\t\t\tselectedValue: model.repeatPeriod,\n\t\t\tselectionChangedHandler: (period) => (model.repeatPeriod = period),\n\t\t\ticon: BootIcons.Expand,\n\t\t\tdisabled: false,\n\t\t} satisfies DropDownSelectorAttrs<RepeatPeriod | null>)\n\t}\n\n\t/** Repeat interval: every day, every second day etc\n\t * @private\n\t */\n\tprivate renderRepeatInterval(model: CalendarEventWhenModel) {\n\t\tconst intervalValues: SelectorItemList<number> = createIntervalValues()\n\t\treturn m(DropDownSelector, {\n\t\t\tlabel: \"interval_title\",\n\t\t\titems: intervalValues,\n\t\t\tselectedValue: model.repeatInterval,\n\t\t\tselectionChangedHandler: (interval: number) => (model.repeatInterval = interval),\n\t\t\ticon: BootIcons.Expand,\n\t\t\tdisabled: false,\n\t\t})\n\t}\n\n\t/**\n\t * if and how the event stops repeating, like after number of occurrences or after a date.\n\t * @param model\n\t * @private\n\t */\n\tprivate renderEndType(model: CalendarEventWhenModel) {\n\t\tconst endTypeValues: SelectorItemList<EndType> = createRepeatRuleEndTypeValues()\n\t\treturn m(DropDownSelector, {\n\t\t\tlabel: () => lang.get(\"calendarRepeatStopCondition_label\"),\n\t\t\titems: endTypeValues,\n\t\t\tselectedValue: model.repeatEndType,\n\t\t\tselectionChangedHandler: (end: EndType) => (model.repeatEndType = end),\n\t\t\ticon: BootIcons.Expand,\n\t\t\tdisabled: false,\n\t\t})\n\t}\n\n\t/**\n\t * the value of the end condition - a number for ending after number of occurrences, a date for ending after date.\n\t * @private\n\t */\n\tprivate renderEndValue(attrs: RepeatRuleEditorAttrs): Children {\n\t\tconst { model, startOfTheWeekOffset } = attrs\n\t\tconst intervalValues: SelectorItemList<number> = createIntervalValues()\n\t\tif (model.repeatPeriod == null || model.repeatEndType === EndType.Never) {\n\t\t\treturn null\n\t\t} else if (model.repeatEndType === EndType.Count) {\n\t\t\treturn m(DropDownSelector, {\n\t\t\t\tlabel: \"emptyString_msg\",\n\t\t\t\titems: intervalValues,\n\t\t\t\tselectedValue: model.repeatEndOccurrences,\n\t\t\t\tselectionChangedHandler: (endValue: number) => (model.repeatEndOccurrences = endValue),\n\t\t\t\ticon: BootIcons.Expand,\n\t\t\t})\n\t\t} else if (model.repeatEndType === EndType.UntilDate) {\n\t\t\treturn m(DatePicker, {\n\t\t\t\tdate: model.repeatEndDateForDisplay,\n\t\t\t\tonDateSelected: (date) => (model.repeatEndDateForDisplay = date),\n\t\t\t\tstartOfTheWeekOffset,\n\t\t\t\tlabel: \"emptyString_msg\",\n\t\t\t\tnullSelectionText: \"emptyString_msg\",\n\t\t\t\t// When the guests expander is expanded and the dialog has overflow, then the scrollbar will overlap the date picker popup\n\t\t\t\t// to fix this we could either:\n\t\t\t\t// * reorganize the layout so it doesn't go over the right edge\n\t\t\t\t// * change the alignment so that it goes to the left (this is what we do)\n\t\t\t\trightAlignDropdown: true,\n\t\t\t})\n\t\t} else {\n\t\t\treturn null\n\t\t}\n\t}\n\n\tprivate renderDeleteExclusionButton(model: CalendarEventWhenModel): Children {\n\t\treturn m(IconButton, {\n\t\t\ttitle: \"restoreExcludedRecurrences_action\",\n\t\t\tclick: () => model.deleteExcludedDates(),\n\t\t\ticon: Icons.Cancel,\n\t\t})\n\t}\n}\n","import m, { Children, Component, Vnode } from \"mithril\"\nimport { ExpanderButton, ExpanderPanel } from \"../../../gui/base/Expander.js\"\nimport { AttendeeListEditor, AttendeeListEditorAttrs, renderOrganizer } from \"./AttendeeListEditor.js\"\nimport { locator } from \"../../../api/main/MainLocator.js\"\nimport { EventTimeEditor, EventTimeEditorAttrs } from \"./EventTimeEditor.js\"\nimport { RepeatRuleEditor, RepeatRuleEditorAttrs } from \"./RepeatRuleEditor.js\"\nimport { TextField, TextFieldAttrs } from \"../../../gui/base/TextField.js\"\nimport { defaultCalendarColor, TimeFormat } from \"../../../api/common/TutanotaConstants.js\"\nimport { lang } from \"../../../misc/LanguageViewModel.js\"\nimport { RecipientsSearchModel } from \"../../../misc/RecipientsSearchModel.js\"\nimport { DropDownSelector, DropDownSelectorAttrs } from \"../../../gui/base/DropDownSelector.js\"\nimport { getSharedGroupName } from \"../../../sharing/GroupUtils.js\"\nimport { BootIcons } from \"../../../gui/base/icons/BootIcons.js\"\nimport { CalendarInfo } from \"../../model/CalendarModel.js\"\nimport { createAlarmIntervalItems } from \"../../date/CalendarUtils.js\"\nimport { Icons } from \"../../../gui/base/icons/Icons.js\"\nimport { IconButton } from \"../../../gui/base/IconButton.js\"\nimport { ButtonSize } from \"../../../gui/base/ButtonSize.js\"\nimport { HtmlEditor } from \"../../../gui/editor/HtmlEditor.js\"\nimport { attachDropdown } from \"../../../gui/base/Dropdown.js\"\nimport { BannerType, InfoBanner, InfoBannerAttrs } from \"../../../gui/base/InfoBanner.js\"\nimport { CalendarEventModel, EventType } from \"../../date/eventeditor/CalendarEventModel.js\"\n\nexport type CalendarEventEditViewAttrs = {\n\tmodel: CalendarEventModel\n\tgroupColors: Map<Id, string>\n\trecipientsSearch: RecipientsSearchModel\n\tdescriptionEditor: HtmlEditor\n\tstartOfTheWeekOffset: number\n\ttimeFormat: TimeFormat\n}\n\n/**\n * combines several semi-related editor components into a full editor for editing calendar events\n * to be displayed in a dialog.\n *\n * controls the enabling/disabling of certain editor components and the display of additional info\n * in the dialog depending on the type of the event being edited.\n */\nexport class CalendarEventEditView implements Component<CalendarEventEditViewAttrs> {\n\tprivate attendeesExpanded: boolean = false\n\n\tprivate readonly recipientsSearch: RecipientsSearchModel\n\tprivate readonly timeFormat: TimeFormat\n\tprivate readonly startOfTheWeekOffset: number\n\n\tconstructor(vnode: Vnode<CalendarEventEditViewAttrs>) {\n\t\tthis.timeFormat = vnode.attrs.timeFormat\n\t\tthis.startOfTheWeekOffset = vnode.attrs.startOfTheWeekOffset\n\t\tthis.attendeesExpanded = vnode.attrs.model.editModels.whoModel.guests.length > 0\n\t\tthis.recipientsSearch = vnode.attrs.recipientsSearch\n\t}\n\n\tview(vnode: Vnode<CalendarEventEditViewAttrs>): Children {\n\t\treturn m(\n\t\t\t\".pb\",\n\t\t\t{\n\t\t\t\tstyle: {\n\t\t\t\t\t// The date picker dialogs have position: fixed, and they are fixed relative to the most recent ancestor with\n\t\t\t\t\t// a transform. So doing a no-op transform will make the dropdowns scroll with the dialog\n\t\t\t\t\t// without this, then the date picker dialogs will show at the same place on the screen regardless of whether the\n\t\t\t\t\t// editor has scrolled or not.\n\t\t\t\t\t// Ideally we could do this inside DatePicker itself, but the rendering breaks and the dialog appears below it's siblings\n\t\t\t\t\t// We also don't want to do this for all dialogs because it could potentially cause other issues\n\t\t\t\t\ttransform: \"translate(0)\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t[\n\t\t\t\tthis.renderReadonlyMessage(vnode.attrs),\n\t\t\t\tthis.renderHeading(vnode.attrs),\n\t\t\t\tthis.renderAttendees(vnode.attrs),\n\t\t\t\tthis.renderEventTimeEditor(vnode.attrs),\n\t\t\t\tthis.renderRepeatRuleEditor(vnode.attrs),\n\t\t\t\tm(\".flex\", [this.renderCalendarPicker(vnode), this.renderRemindersEditor(vnode)]),\n\t\t\t\tthis.renderLocationField(vnode),\n\t\t\t\tthis.renderDescriptionEditor(vnode),\n\t\t\t],\n\t\t)\n\t}\n\n\tprivate renderHeading(attrs: CalendarEventEditViewAttrs): Children {\n\t\tconst { model } = attrs\n\t\treturn m(TextField, {\n\t\t\tlabel: \"title_placeholder\",\n\t\t\tvalue: model.editModels.summary.content,\n\t\t\toninput: (v) => (model.editModels.summary.content = v),\n\t\t\tdisabled: model.eventType !== EventType.OWN && model.eventType !== EventType.SHARED_RW,\n\t\t\tclass: \"big-input pt flex-grow\",\n\t\t\tinjectionsRight: () => this.renderGuestsExpanderButton(attrs),\n\t\t} satisfies TextFieldAttrs)\n\t}\n\n\tprivate renderGuestsExpanderButton(attrs: CalendarEventEditViewAttrs): Children {\n\t\tif (!attrs.model.editModels.whoModel.canModifyGuests) return null\n\t\treturn m(\n\t\t\t\".mr-s\",\n\t\t\tm(ExpanderButton, {\n\t\t\t\tlabel: \"guests_label\",\n\t\t\t\texpanded: this.attendeesExpanded,\n\t\t\t\tonExpandedChange: (v) => (this.attendeesExpanded = v),\n\t\t\t\tstyle: {\n\t\t\t\t\tpaddingTop: 0,\n\t\t\t\t},\n\t\t\t}),\n\t\t)\n\t}\n\n\tprivate renderReadonlyMessage(attrs: CalendarEventEditViewAttrs): Children {\n\t\tconst { model } = attrs\n\t\t// when editing new events, eventType is always is OWN, but you might still not be able to add guests.\n\t\tif (model.eventType === EventType.OWN && model.editModels.whoModel.canModifyGuests) return null\n\t\treturn m(\n\t\t\t\".pt-s\",\n\t\t\tm(InfoBanner, {\n\t\t\t\tmessage: () => m(\".small\", lang.get(\"cannotEditFullEvent_msg\")),\n\t\t\t\ticon: Icons.People,\n\t\t\t\ttype: BannerType.Info,\n\t\t\t\tbuttons: [],\n\t\t\t} satisfies InfoBannerAttrs),\n\t\t)\n\t}\n\n\tprivate renderAttendees(attrs: CalendarEventEditViewAttrs): Children {\n\t\tconst { model } = attrs\n\t\tif (!model.editModels.whoModel.canModifyGuests) {\n\t\t\tconst { organizer } = model.editModels.whoModel\n\t\t\treturn organizer && renderOrganizer(organizer, { model })\n\t\t} else {\n\t\t\treturn m(\n\t\t\t\t\".mb.rel\",\n\t\t\t\tm(\n\t\t\t\t\tExpanderPanel,\n\t\t\t\t\t{ expanded: this.attendeesExpanded },\n\t\t\t\t\tm(AttendeeListEditor, {\n\t\t\t\t\t\tmodel,\n\t\t\t\t\t\trecipientsSearch: this.recipientsSearch,\n\t\t\t\t\t\tlogins: locator.logins,\n\t\t\t\t\t} satisfies AttendeeListEditorAttrs),\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\t}\n\n\tprivate renderEventTimeEditor(attrs: CalendarEventEditViewAttrs): Children {\n\t\treturn m(EventTimeEditor, {\n\t\t\teditModel: attrs.model.editModels.whenModel,\n\t\t\ttimeFormat: this.timeFormat,\n\t\t\tstartOfTheWeekOffset: this.startOfTheWeekOffset,\n\t\t\tdisabled: attrs.model.eventType !== EventType.OWN && attrs.model.eventType !== EventType.SHARED_RW,\n\t\t} satisfies EventTimeEditorAttrs)\n\t}\n\n\tprivate renderRepeatRuleEditor(attrs: CalendarEventEditViewAttrs): Children {\n\t\tif (attrs.model.eventType !== EventType.OWN && attrs.model.eventType !== EventType.SHARED_RW) return null\n\t\treturn m(RepeatRuleEditor, {\n\t\t\tmodel: attrs.model.editModels.whenModel,\n\t\t\tstartOfTheWeekOffset: this.startOfTheWeekOffset,\n\t\t} satisfies RepeatRuleEditorAttrs)\n\t}\n\n\tprivate renderCalendarPicker(vnode: Vnode<CalendarEventEditViewAttrs>): Children {\n\t\tconst { model } = vnode.attrs\n\t\tconst availableCalendars = model.editModels.whoModel.getAvailableCalendars()\n\t\treturn m(\n\t\t\t\".flex-half.pr-s\",\n\t\t\tm(DropDownSelector, {\n\t\t\t\tlabel: \"calendar_label\",\n\t\t\t\titems: availableCalendars.map((calendarInfo) => {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tname: getSharedGroupName(calendarInfo.groupInfo, model.userController, calendarInfo.shared),\n\t\t\t\t\t\tvalue: calendarInfo,\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t\tselectedValue: model.editModels.whoModel.selectedCalendar,\n\t\t\t\tselectionChangedHandler: (v) => (model.editModels.whoModel.selectedCalendar = v),\n\t\t\t\ticon: BootIcons.Expand,\n\t\t\t\tdisabled: model.eventType !== EventType.OWN && model.eventType !== EventType.SHARED_RW,\n\t\t\t\thelpLabel: () => this.renderCalendarColor(model.editModels.whoModel.selectedCalendar, vnode.attrs.groupColors),\n\t\t\t} satisfies DropDownSelectorAttrs<CalendarInfo>),\n\t\t)\n\t}\n\n\tprivate renderCalendarColor(selectedCalendar: CalendarInfo | null, groupColors: Map<Id, string>) {\n\t\tconst color = selectedCalendar ? groupColors.get(selectedCalendar.groupInfo.group) ?? defaultCalendarColor : null\n\t\treturn m(\".mt-xs\", {\n\t\t\tstyle: {\n\t\t\t\twidth: \"100px\",\n\t\t\t\theight: \"10px\",\n\t\t\t\tbackground: color ? \"#\" + color : \"transparent\",\n\t\t\t},\n\t\t})\n\t}\n\n\tprivate renderRemindersEditor(vnode: Vnode<CalendarEventEditViewAttrs>): Children {\n\t\tif (!vnode.attrs.model.editModels.alarmModel.canEditReminders) return null\n\t\tconst { alarmModel } = vnode.attrs.model.editModels\n\t\tconst { taken, available } = alarmModel.splitTriggers(createAlarmIntervalItems(), (i) => i.value)\n\t\tconst textFieldAttrs: Array<TextFieldAttrs> = taken.map((a) => ({\n\t\t\tvalue: a.name,\n\t\t\tlabel: \"emptyString_msg\",\n\t\t\tdisabled: true,\n\t\t\tinjectionsRight: () =>\n\t\t\t\tm(IconButton, {\n\t\t\t\t\ttitle: \"delete_action\",\n\t\t\t\t\ticon: Icons.Cancel,\n\t\t\t\t\tclick: () => alarmModel.removeAlarm(a.value),\n\t\t\t\t}),\n\t\t}))\n\n\t\tif (available.length > 0) {\n\t\t\ttextFieldAttrs.push({\n\t\t\t\tvalue: lang.get(\"add_action\"),\n\t\t\t\tlabel: \"emptyString_msg\",\n\t\t\t\tdisabled: true,\n\t\t\t\tinjectionsRight: () =>\n\t\t\t\t\tm(\n\t\t\t\t\t\tIconButton,\n\t\t\t\t\t\tattachDropdown({\n\t\t\t\t\t\t\tmainButtonAttrs: {\n\t\t\t\t\t\t\t\ttitle: \"add_action\",\n\t\t\t\t\t\t\t\ticon: Icons.Add,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tchildAttrs: () =>\n\t\t\t\t\t\t\t\tavailable.map((i) => ({\n\t\t\t\t\t\t\t\t\tlabel: () => i.name,\n\t\t\t\t\t\t\t\t\tclick: () => alarmModel.addAlarm(i.value),\n\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t})\n\t\t}\n\n\t\ttextFieldAttrs[0].label = \"reminderBeforeEvent_label\"\n\n\t\treturn m(\n\t\t\t\".flex.col.flex-half.pl-s\",\n\t\t\ttextFieldAttrs.map((a) => m(TextField, a)),\n\t\t)\n\t}\n\n\tprivate renderLocationField(vnode: Vnode<CalendarEventEditViewAttrs>): Children {\n\t\tconst { model } = vnode.attrs\n\t\treturn m(TextField, {\n\t\t\tlabel: \"location_label\",\n\t\t\tvalue: model.editModels.location.content,\n\t\t\toninput: (v) => (model.editModels.location.content = v),\n\t\t\tdisabled: model.eventType !== EventType.OWN && model.eventType !== EventType.SHARED_RW,\n\t\t\tclass: \"text pt-s\", // override default pt with pt-s because calendar color indicator takes up some space\n\t\t\tinjectionsRight: () => {\n\t\t\t\tlet address = encodeURIComponent(model.editModels.location.content)\n\n\t\t\t\tif (address === \"\") {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\n\t\t\t\treturn m(IconButton, {\n\t\t\t\t\ttitle: \"showAddress_alt\",\n\t\t\t\t\ticon: Icons.Pin,\n\t\t\t\t\tsize: ButtonSize.Compact,\n\t\t\t\t\tclick: () => {\n\t\t\t\t\t\twindow.open(`https://www.openstreetmap.org/search?query=${address}`, \"_blank\")\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t},\n\t\t})\n\t}\n\n\tprivate renderDescriptionEditor(vnode: Vnode<CalendarEventEditViewAttrs>): Children {\n\t\tconst { model } = vnode.attrs\n\t\tvnode.attrs.descriptionEditor.setEnabled(model.eventType === EventType.OWN || model.eventType === EventType.SHARED_RW)\n\t\treturn m(vnode.attrs.descriptionEditor)\n\t}\n}\n","/**\n * This file contains the functions used to set up and tear down edit dialogs for calendar events.\n *\n * they're not responsible for upholding invariants or ensure valid events (CalendarEventModel.editModels\n * and CalendarEventEditView do that), but know what additional information to ask the user before saving\n * and which methods to call to save the changes.\n */\n\nimport { Dialog } from \"../../../gui/base/Dialog.js\"\nimport { lang } from \"../../../misc/LanguageViewModel.js\"\nimport { ButtonType } from \"../../../gui/base/Button.js\"\nimport { Keys } from \"../../../api/common/TutanotaConstants.js\"\nimport { getStartOfTheWeekOffsetForUser, getTimeFormatForUser } from \"../../date/CalendarUtils.js\"\nimport { client } from \"../../../misc/ClientDetector.js\"\nimport type { DialogHeaderBarAttrs } from \"../../../gui/base/DialogHeaderBar.js\"\nimport { assertNotNull, noOp, Thunk } from \"@tutao/tutanota-utils\"\nimport { PosRect } from \"../../../gui/base/Dropdown.js\"\nimport { Mail } from \"../../../api/entities/tutanota/TypeRefs.js\"\nimport type { HtmlEditor } from \"../../../gui/editor/HtmlEditor.js\"\nimport { locator } from \"../../../api/main/MainLocator.js\"\nimport { CalendarEventEditView } from \"./CalendarEventEditView.js\"\nimport { askIfShouldSendCalendarUpdatesToAttendees } from \"../CalendarGuiUtils.js\"\nimport { UserError } from \"../../../api/main/UserError.js\"\nimport { showPlanUpgradeRequiredDialog } from \"../../../misc/SubscriptionDialogs.js\"\nimport { showUserError } from \"../../../misc/ErrorHandlerImpl.js\"\nimport { CalendarEventIdentity, CalendarEventModel, EventSaveResult, EventType } from \"../../date/eventeditor/CalendarEventModel.js\"\nimport { ProgrammingError } from \"../../../api/common/error/ProgrammingError.js\"\nimport { UpgradeRequiredError } from \"../../../api/main/UpgradeRequiredError.js\"\n\n/**\n * which parts of a calendar event series to apply an edit operation to.\n * consumers must take care to only use appropriate values for the operation\n * in question (ie removing a repeat rule from a single event in a series is nonsensical)\n */\nexport const enum CalendarEventEditMode {\n\t/** only apply the edit to only one particular instance of the series */\n\tThis,\n\t/** edit the whole series */\n\tAll,\n\t/** apply the edit to every instance from the edited one out */\n\tThisAndFuture,\n\t/** don't apply the edit at all */\n\tCancel,\n}\n\nconst enum ConfirmationResult {\n\tCancel,\n\tContinue,\n}\n\ntype EditDialogOkHandler = (posRect: PosRect, finish: Thunk) => Promise<unknown>\n\n/**\n * the generic way to open any calendar edit dialog. the caller should know what to do after the\n * dialog is closed.\n */\nasync function showCalendarEventEditDialog(model: CalendarEventModel, responseMail: Mail | null, handler: EditDialogOkHandler): Promise<void> {\n\tconst recipientsSearch = await locator.recipientsSearchModel()\n\tconst { HtmlEditor } = await import(\"../../../gui/editor/HtmlEditor.js\")\n\tconst groupColors: Map<Id, string> = locator.logins.getUserController().userSettingsGroupRoot.groupSettings.reduce((acc, gc) => {\n\t\tacc.set(gc.group, gc.color)\n\t\treturn acc\n\t}, new Map())\n\tconst descriptionEditor: HtmlEditor = new HtmlEditor(\"description_label\")\n\t\t.setMinHeight(400)\n\t\t.showBorders()\n\t\t.setEnabled(true)\n\t\t// We only set it once, we don't viewModel on every change, that would be slow\n\t\t.setValue(model.editModels.description.content)\n\t\t.setToolbarOptions({\n\t\t\talignmentEnabled: false,\n\t\t\tfontSizeEnabled: false,\n\t\t})\n\t\t.enableToolbar()\n\n\tconst okAction = (dom: HTMLElement) => {\n\t\tmodel.editModels.description.content = descriptionEditor.getTrimmedValue()\n\t\thandler(dom.getBoundingClientRect(), () => dialog.close())\n\t}\n\n\tlet headerDom: HTMLElement | null = null\n\tconst heading = () => {\n\t\tconst summary = model.editModels.summary.content\n\t\treturn summary.trim().length > 0 ? summary : lang.get(\"createEvent_label\")\n\t}\n\tconst dialogHeaderBarAttrs: DialogHeaderBarAttrs = {\n\t\tleft: [\n\t\t\t{\n\t\t\t\tlabel: \"cancel_action\",\n\t\t\t\tclick: () => dialog.close(),\n\t\t\t\ttype: ButtonType.Secondary,\n\t\t\t},\n\t\t],\n\t\tmiddle: heading,\n\t\tright: [\n\t\t\t{\n\t\t\t\tlabel: \"save_action\",\n\t\t\t\tclick: (event, dom) => okAction(dom),\n\t\t\t\ttype: ButtonType.Primary,\n\t\t\t},\n\t\t],\n\t\tcreate: (dom) => {\n\t\t\theaderDom = dom\n\t\t},\n\t}\n\tconst dialog: Dialog = Dialog.editDialog(dialogHeaderBarAttrs, CalendarEventEditView, {\n\t\tmodel,\n\t\trecipientsSearch,\n\t\tdescriptionEditor,\n\t\tstartOfTheWeekOffset: getStartOfTheWeekOffsetForUser(locator.logins.getUserController().userSettingsGroupRoot),\n\t\ttimeFormat: getTimeFormatForUser(locator.logins.getUserController().userSettingsGroupRoot),\n\t\tgroupColors,\n\t})\n\t\t.addShortcut({\n\t\t\tkey: Keys.ESC,\n\t\t\texec: () => dialog.close(),\n\t\t\thelp: \"close_alt\",\n\t\t})\n\t\t.addShortcut({\n\t\t\tkey: Keys.S,\n\t\t\tctrl: true,\n\t\t\texec: () => okAction(assertNotNull(headerDom, \"headerDom was null\")),\n\t\t\thelp: \"save_action\",\n\t\t})\n\n\tif (client.isMobileDevice()) {\n\t\t// Prevent focusing text field automatically on mobile. It opens keyboard and you don't see all details.\n\t\tdialog.setFocusOnLoadFunction(noOp)\n\t}\n\tdialog.show()\n}\n\n/**\n * show an edit dialog for an event that does not exist on the server yet (or anywhere else)\n *\n * will unconditionally send invites on save.\n * @param model the calendar event model used to edit and save the event\n */\nexport async function showNewCalendarEventEditDialog(model: CalendarEventModel): Promise<void> {\n\tlet finished = false\n\n\tconst okAction: EditDialogOkHandler = async (posRect, finish) => {\n\t\t/** new event, so we always want to send invites. */\n\t\tmodel.shouldSendUpdates = true\n\t\tif (finished || (await askUserIfInsecurePasswordsAreOk(model)) === ConfirmationResult.Cancel) {\n\t\t\treturn\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await model.saveNewEvent()\n\t\t\tif (result === EventSaveResult.Saved) {\n\t\t\t\tfinished = true\n\t\t\t\tfinish()\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tif (e instanceof UserError) {\n\t\t\t\t// noinspection ES6MissingAwait\n\t\t\t\tshowUserError(e)\n\t\t\t} else if (e instanceof UpgradeRequiredError) {\n\t\t\t\tmodel.canUseInvites = await showPlanUpgradeRequiredDialog(e.plans)\n\t\t\t} else {\n\t\t\t\tthrow e\n\t\t\t}\n\t\t}\n\t}\n\treturn showCalendarEventEditDialog(model, null, okAction)\n}\n\n/**\n * show a dialog that allows to edit a calendar event that already exists.\n *\n * on save, will validate external passwords, account type and user intent before actually saving and sending updates/invites/cancellations.\n *\n * @param model the calendar event model used to edit & save the event\n * @param identity the identity of the event to edit\n * @param responseMail a mail containing an invite and/or update for this event in case we need to reply to the organizer\n */\nexport async function showExistingCalendarEventEditDialog(\n\tmodel: CalendarEventModel,\n\tidentity: CalendarEventIdentity,\n\tresponseMail: Mail | null = null,\n): Promise<void> {\n\tlet finished = false\n\n\tif (identity.uid == null) {\n\t\tthrow new ProgrammingError(\"tried to edit existing event without uid, this is impossible for certain edit operations.\")\n\t}\n\n\tconst okAction: EditDialogOkHandler = async (posRect, finish) => {\n\t\tif (\n\t\t\tfinished ||\n\t\t\t(await askUserIfUpdatesAreNeededOrCancel(model)) === ConfirmationResult.Cancel ||\n\t\t\t(await askUserIfInsecurePasswordsAreOk(model)) === ConfirmationResult.Cancel\n\t\t) {\n\t\t\treturn\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await model.updateExistingEvent()\n\t\t\tif (result === EventSaveResult.Saved) {\n\t\t\t\tfinished = true\n\t\t\t\tfinish()\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tif (e instanceof UserError) {\n\t\t\t\t// noinspection ES6MissingAwait\n\t\t\t\tshowUserError(e)\n\t\t\t} else if (e instanceof UpgradeRequiredError) {\n\t\t\t\tmodel.canUseInvites = await showPlanUpgradeRequiredDialog(e.plans)\n\t\t\t} else {\n\t\t\t\tthrow e\n\t\t\t}\n\t\t}\n\t}\n\tawait showCalendarEventEditDialog(model, responseMail, okAction)\n}\n\n/** if there are update worthy changes on the model, ask the user what to do with them.\n * @returns {ConfirmationResult} Cancel if the whole process should be cancelled, Continue if the user selected whether to send updates and the saving\n * should proceed.\n * */\nasync function askUserIfUpdatesAreNeededOrCancel(model: CalendarEventModel): Promise<ConfirmationResult> {\n\tif (\n\t\tmodel.eventType === EventType.OWN &&\n\t\t!model.shouldSendUpdates &&\n\t\tmodel.editModels.whoModel.initiallyHadOtherAttendees &&\n\t\t(await model.hasUpdateWorthyChanges())\n\t) {\n\t\tswitch (await askIfShouldSendCalendarUpdatesToAttendees()) {\n\t\t\tcase \"yes\":\n\t\t\t\tmodel.shouldSendUpdates = true\n\t\t\t\tbreak\n\t\t\tcase \"no\":\n\t\t\t\tbreak\n\t\t\tcase \"cancel\":\n\t\t\t\tconsole.log(\"not saving event: user cancelled update sending.\")\n\t\t\t\treturn ConfirmationResult.Cancel\n\t\t}\n\t}\n\n\treturn ConfirmationResult.Continue\n}\n\n/** if {@param model} is set to send updates but has insecure external passwords, ask the user what the action to resolve this should be.\n * @returns {ConfirmationResult} Cancel if the dialog should stay open, Continue if the save action should proceed despite insecure passwords. */\nasync function askUserIfInsecurePasswordsAreOk(model: CalendarEventModel): Promise<ConfirmationResult> {\n\tif (\n\t\tmodel.shouldSendUpdates && // we want to send updates\n\t\tmodel.editModels.whoModel.hasInsecurePasswords() && // the model declares some of the passwords insecure\n\t\t!(await Dialog.confirm(\"presharedPasswordNotStrongEnough_msg\")) // and the user is not OK with that\n\t) {\n\t\tconsole.log(\"not saving event: insecure passwords.\")\n\t\treturn ConfirmationResult.Cancel\n\t}\n\n\treturn ConfirmationResult.Continue\n}\n","import type { CalendarEvent, CalendarEventAttendee, CalendarRepeatRule, EncryptedMailAddress } from \"../../../api/entities/tutanota/TypeRefs.js\"\nimport { createCalendarEventAttendee, createEncryptedMailAddress } from \"../../../api/entities/tutanota/TypeRefs.js\"\nimport m, { Children, Component, Vnode } from \"mithril\"\nimport { AllIcons, Icon } from \"../../../gui/base/Icon.js\"\nimport { theme } from \"../../../gui/theme.js\"\nimport { BootIcons } from \"../../../gui/base/icons/BootIcons.js\"\nimport { Icons } from \"../../../gui/base/icons/Icons.js\"\nimport {\n\tcreateRepeatRuleFrequencyValues,\n\tformatEventDuration,\n\tgetRepeatEndTimeForDisplay,\n\tgetTimeZone,\n\ticonForAttendeeStatus,\n} from \"../../date/CalendarUtils.js\"\nimport { CalendarAttendeeStatus, EndType, getAttendeeStatus, RepeatPeriod } from \"../../../api/common/TutanotaConstants.js\"\nimport { downcast, memoized } from \"@tutao/tutanota-utils\"\nimport { lang, TranslationKey } from \"../../../misc/LanguageViewModel.js\"\nimport type { RepeatRule } from \"../../../api/entities/sys/TypeRefs.js\"\nimport { cleanMailAddress, findAttendeeInAddresses, isAllDayEvent } from \"../../../api/common/utils/CommonCalendarUtils.js\"\nimport { formatDateWithMonth } from \"../../../misc/Formatter.js\"\nimport { hasError } from \"../../../api/common/utils/ErrorCheckUtils.js\"\nimport { BannerButton, BannerButtonAttrs } from \"../../../gui/base/buttons/BannerButton.js\"\nimport { pureComponent } from \"../../../gui/base/PureComponent.js\"\n\nexport type EventPreviewViewAttrs = {\n\tevent: Omit<CalendarEvent, \"description\">\n\tsanitizedDescription: string | null\n\tparticipation: null | { ownAttendee: CalendarEventAttendee; setParticipation: (status: CalendarAttendeeStatus) => unknown }\n}\n\n/** the buttons enabling the user to view their current participation status on an event and to trigger a change to it, including\n * a mail to the organizer. */\nexport const ReplyButtons = pureComponent((participation: NonNullable<EventPreviewViewAttrs[\"participation\"]>) => {\n\tconst colors = {\n\t\tborderColor: theme.content_button,\n\t\tcolor: theme.content_fg,\n\t}\n\n\tconst highlightColors = {\n\t\tborderColor: theme.content_accent,\n\t\tcolor: theme.content_accent,\n\t}\n\n\tconst makeStatusButtonAttrs = (status: CalendarAttendeeStatus, text: TranslationKey): BannerButtonAttrs =>\n\t\tObject.assign({ text, click: () => participation.setParticipation(status) }, participation.ownAttendee.status === status ? highlightColors : colors)\n\n\treturn m(\".flex.col\", [\n\t\tm(\".small\", lang.get(\"invitedToEvent_msg\")),\n\t\tm(\".flex.items-center.mt-s\", [\n\t\t\tm(BannerButton, makeStatusButtonAttrs(CalendarAttendeeStatus.ACCEPTED, \"yes_label\")),\n\t\t\tm(BannerButton, makeStatusButtonAttrs(CalendarAttendeeStatus.TENTATIVE, \"maybe_label\")),\n\t\t\tm(BannerButton, makeStatusButtonAttrs(CalendarAttendeeStatus.DECLINED, \"no_label\")),\n\t\t]),\n\t])\n})\n\nexport class EventPreviewView implements Component<EventPreviewViewAttrs> {\n\t// Cache the parsed URL so we don't parse the URL on every single view call\n\tprivate readonly getLocationUrl: typeof getLocationUrl\n\n\tconstructor() {\n\t\tthis.getLocationUrl = memoized(getLocationUrl)\n\t}\n\n\tview(vnode: Vnode<EventPreviewViewAttrs>): Children {\n\t\tconst { event, sanitizedDescription, participation } = vnode.attrs\n\t\tconst attendees = prepareAttendees(event.attendees, event.organizer)\n\n\t\treturn m(\".flex.col\", [\n\t\t\tm(\".flex.col.smaller\", [\n\t\t\t\tm(\".flex.pb-s.items-center\", [this.renderSectionIndicator(BootIcons.Calendar), m(\".h3.selectable.text-break\", event.summary)]),\n\t\t\t\tm(\".flex.pb-s\", [\n\t\t\t\t\tthis.renderSectionIndicator(Icons.Time),\n\t\t\t\t\tm(\".align-self-center.selectable.flex-column\", [\n\t\t\t\t\t\tm(\"\", formatEventDuration(event, getTimeZone(), false)),\n\t\t\t\t\t\tthis.renderRepeatRule(event.repeatRule, isAllDayEvent(event)),\n\t\t\t\t\t]),\n\t\t\t\t]),\n\t\t\t\tthis.renderLocation(event.location),\n\t\t\t\tthis.renderAttendeesSection(attendees, participation),\n\t\t\t\tthis.renderAttendanceSection(event, attendees, participation),\n\t\t\t\tthis.renderDescription(sanitizedDescription),\n\t\t\t]),\n\t\t])\n\t}\n\n\tprivate renderSectionIndicator(icon: AllIcons, style: Record<string, any> = {}): Children {\n\t\treturn m(\n\t\t\t\".pr\",\n\t\t\tm(Icon, {\n\t\t\t\ticon,\n\t\t\t\tlarge: true,\n\t\t\t\tstyle: Object.assign(\n\t\t\t\t\t{\n\t\t\t\t\t\tfill: theme.content_button,\n\t\t\t\t\t\tdisplay: \"block\",\n\t\t\t\t\t},\n\t\t\t\t\tstyle,\n\t\t\t\t),\n\t\t\t}),\n\t\t)\n\t}\n\n\tprivate renderRepeatRule(rule: CalendarRepeatRule | null, isAllDay: boolean): Children {\n\t\tif (rule == null) return null\n\n\t\tconst frequency = formatRepetitionFrequency(rule)\n\n\t\tif (frequency) {\n\t\t\treturn m(\"\", frequency + formatRepetitionEnd(rule, isAllDay))\n\t\t} else {\n\t\t\t// If we cannot properly process the frequency we just indicate that the event is part of a series.\n\t\t\treturn m(\"\", lang.get(\"unknownRepetition_msg\"))\n\t\t}\n\t}\n\n\tprivate renderLocation(location: string | null): Children {\n\t\tif (location == null || location.trim().length === 0) return null\n\t\treturn m(\".flex.pb-s.items-center\", [\n\t\t\tthis.renderSectionIndicator(Icons.Pin),\n\t\t\tm(\n\t\t\t\t\".text-ellipsis.selectable\",\n\t\t\t\tm(\n\t\t\t\t\t\"a\",\n\t\t\t\t\t{\n\t\t\t\t\t\thref: this.getLocationUrl(location.trim()).toString(),\n\t\t\t\t\t\ttarget: \"_blank\",\n\t\t\t\t\t\trel: \"noopener noreferrer\",\n\t\t\t\t\t},\n\t\t\t\t\tlocation,\n\t\t\t\t),\n\t\t\t),\n\t\t])\n\t}\n\n\tprivate renderAttendeesSection(attendees: Array<CalendarEventAttendee>, participation: EventPreviewViewAttrs[\"participation\"]): Children {\n\t\tif (attendees.length === 0) return null\n\t\treturn m(\".flex.pb-s\", [\n\t\t\tthis.renderSectionIndicator(Icons.People),\n\t\t\tm(\n\t\t\t\t\".flex-wrap\",\n\t\t\t\tattendees.map((a) => this.renderAttendee(a, participation)),\n\t\t\t),\n\t\t])\n\t}\n\n\t/**\n\t * if we're an attendee of this event, this renders a selector to be able to set our own attendance.\n\t * @param event if the event is not in a calendar, we don't want to set our attendance from here.\n\t * @param attendees list of attendees (including the organizer)\n\t * @param participation\n\t * @private\n\t */\n\tprivate renderAttendanceSection(\n\t\tevent: EventPreviewViewAttrs[\"event\"],\n\t\tattendees: Array<CalendarEventAttendee>,\n\t\tparticipation: EventPreviewViewAttrs[\"participation\"],\n\t): Children {\n\t\tif (attendees.length === 0 || participation == null || event._ownerGroup == null) return null\n\t\treturn m(\".flex.pb-s\", [this.renderSectionIndicator(BootIcons.Contacts), m(ReplyButtons, participation)])\n\t}\n\n\tprivate renderAttendee(attendee: CalendarEventAttendee, participation: EventPreviewViewAttrs[\"participation\"]): Children {\n\t\tconst attendeeField = hasError(attendee.address) ? lang.get(\"corruptedValue_msg\") : attendee.address.address\n\t\t/** we might have a more current local attendance for ourselves. */\n\t\tconst status =\n\t\t\tparticipation != null && cleanMailAddress(attendee.address.address) === participation.ownAttendee.address.address\n\t\t\t\t? getAttendeeStatus(participation.ownAttendee)\n\t\t\t\t: getAttendeeStatus(attendee)\n\n\t\treturn m(\".flex.items-center\", [\n\t\t\tm(Icon, {\n\t\t\t\ticon: iconForAttendeeStatus[status],\n\t\t\t\tstyle: {\n\t\t\t\t\tfill: theme.content_fg,\n\t\t\t\t},\n\t\t\t\tclass: \"mr-s\",\n\t\t\t}),\n\t\t\tm(\".span.line-break-anywhere.selectable\", attendeeField),\n\t\t])\n\t}\n\n\tprivate renderDescription(sanitizedDescription: string | null) {\n\t\tif (sanitizedDescription == null || sanitizedDescription.length === 0) return null\n\t\treturn m(\".flex.pb-s.items-start\", [\n\t\t\tthis.renderSectionIndicator(Icons.AlignLeft, {\n\t\t\t\tmarginTop: \"2px\",\n\t\t\t}),\n\t\t\tm(\".full-width.selectable.text-break\", m.trust(sanitizedDescription)),\n\t\t])\n\t}\n}\n\n/**\n * if text is a valid absolute url, then returns a URL with text as the href\n * otherwise passes text as the search parameter for open street map\n * @param text\n * @returns {*}\n */\nfunction getLocationUrl(text: string): URL {\n\tconst osmHref = `https://www.openstreetmap.org/search?query=${text}`\n\tlet url\n\n\ttry {\n\t\t// if not a valid _absolute_ url then we get an exception\n\t\turl = new URL(text)\n\t} catch {\n\t\turl = new URL(osmHref)\n\t}\n\n\treturn url\n}\n\nfunction formatRepetitionFrequency(repeatRule: RepeatRule): string | null {\n\tif (repeatRule.interval === \"1\") {\n\t\tconst frequency = createRepeatRuleFrequencyValues().find((frequency) => frequency.value === repeatRule.frequency)\n\n\t\tif (frequency) {\n\t\t\treturn frequency.name\n\t\t}\n\t} else {\n\t\treturn lang.get(\"repetition_msg\", {\n\t\t\t\"{interval}\": repeatRule.interval,\n\t\t\t\"{timeUnit}\": getFrequencyTimeUnit(downcast(repeatRule.frequency)),\n\t\t})\n\t}\n\n\treturn null\n}\n\n/**\n * @returns {string} The returned string includes a leading separator (\", \" or \" \").\n */\nfunction formatRepetitionEnd(repeatRule: RepeatRule, isAllDay: boolean): string {\n\tswitch (repeatRule.endType) {\n\t\tcase EndType.Count:\n\t\t\tif (!repeatRule.endValue) {\n\t\t\t\treturn \"\"\n\t\t\t}\n\n\t\t\treturn (\n\t\t\t\t\", \" +\n\t\t\t\tlang.get(\"times_msg\", {\n\t\t\t\t\t\"{amount}\": repeatRule.endValue,\n\t\t\t\t})\n\t\t\t)\n\n\t\tcase EndType.UntilDate:\n\t\t\tconst repeatEndTime = getRepeatEndTimeForDisplay(repeatRule, isAllDay, getTimeZone())\n\t\t\treturn \" \" + lang.get(\"until_label\") + \" \" + formatDateWithMonth(repeatEndTime)\n\n\t\tdefault:\n\t\t\treturn \"\"\n\t}\n}\n\nfunction getFrequencyTimeUnit(frequency: RepeatPeriod): string {\n\tswitch (frequency) {\n\t\tcase RepeatPeriod.DAILY:\n\t\t\treturn lang.get(\"days_label\")\n\n\t\tcase RepeatPeriod.WEEKLY:\n\t\t\treturn lang.get(\"weeks_label\")\n\n\t\tcase RepeatPeriod.MONTHLY:\n\t\t\treturn lang.get(\"pricing.months_label\")\n\n\t\tcase RepeatPeriod.ANNUALLY:\n\t\t\treturn lang.get(\"years_label\")\n\n\t\tdefault:\n\t\t\tthrow new Error(\"Unknown calendar event repeat rule frequency: \" + frequency)\n\t}\n}\n\nfunction prepareAttendees(attendees: Array<CalendarEventAttendee>, organizer: EncryptedMailAddress | null): Array<CalendarEventAttendee> {\n\t// We copy the attendees array so that we can add the organizer, in the case that they are not already in attendees\n\t// This is just for display purposes. We need to copy because event.attendees is the source of truth for the event\n\t// so we can't modify it\n\tconst attendeesCopy = attendees.slice()\n\n\tif (organizer != null && attendeesCopy.length > 0 && !findAttendeeInAddresses(attendeesCopy, [organizer.address])) {\n\t\tattendeesCopy.unshift(\n\t\t\tcreateCalendarEventAttendee({\n\t\t\t\taddress: createEncryptedMailAddress({\n\t\t\t\t\taddress: organizer.address,\n\t\t\t\t}),\n\t\t\t\tstatus: CalendarAttendeeStatus.ADDED, // We don't know whether the organizer will be attending or not in this case\n\t\t\t}),\n\t\t)\n\t}\n\n\treturn attendeesCopy\n}\n","import type { Shortcut } from \"../../../misc/KeyManager.js\"\nimport m, { Children } from \"mithril\"\nimport { px } from \"../../../gui/size.js\"\nimport { Button, ButtonColor, ButtonType } from \"../../../gui/base/Button.js\"\nimport { Icons } from \"../../../gui/base/icons/Icons.js\"\nimport type { ModalComponent } from \"../../../gui/base/Modal.js\"\nimport { modal } from \"../../../gui/base/Modal.js\"\nimport { EventPreviewView } from \"./EventPreviewView.js\"\nimport { Dialog } from \"../../../gui/base/Dialog.js\"\nimport { createAsyncDropdown, DROPDOWN_MARGIN, PosRect, showDropdown } from \"../../../gui/base/Dropdown.js\"\nimport { Keys } from \"../../../api/common/TutanotaConstants.js\"\nimport type { HtmlSanitizer } from \"../../../misc/HtmlSanitizer.js\"\nimport { prepareCalendarDescription } from \"../../date/CalendarUtils.js\"\nimport { BootIcons } from \"../../../gui/base/icons/BootIcons.js\"\nimport { IconButton } from \"../../../gui/base/IconButton.js\"\nimport { CalendarEventPopupViewModel } from \"./CalendarEventPopupViewModel.js\"\n\nimport { EventType } from \"../../date/eventeditor/CalendarEventModel.js\"\n\n/**\n * small modal displaying all relevant information about an event in a compact fashion. offers limited editing capabilities to participants in the\n * form of quick response buttons to set attendance and adding exclusions.\n *\n * It is the first line of defense against invalid edit operations since it's the only way to open a\n * calendar editor for an event that's not brand new or to delete an event/parts of it.\n *\n * it is also responsible for divining the users intent before starting any operations so we can make sure we have the right\n * information available, like when editing all event occurrences or only some.\n */\nexport class CalendarEventPopup implements ModalComponent {\n\tprivate readonly _shortcuts: Shortcut[] = []\n\tprivate readonly sanitizedDescription: string\n\tprivate dom: HTMLElement | null = null\n\n\t/**\n\t * @param model\n\t * @param eventBubbleRect the rect where the event bubble was displayed that was clicked (if any)\n\t * @param htmlSanitizer\n\t */\n\tconstructor(private readonly model: CalendarEventPopupViewModel, private readonly eventBubbleRect: PosRect, htmlSanitizer: HtmlSanitizer) {\n\t\t// We receive the HtmlSanitizer from outside and do the sanitization inside, so that we don't have to just assume it was already done\n\t\tthis.sanitizedDescription = prepareCalendarDescription(\n\t\t\tmodel.calendarEvent.description,\n\t\t\t(s) =>\n\t\t\t\thtmlSanitizer.sanitizeHTML(s, {\n\t\t\t\t\tblockExternalContent: true,\n\t\t\t\t}).html,\n\t\t)\n\n\t\tthis.setupShortcuts()\n\t\tthis.view = this.view.bind(this)\n\t}\n\n\tprivate readonly handleDeleteButtonClick: (ev: MouseEvent, receiver: HTMLElement) => void = async (ev: MouseEvent, receiver: HTMLElement) => {\n\t\tif (await this.model.isRepeatingForDeleting()) {\n\t\t\tcreateAsyncDropdown({\n\t\t\t\tlazyButtons: () =>\n\t\t\t\t\tPromise.resolve([\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlabel: \"deleteSingleEventRecurrence_action\",\n\t\t\t\t\t\t\tclick: async () => {\n\t\t\t\t\t\t\t\tawait this.model.deleteSingle()\n\t\t\t\t\t\t\t\tthis.close()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlabel: \"deleteAllEventRecurrence_action\",\n\t\t\t\t\t\t\tclick: () => this.confirmDeleteClose(),\n\t\t\t\t\t\t},\n\t\t\t\t\t]),\n\t\t\t\twidth: 300,\n\t\t\t})(ev, receiver)\n\t\t} else {\n\t\t\t// noinspection JSIgnoredPromiseFromCall\n\t\t\tthis.confirmDeleteClose()\n\t\t}\n\t}\n\n\tprivate readonly handleEditButtonClick: (ev: MouseEvent, receiver: HTMLElement) => void = (ev: MouseEvent, receiver: HTMLElement) => {\n\t\tif (this.model.isRepeatingForEditing) {\n\t\t\tcreateAsyncDropdown({\n\t\t\t\tlazyButtons: () =>\n\t\t\t\t\tPromise.resolve([\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlabel: \"updateOneCalendarEvent_action\",\n\t\t\t\t\t\t\tclick: () => {\n\t\t\t\t\t\t\t\tthis.model.editSingle()\n\t\t\t\t\t\t\t\tthis.close()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlabel: \"updateAllCalendarEvents_action\",\n\t\t\t\t\t\t\tclick: () => {\n\t\t\t\t\t\t\t\tthis.model.editAll()\n\t\t\t\t\t\t\t\tthis.close()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t]),\n\t\t\t\twidth: 300,\n\t\t\t})(ev, receiver)\n\t\t} else {\n\t\t\t// noinspection JSIgnoredPromiseFromCall\n\t\t\tthis.model.editAll()\n\t\t\tthis.close()\n\t\t}\n\t}\n\n\t// we handle askForUpdates here to avoid making a request if not necessary\n\tprivate readonly handleSendUpdatesClick: () => void = async () => {\n\t\tconst confirmed = await Dialog.confirm(\"sendUpdates_msg\")\n\t\tif (confirmed) await this.model.sendUpdates()\n\t\tthis.close()\n\t}\n\n\tview(): Children {\n\t\treturn m(\n\t\t\t\".abs.elevated-bg.plr.pb.border-radius.dropdown-shadow.flex.flex-column\",\n\t\t\t{\n\t\t\t\tstyle: {\n\t\t\t\t\t// minus margin, need to apply it now to not overflow later\n\t\t\t\t\twidth: px(Math.min(window.innerWidth - DROPDOWN_MARGIN * 2, 400)),\n\t\t\t\t\t// see hack description below\n\t\t\t\t\topacity: \"0\",\n\t\t\t\t\t// because calendar event bubbles have 1px border, we want to align\n\t\t\t\t\tmargin: \"1px\",\n\t\t\t\t},\n\t\t\t\toncreate: (vnode) => {\n\t\t\t\t\tthis.dom = vnode.dom as HTMLElement\n\t\t\t\t\t// This is a hack to get \"natural\" view size but render it without opacity first and then show dropdown with inferred\n\t\t\t\t\t// size.\n\t\t\t\t\tsetTimeout(() => showDropdown(this.eventBubbleRect, this.dom!, this.dom!.offsetHeight, 400), 24)\n\t\t\t\t},\n\t\t\t},\n\t\t\t[\n\t\t\t\tm(\".flex.flex-end\", [this.renderSendUpdateButton(), this.renderEditButton(), this.renderDeleteButton(), this.renderCloseButton()]),\n\t\t\t\tm(\".flex-grow.scroll.visible-scrollbar\", [\n\t\t\t\t\tm(EventPreviewView, {\n\t\t\t\t\t\tevent: this.model.calendarEvent,\n\t\t\t\t\t\tsanitizedDescription: this.sanitizedDescription,\n\t\t\t\t\t\tparticipation:\n\t\t\t\t\t\t\tthis.model.ownAttendee != null && this.model.eventType === EventType.INVITE\n\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\townAttendee: this.model.ownAttendee,\n\t\t\t\t\t\t\t\t\t\tsetParticipation: async (status) => {\n\t\t\t\t\t\t\t\t\t\t\tawait this.model.setOwnAttendance(status)\n\t\t\t\t\t\t\t\t\t\t\t// closing this since repeated edit operations from the popup would always\n\t\t\t\t\t\t\t\t\t\t\t// use the version of the event the popup was opened with.\n\t\t\t\t\t\t\t\t\t\t\tthis.close()\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t  }\n\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t}),\n\t\t\t\t]),\n\t\t\t],\n\t\t)\n\t}\n\n\tprivate renderEditButton(): Children {\n\t\tif (!this.model.canEdit) return null\n\t\treturn m(IconButton, { title: \"edit_action\", icon: Icons.Edit, colors: ButtonColor.DrawerNav, click: this.handleEditButtonClick })\n\t}\n\n\tprivate renderDeleteButton(): Children {\n\t\tif (!this.model.canDelete) return null\n\t\treturn m(IconButton, { title: \"delete_action\", icon: Icons.Trash, colors: ButtonColor.DrawerNav, click: this.handleDeleteButtonClick })\n\t}\n\n\tprivate renderSendUpdateButton(): Children {\n\t\tif (!this.model.canSendUpdates) return null\n\t\treturn m(Button, {\n\t\t\tlabel: \"sendUpdates_label\",\n\t\t\tclick: () => this.handleSendUpdatesClick(),\n\t\t\ttype: ButtonType.ActionLarge,\n\t\t\ticon: () => BootIcons.Mail,\n\t\t\tcolors: ButtonColor.DrawerNav,\n\t\t})\n\t}\n\n\tprivate renderCloseButton(): Children {\n\t\treturn m(Button, {\n\t\t\tlabel: \"close_alt\",\n\t\t\tclick: () => this.close(),\n\t\t\ttype: ButtonType.ActionLarge,\n\t\t\ticon: () => Icons.Cancel,\n\t\t\tcolors: ButtonColor.DrawerNav,\n\t\t})\n\t}\n\n\tshow() {\n\t\tmodal.display(this, false)\n\t}\n\n\tprivate close() {\n\t\tmodal.remove(this)\n\t}\n\n\tbackgroundClick(e: MouseEvent): void {\n\t\tmodal.remove(this)\n\t}\n\n\thideAnimation(): Promise<void> {\n\t\treturn Promise.resolve()\n\t}\n\n\tonClose(): void {\n\t\tthis.close()\n\t}\n\n\tshortcuts(): Shortcut[] {\n\t\treturn this._shortcuts\n\t}\n\n\tpopState(e: Event): boolean {\n\t\tmodal.remove(this)\n\t\treturn false\n\t}\n\n\tprivate setupShortcuts() {\n\t\tconst close: Shortcut = {\n\t\t\tkey: Keys.ESC,\n\t\t\texec: () => this.close(),\n\t\t\thelp: \"close_alt\",\n\t\t}\n\t\tconst edit: Shortcut = {\n\t\t\tkey: Keys.E,\n\t\t\texec: () => this.handleEditButtonClick(new MouseEvent(\"click\", {}), this.dom!),\n\t\t\thelp: \"edit_action\",\n\t\t}\n\t\tconst sendUpdates: Shortcut = {\n\t\t\tkey: Keys.R,\n\t\t\texec: this.handleSendUpdatesClick,\n\t\t\thelp: \"sendUpdates_label\",\n\t\t}\n\t\tconst remove: Shortcut = {\n\t\t\tkey: Keys.DELETE,\n\t\t\texec: () => this.handleDeleteButtonClick(new MouseEvent(\"click\", {}), this.dom!),\n\t\t\thelp: \"delete_action\",\n\t\t}\n\n\t\tthis._shortcuts.push(close)\n\n\t\tif (this.model.canSendUpdates) {\n\t\t\tthis._shortcuts.push(sendUpdates)\n\t\t}\n\n\t\tif (this.model.canEdit) {\n\t\t\tthis._shortcuts.push(edit)\n\t\t}\n\n\t\tif (this.model.canDelete) {\n\t\t\tthis._shortcuts.push(remove)\n\t\t}\n\t}\n\n\tprivate async confirmDeleteClose(): Promise<void> {\n\t\tif (!(await Dialog.confirm(\"deleteEventConfirmation_msg\"))) return\n\t\tawait this.model.deleteAll()\n\t\tthis.close()\n\t}\n}\n","import { CalendarEvent, CalendarEventAttendee } from \"../../../api/entities/tutanota/TypeRefs.js\"\nimport { calendarEventHasMoreThanOneOccurrencesLeft } from \"../../date/CalendarUtils.js\"\nimport { CalendarEventModel, EventSaveResult, EventType, getNonOrganizerAttendees } from \"../../date/eventeditor/CalendarEventModel.js\"\nimport { NotFoundError } from \"../../../api/common/error/RestError.js\"\nimport { CalendarModel } from \"../../model/CalendarModel.js\"\nimport { CalendarEventEditMode, showExistingCalendarEventEditDialog } from \"../eventeditor/CalendarEventEditDialog.js\"\nimport { ProgrammingError } from \"../../../api/common/error/ProgrammingError.js\"\nimport { CalendarAttendeeStatus } from \"../../../api/common/TutanotaConstants.js\"\nimport m from \"mithril\"\nimport { clone } from \"@tutao/tutanota-utils\"\n\n/**\n * makes decisions about which operations are available from the popup and knows how to implement them depending on the event's type.\n */\nexport class CalendarEventPopupViewModel {\n\treadonly canEdit: boolean\n\treadonly canDelete: boolean\n\treadonly canSendUpdates: boolean\n\t/** for editing, an event that has only one non-deleted instance is still considered repeating\n\t * because we might reschedule that instance and then unexclude some deleted instances.\n\t */\n\treadonly isRepeatingForEditing: boolean\n\n\tprivate processing: boolean = false\n\tprivate readonly _ownAttendee: CalendarEventAttendee | null\n\n\t/**\n\t *\n\t * @param calendarEvent the event to display in the popup\n\t * @param calendarModel the calendar model where the event can be updated/deleted\n\t * @param eventType\n\t * @param hasBusinessFeature if the current user is allowed to do certain operations.\n\t * @param ownAttendee will be cloned to have a copy that's not influencing the actual event but can be changed to quickly update the UI\n\t * @param eventModelFactory\n\t * @param uiUpdateCallback\n\t */\n\tconstructor(\n\t\treadonly calendarEvent: Readonly<CalendarEvent>,\n\t\tprivate readonly calendarModel: CalendarModel,\n\t\treadonly eventType: EventType,\n\t\tprivate readonly hasBusinessFeature: boolean,\n\t\townAttendee: CalendarEventAttendee | null,\n\t\tprivate readonly lazyProgenitor: () => Promise<CalendarEvent>,\n\t\tprivate readonly eventModelFactory: (mode: CalendarEventEditMode) => Promise<CalendarEventModel>,\n\t\tprivate readonly uiUpdateCallback: () => void = m.redraw,\n\t) {\n\t\tthis._ownAttendee = clone(ownAttendee)\n\t\tif (this.calendarEvent._ownerGroup == null) {\n\t\t\tthis.canEdit = false\n\t\t\tthis.canDelete = false\n\t\t\tthis.canSendUpdates = false\n\t\t} else {\n\t\t\t// partially editable (adding alarms) counts as editable.\n\t\t\tthis.canEdit =\n\t\t\t\tthis.eventType === EventType.OWN ||\n\t\t\t\tthis.eventType === EventType.SHARED_RW ||\n\t\t\t\tthis.eventType === EventType.LOCKED ||\n\t\t\t\tthis.eventType === EventType.INVITE\n\t\t\tthis.canDelete = this.canEdit || this.eventType === EventType.INVITE\n\t\t\tthis.canSendUpdates = hasBusinessFeature && this.eventType === EventType.OWN && getNonOrganizerAttendees(calendarEvent).length > 0\n\t\t}\n\n\t\t// we do not edit single instances yet\n\t\tthis.isRepeatingForEditing = false // calendarEvent.repeatRule != null\n\t}\n\n\t/** for deleting, an event that has only one non-deleted instance behaves as if it wasn't repeating\n\t * because deleting the last instance is the same as deleting the whole event from the pov of the user.\n\t */\n\tasync isRepeatingForDeleting(): Promise<boolean> {\n\t\tconst progenitor = await this.lazyProgenitor()\n\t\treturn calendarEventHasMoreThanOneOccurrencesLeft(progenitor)\n\t}\n\n\tget ownAttendee(): CalendarEventAttendee | null {\n\t\treturn this._ownAttendee\n\t}\n\n\tasync setOwnAttendance(status: CalendarAttendeeStatus): Promise<void> {\n\t\tif (this.calendarEvent.organizer == null || this.ownAttendee == null || this.processing || this._ownAttendee?.status === status) return\n\t\tconst oldStatus = this.ownAttendee.status\n\t\tthis.processing = true\n\t\ttry {\n\t\t\tthis.ownAttendee.status = status\n\t\t\tthis.uiUpdateCallback()\n\t\t\tconst model = await this.eventModelFactory(CalendarEventEditMode.All)\n\t\t\tmodel.editModels.whoModel.setOwnAttendance(status)\n\t\t\tawait model.updateExistingEvent()\n\t\t} catch (e) {\n\t\t\tthis.ownAttendee.status = oldStatus\n\t\t\tthrow e\n\t\t} finally {\n\t\t\tthis.processing = false\n\t\t}\n\t}\n\n\t/** add an exclusion for this event instances start time on the original event.\n\t * if this is a rescheduled instance, we will just delete the event because the progenitor already\n\t * has an exclusion for this time.\n\t * */\n\tasync deleteSingle() {\n\t\ttry {\n\t\t\t// passing \"all\" because this is actually an update to the progenitor\n\t\t\tconst model = await this.eventModelFactory(CalendarEventEditMode.All)\n\t\t\tawait model.editModels.whenModel.excludeDate(this.calendarEvent.startTime)\n\t\t\tawait model.updateExistingEvent()\n\t\t} catch (e) {\n\t\t\tif (!(e instanceof NotFoundError)) {\n\t\t\t\tthrow e\n\t\t\t}\n\t\t}\n\t}\n\n\tasync deleteAll(): Promise<void> {\n\t\ttry {\n\t\t\tconst model = await this.eventModelFactory(CalendarEventEditMode.All)\n\t\t\tawait model.deleteEvent()\n\t\t} catch (e) {\n\t\t\tif (!(e instanceof NotFoundError)) {\n\t\t\t\tthrow e\n\t\t\t}\n\t\t}\n\t}\n\n\tasync editSingle() {\n\t\tthrow new ProgrammingError(\"not implemented\")\n\t}\n\n\tasync editAll() {\n\t\tconst model = await this.eventModelFactory(CalendarEventEditMode.All)\n\n\t\ttry {\n\t\t\treturn await showExistingCalendarEventEditDialog(model, {\n\t\t\t\tuid: this.calendarEvent.uid,\n\t\t\t\tsequence: this.calendarEvent.sequence,\n\t\t\t})\n\t\t} catch (err) {\n\t\t\tif (err instanceof NotFoundError) {\n\t\t\t\tconsole.log(\"calendar event not found when clicking on the event\")\n\t\t\t} else {\n\t\t\t\tthrow err\n\t\t\t}\n\t\t}\n\t}\n\n\tasync sendUpdates(): Promise<EventSaveResult> {\n\t\tconst model = await this.eventModelFactory(CalendarEventEditMode.All)\n\t\ttry {\n\t\t\tmodel.shouldSendUpdates = true\n\t\t\tawait model.updateExistingEvent()\n\t\t\treturn EventSaveResult.Saved\n\t\t} finally {\n\t\t\tmodel.shouldSendUpdates = false\n\t\t}\n\t}\n}\n","import m, { Children, Component, Vnode } from \"mithril\"\nimport { IconButton } from \"../../gui/base/IconButton.js\"\nimport { BootIcons } from \"../../gui/base/icons/BootIcons.js\"\nimport { ViewSlider } from \"../../gui/nav/ViewSlider.js\"\nimport { BaseMobileHeader } from \"../../gui/BaseMobileHeader.js\"\nimport { OfflineIndicatorMobile } from \"../../gui/base/OfflineIndicator.js\"\nimport { ProgressBar } from \"../../gui/base/ProgressBar.js\"\nimport { Icons } from \"../../gui/base/icons/Icons.js\"\nimport { CalendarNavConfiguration, CalendarViewType } from \"./CalendarGuiUtils.js\"\nimport { MobileHeaderMenuButton, MobileHeaderTitle } from \"../../gui/MobileHeader.js\"\nimport { AppHeaderAttrs } from \"../../gui/Header.js\"\n\nexport interface CalendarMobileHeaderAttrs extends AppHeaderAttrs {\n\tviewType: CalendarViewType\n\tviewSlider: ViewSlider\n\tnavConfiguration: CalendarNavConfiguration\n\tonBack: () => unknown\n\tonCreateEvent: () => unknown\n}\n\n/**\n * A special header that is used instead of {@link MobileHeader} but just for calendar.\n */\nexport class CalendarMobileHeader implements Component<CalendarMobileHeaderAttrs> {\n\tview({ attrs }: Vnode<CalendarMobileHeaderAttrs>): Children {\n\t\treturn m(BaseMobileHeader, {\n\t\t\tleft:\n\t\t\t\tattrs.viewType === CalendarViewType.DAY || attrs.viewType === CalendarViewType.WEEK\n\t\t\t\t\t? m(IconButton, {\n\t\t\t\t\t\t\ticon: BootIcons.Back,\n\t\t\t\t\t\t\ttitle: \"back_action\",\n\t\t\t\t\t\t\tclick: attrs.onBack,\n\t\t\t\t\t  })\n\t\t\t\t\t: m(MobileHeaderMenuButton, { newsModel: attrs.newsModel, backAction: () => attrs.viewSlider.focusPreviousColumn() }),\n\t\t\tcenter: m(MobileHeaderTitle, {\n\t\t\t\ttitle: attrs.navConfiguration.title,\n\t\t\t\tbottom: m(OfflineIndicatorMobile, attrs.offlineIndicatorModel.getCurrentAttrs()),\n\t\t\t}),\n\t\t\tright: [\n\t\t\t\tattrs.navConfiguration.back,\n\t\t\t\tattrs.navConfiguration.forward,\n\t\t\t\tm(IconButton, {\n\t\t\t\t\ticon: Icons.Add,\n\t\t\t\t\ttitle: \"createEvent_label\",\n\t\t\t\t\tclick: attrs.onCreateEvent,\n\t\t\t\t}),\n\t\t\t],\n\t\t\tinjections: m(ProgressBar, { progress: attrs.offlineIndicatorModel.getProgress() }),\n\t\t})\n\t}\n}\n","import { CalendarNavConfiguration } from \"./CalendarGuiUtils.js\"\nimport m, { Children, Component, Vnode } from \"mithril\"\nimport { theme } from \"../../gui/theme.js\"\n\ntype CalendarDesktopToolbarAttrs = { navConfig: CalendarNavConfiguration }\n\nexport class CalendarDesktopToolbar implements Component<CalendarDesktopToolbarAttrs> {\n\tview({ attrs }: Vnode<CalendarDesktopToolbarAttrs>): Children {\n\t\tconst { navConfig } = attrs\n\t\treturn m(\".flex.row.items-center.content-bg.border-radius-top-left-big\", [\n\t\t\tm(\".flex.pt-xs.pb-xs\", [navConfig.back ?? m(\".button-width-fixed\"), navConfig.forward ?? m(\".button-width-fixed\")]),\n\t\t\tm(\"h1\", navConfig.title),\n\t\t\tnavConfig.week && this.renderWeekNumberLabel(navConfig.week),\n\t\t\tm(\".flex-grow\"),\n\t\t])\n\t}\n\n\tprivate renderWeekNumberLabel(label: string): Children {\n\t\treturn m(\n\t\t\t\".ml-m.small.font-weight-600\",\n\t\t\t{\n\t\t\t\tstyle: {\n\t\t\t\t\tpadding: \"2px 8px\",\n\t\t\t\t\tbackgroundColor: theme.list_alternate_bg,\n\t\t\t\t},\n\t\t\t},\n\t\t\tlabel,\n\t\t)\n\t}\n}\n","import m, { Children, Component, Vnode } from \"mithril\"\nimport { AppHeaderAttrs, Header } from \"../../gui/Header.js\"\nimport { ColumnType, ViewColumn } from \"../../gui/base/ViewColumn\"\nimport { lang } from \"../../misc/LanguageViewModel\"\nimport { ViewSlider } from \"../../gui/nav/ViewSlider.js\"\nimport type { Shortcut } from \"../../misc/KeyManager\"\nimport { keyManager } from \"../../misc/KeyManager\"\nimport { Icons } from \"../../gui/base/icons/Icons\"\nimport { downcast, getStartOfDay, memoized, ofClass } from \"@tutao/tutanota-utils\"\nimport type { CalendarEvent, CalendarEventAttendee, GroupSettings, UserSettingsGroupRoot } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { createGroupSettings } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { defaultCalendarColor, FeatureType, GroupType, Keys, reverse, ShareCapability, TimeFormat } from \"../../api/common/TutanotaConstants\"\nimport { locator } from \"../../api/main/MainLocator\"\nimport { getEventType, getTimeZone, resolveCalendarEventProgenitor, shouldDefaultToAmPmTimeFormat } from \"../date/CalendarUtils\"\nimport { Button, ButtonColor, ButtonType } from \"../../gui/base/Button.js\"\nimport { NavButton, NavButtonColor } from \"../../gui/base/NavButton.js\"\nimport { CalendarMonthView } from \"./CalendarMonthView\"\nimport { DateTime } from \"luxon\"\nimport { NotFoundError } from \"../../api/common/error/RestError\"\nimport { CalendarAgendaView } from \"./CalendarAgendaView\"\nimport type { GroupInfo } from \"../../api/entities/sys/TypeRefs.js\"\nimport { showEditCalendarDialog } from \"./EditCalendarDialog\"\nimport { styles } from \"../../gui/styles\"\nimport { MultiDayCalendarView } from \"./MultiDayCalendarView\"\nimport { Dialog } from \"../../gui/base/Dialog\"\nimport { isApp } from \"../../api/common/Env\"\nimport { px, size } from \"../../gui/size\"\nimport { FolderColumnView } from \"../../gui/FolderColumnView.js\"\nimport { deviceConfig } from \"../../misc/DeviceConfig\"\nimport { exportCalendar, showCalendarImportDialog } from \"../export/CalendarImporterDialog\"\nimport { showNotAvailableForFreeDialog } from \"../../misc/SubscriptionDialogs\"\nimport { getSharedGroupName, hasCapabilityOnGroup, loadGroupMembers } from \"../../sharing/GroupUtils\"\nimport { showGroupSharingDialog } from \"../../sharing/view/GroupSharingDialog\"\nimport { GroupInvitationFolderRow } from \"../../sharing/view/GroupInvitationFolderRow\"\nimport { SidebarSection } from \"../../gui/SidebarSection\"\nimport type { HtmlSanitizer } from \"../../misc/HtmlSanitizer\"\nimport { ProgrammingError } from \"../../api/common/error/ProgrammingError\"\nimport { calendarNavConfiguration, CalendarViewType } from \"./CalendarGuiUtils\"\nimport { CalendarViewModel, MouseOrPointerEvent } from \"./CalendarViewModel\"\nimport { CalendarEventEditMode, showNewCalendarEventEditDialog } from \"./eventeditor/CalendarEventEditDialog.js\"\nimport { CalendarEventPopup } from \"./eventpopup/CalendarEventPopup.js\"\nimport { showProgressDialog } from \"../../gui/dialogs/ProgressDialog\"\nimport type { CalendarInfo } from \"../model/CalendarModel\"\nimport { client } from \"../../misc/ClientDetector\"\nimport type Stream from \"mithril/stream\"\nimport { IconButton } from \"../../gui/base/IconButton.js\"\nimport { createDropdown } from \"../../gui/base/Dropdown.js\"\nimport { ButtonSize } from \"../../gui/base/ButtonSize.js\"\nimport { BottomNav } from \"../../gui/nav/BottomNav.js\"\nimport { DrawerMenuAttrs } from \"../../gui/nav/DrawerMenu.js\"\nimport { BaseTopLevelView } from \"../../gui/BaseTopLevelView.js\"\nimport { TopLevelAttrs, TopLevelView } from \"../../TopLevelView.js\"\nimport { getEnabledMailAddressesWithUser } from \"../../mail/model/MailUtils.js\"\nimport { CalendarEventPopupViewModel } from \"./eventpopup/CalendarEventPopupViewModel.js\"\nimport { isCustomizationEnabledForCustomer } from \"../../api/common/utils/Utils.js\"\nimport { findAttendeeInAddresses, getEventWithDefaultTimes } from \"../../api/common/utils/CommonCalendarUtils.js\"\nimport { BackgroundColumnLayout } from \"../../gui/BackgroundColumnLayout.js\"\nimport { theme } from \"../../gui/theme.js\"\nimport { CalendarMobileHeader } from \"./CalendarMobileHeader.js\"\nimport { CalendarDesktopToolbar } from \"./CalendarDesktopToolbar.js\"\n\nexport type GroupColors = Map<Id, string>\n\nexport interface CalendarViewAttrs extends TopLevelAttrs {\n\tdrawerAttrs: DrawerMenuAttrs\n\theader: AppHeaderAttrs\n\tcalendarViewModel: CalendarViewModel\n}\n\nconst CalendarViewTypeByValue = reverse(CalendarViewType)\n\nexport class CalendarView extends BaseTopLevelView implements TopLevelView<CalendarViewAttrs> {\n\tprivate readonly sidebarColumn: ViewColumn\n\tprivate readonly contentColumn: ViewColumn\n\tprivate readonly viewSlider: ViewSlider\n\tprivate currentViewType: CalendarViewType\n\tprivate readonly viewModel: CalendarViewModel\n\t// For sanitizing event descriptions, which get rendered as html in the CalendarEventPopup\n\tprivate readonly htmlSanitizer: Promise<HtmlSanitizer>\n\toncreate: Component[\"oncreate\"]\n\tonremove: Component[\"onremove\"]\n\n\tconstructor(vnode: Vnode<CalendarViewAttrs>) {\n\t\tsuper()\n\t\tconst userId = locator.logins.getUserController().user._id\n\n\t\tthis.viewModel = vnode.attrs.calendarViewModel\n\t\tthis.currentViewType = deviceConfig.getDefaultCalendarView(userId) || CalendarViewType.MONTH\n\t\tthis.htmlSanitizer = import(\"../../misc/HtmlSanitizer\").then((m) => m.htmlSanitizer)\n\t\tthis.sidebarColumn = new ViewColumn(\n\t\t\t{\n\t\t\t\tview: () =>\n\t\t\t\t\tm(FolderColumnView, {\n\t\t\t\t\t\tdrawer: vnode.attrs.drawerAttrs,\n\t\t\t\t\t\tbutton: styles.isUsingBottomNavigation()\n\t\t\t\t\t\t\t? null\n\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\ttype: ButtonType.FolderColumnHeader,\n\t\t\t\t\t\t\t\t\tlabel: \"newEvent_action\",\n\t\t\t\t\t\t\t\t\tclick: () => this._createNewEventDialog(),\n\t\t\t\t\t\t\t  },\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\tSidebarSection,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tname: \"view_label\",\n\t\t\t\t\t\t\t\t\tbutton:\n\t\t\t\t\t\t\t\t\t\tthis.currentViewType !== CalendarViewType.AGENDA\n\t\t\t\t\t\t\t\t\t\t\t? m(Button, {\n\t\t\t\t\t\t\t\t\t\t\t\t\tlabel: \"today_label\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tclick: () => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tthis._setUrl(m.route.param(\"view\"), new Date())\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tthis.viewSlider.focus(this.contentColumn)\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolors: ButtonColor.Nav,\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype: ButtonType.Primary,\n\t\t\t\t\t\t\t\t\t\t\t  })\n\t\t\t\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tthis._renderCalendarViewButtons(),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\tSidebarSection,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tname: \"yourCalendars_label\",\n\t\t\t\t\t\t\t\t\tbutton: m(IconButton, {\n\t\t\t\t\t\t\t\t\t\ttitle: \"addCalendar_action\",\n\t\t\t\t\t\t\t\t\t\tcolors: ButtonColor.Nav,\n\t\t\t\t\t\t\t\t\t\tclick: () => this._onPressedAddCalendar(),\n\t\t\t\t\t\t\t\t\t\ticon: Icons.Add,\n\t\t\t\t\t\t\t\t\t\tsize: ButtonSize.Compact,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tthis._renderCalendars(false),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\tSidebarSection,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tname: \"otherCalendars_label\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tthis._renderCalendars(true),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tthis.viewModel.calendarInvitations().length > 0\n\t\t\t\t\t\t\t\t? m(\n\t\t\t\t\t\t\t\t\t\tSidebarSection,\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tname: \"calendarInvitations_label\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tthis.viewModel.calendarInvitations().map((invitation) =>\n\t\t\t\t\t\t\t\t\t\t\tm(GroupInvitationFolderRow, {\n\t\t\t\t\t\t\t\t\t\t\t\tinvitation,\n\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t  )\n\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\t],\n\t\t\t\t\t\tariaLabel: \"calendar_label\",\n\t\t\t\t\t}),\n\t\t\t},\n\t\t\tColumnType.Foreground,\n\t\t\tsize.first_col_min_width,\n\t\t\tsize.first_col_max_width,\n\t\t\t() => (this.currentViewType === CalendarViewType.WEEK ? lang.get(\"month_label\") : lang.get(\"calendar_label\")),\n\t\t)\n\t\tconst getGroupColors = memoized((userSettingsGroupRoot: UserSettingsGroupRoot) => {\n\t\t\treturn userSettingsGroupRoot.groupSettings.reduce((acc, gc) => {\n\t\t\t\tacc.set(gc.group, gc.color)\n\t\t\t\treturn acc\n\t\t\t}, new Map())\n\t\t})\n\t\tthis.contentColumn = new ViewColumn(\n\t\t\t{\n\t\t\t\tview: () => {\n\t\t\t\t\tconst groupColors = getGroupColors(locator.logins.getUserController().userSettingsGroupRoot)\n\n\t\t\t\t\tswitch (this.currentViewType) {\n\t\t\t\t\t\tcase CalendarViewType.MONTH:\n\t\t\t\t\t\t\treturn m(BackgroundColumnLayout, {\n\t\t\t\t\t\t\t\tbackgroundColor: theme.navigation_bg,\n\t\t\t\t\t\t\t\tdesktopToolbar: () => this.renderDesktopToolbar(),\n\t\t\t\t\t\t\t\tmobileHeader: () => this.renderMobileHeader(vnode.attrs.header),\n\t\t\t\t\t\t\t\tcolumnLayout: m(CalendarMonthView, {\n\t\t\t\t\t\t\t\t\ttemporaryEvents: this.viewModel.temporaryEvents,\n\t\t\t\t\t\t\t\t\teventsForDays: this.viewModel.eventsForDays,\n\t\t\t\t\t\t\t\t\tgetEventsOnDays: this.viewModel.getEventsOnDays.bind(this.viewModel),\n\t\t\t\t\t\t\t\t\tonEventClicked: (calendarEvent, domEvent) => this._onEventSelected(calendarEvent, domEvent, this.htmlSanitizer),\n\t\t\t\t\t\t\t\t\tonNewEvent: (date) => {\n\t\t\t\t\t\t\t\t\t\tthis._createNewEventDialog(date)\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tselectedDate: this.viewModel.selectedDate(),\n\t\t\t\t\t\t\t\t\tonDateSelected: (date, calendarViewType) => {\n\t\t\t\t\t\t\t\t\t\tthis._setUrl(calendarViewType, date)\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tonChangeMonth: (next) => this._viewPeriod(CalendarViewType.MONTH, next),\n\t\t\t\t\t\t\t\t\tamPmFormat: locator.logins.getUserController().userSettingsGroupRoot.timeFormat === TimeFormat.TWELVE_HOURS,\n\t\t\t\t\t\t\t\t\tstartOfTheWeek: downcast(locator.logins.getUserController().userSettingsGroupRoot.startOfTheWeek),\n\t\t\t\t\t\t\t\t\tgroupColors,\n\t\t\t\t\t\t\t\t\thiddenCalendars: this.viewModel.hiddenCalendars,\n\t\t\t\t\t\t\t\t\tdragHandlerCallbacks: this.viewModel,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\tcase CalendarViewType.DAY:\n\t\t\t\t\t\t\treturn m(BackgroundColumnLayout, {\n\t\t\t\t\t\t\t\tbackgroundColor: theme.navigation_bg,\n\t\t\t\t\t\t\t\tdesktopToolbar: () => this.renderDesktopToolbar(),\n\t\t\t\t\t\t\t\tmobileHeader: () => this.renderMobileHeader(vnode.attrs.header),\n\t\t\t\t\t\t\t\tcolumnLayout: m(MultiDayCalendarView, {\n\t\t\t\t\t\t\t\t\ttemporaryEvents: this.viewModel.temporaryEvents,\n\t\t\t\t\t\t\t\t\tgetEventsOnDays: this.viewModel.getEventsOnDays.bind(this.viewModel),\n\t\t\t\t\t\t\t\t\tdaysInPeriod: 1,\n\t\t\t\t\t\t\t\t\tonEventClicked: (event, domEvent) => this._onEventSelected(event, domEvent, this.htmlSanitizer),\n\t\t\t\t\t\t\t\t\tonNewEvent: (date) => {\n\t\t\t\t\t\t\t\t\t\tthis._createNewEventDialog(date)\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tselectedDate: this.viewModel.selectedDate(),\n\t\t\t\t\t\t\t\t\tonDateSelected: (date) => {\n\t\t\t\t\t\t\t\t\t\tthis.viewModel.selectedDate(date)\n\n\t\t\t\t\t\t\t\t\t\tm.redraw()\n\n\t\t\t\t\t\t\t\t\t\tthis._setUrl(CalendarViewType.DAY, date)\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tgroupColors,\n\t\t\t\t\t\t\t\t\thiddenCalendars: this.viewModel.hiddenCalendars,\n\t\t\t\t\t\t\t\t\tonChangeViewPeriod: (next) => this._viewPeriod(CalendarViewType.DAY, next),\n\t\t\t\t\t\t\t\t\tstartOfTheWeek: downcast(locator.logins.getUserController().userSettingsGroupRoot.startOfTheWeek),\n\t\t\t\t\t\t\t\t\tdragHandlerCallbacks: this.viewModel,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t})\n\n\t\t\t\t\t\tcase CalendarViewType.WEEK:\n\t\t\t\t\t\t\treturn m(BackgroundColumnLayout, {\n\t\t\t\t\t\t\t\tbackgroundColor: theme.navigation_bg,\n\t\t\t\t\t\t\t\tdesktopToolbar: () => this.renderDesktopToolbar(),\n\t\t\t\t\t\t\t\tmobileHeader: () => this.renderMobileHeader(vnode.attrs.header),\n\t\t\t\t\t\t\t\tcolumnLayout: m(MultiDayCalendarView, {\n\t\t\t\t\t\t\t\t\ttemporaryEvents: this.viewModel.temporaryEvents,\n\t\t\t\t\t\t\t\t\tgetEventsOnDays: this.viewModel.getEventsOnDays.bind(this.viewModel),\n\t\t\t\t\t\t\t\t\tdaysInPeriod: 7,\n\t\t\t\t\t\t\t\t\tonEventClicked: (event, domEvent) => this._onEventSelected(event, domEvent, this.htmlSanitizer),\n\t\t\t\t\t\t\t\t\tonNewEvent: (date) => {\n\t\t\t\t\t\t\t\t\t\tthis._createNewEventDialog(date)\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tselectedDate: this.viewModel.selectedDate(),\n\t\t\t\t\t\t\t\t\tonDateSelected: (date, viewType) => {\n\t\t\t\t\t\t\t\t\t\tthis._setUrl(viewType ?? CalendarViewType.WEEK, date)\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tstartOfTheWeek: downcast(locator.logins.getUserController().userSettingsGroupRoot.startOfTheWeek),\n\t\t\t\t\t\t\t\t\tgroupColors,\n\t\t\t\t\t\t\t\t\thiddenCalendars: this.viewModel.hiddenCalendars,\n\t\t\t\t\t\t\t\t\tonChangeViewPeriod: (next) => this._viewPeriod(CalendarViewType.WEEK, next),\n\t\t\t\t\t\t\t\t\tdragHandlerCallbacks: this.viewModel,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t})\n\n\t\t\t\t\t\tcase CalendarViewType.AGENDA:\n\t\t\t\t\t\t\treturn m(BackgroundColumnLayout, {\n\t\t\t\t\t\t\t\tbackgroundColor: theme.navigation_bg,\n\t\t\t\t\t\t\t\tdesktopToolbar: () => this.renderDesktopToolbar(),\n\t\t\t\t\t\t\t\tmobileHeader: () => this.renderMobileHeader(vnode.attrs.header),\n\t\t\t\t\t\t\t\tcolumnLayout: m(CalendarAgendaView, {\n\t\t\t\t\t\t\t\t\teventsForDays: this.viewModel.eventsForDays,\n\t\t\t\t\t\t\t\t\tamPmFormat: shouldDefaultToAmPmTimeFormat(),\n\t\t\t\t\t\t\t\t\tonEventClicked: (event, domEvent) => this._onEventSelected(event, domEvent, this.htmlSanitizer),\n\t\t\t\t\t\t\t\t\tgroupColors,\n\t\t\t\t\t\t\t\t\thiddenCalendars: this.viewModel.hiddenCalendars,\n\t\t\t\t\t\t\t\t\tonDateSelected: (date) => {\n\t\t\t\t\t\t\t\t\t\tthis._setUrl(CalendarViewType.DAY, date)\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t})\n\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tthrow new ProgrammingError(`invalid CalendarViewType: \"${this.currentViewType}\"`)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\tColumnType.Background,\n\t\t\tsize.second_col_min_width + size.third_col_min_width,\n\t\t\tsize.third_col_max_width,\n\t\t)\n\t\tthis.viewSlider = new ViewSlider([this.sidebarColumn, this.contentColumn], \"CalendarView\")\n\n\t\tconst shortcuts = this._setupShortcuts()\n\n\t\tconst streamListeners: Stream<void>[] = []\n\n\t\tthis.oncreate = () => {\n\t\t\tkeyManager.registerShortcuts(shortcuts)\n\t\t\tstreamListeners.push(\n\t\t\t\tthis.viewModel.calendarInvitations.map(() => {\n\t\t\t\t\tm.redraw()\n\t\t\t\t}),\n\t\t\t)\n\t\t\tstreamListeners.push(this.viewModel.redraw.map(m.redraw))\n\t\t}\n\n\t\tthis.onremove = () => {\n\t\t\tkeyManager.unregisterShortcuts(shortcuts)\n\n\t\t\tfor (let listener of streamListeners) {\n\t\t\t\tlistener.end(true)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate renderDesktopToolbar(): Children {\n\t\tconst navConfig = calendarNavConfiguration(this.currentViewType, this.viewModel.selectedDate(), this.viewModel.weekStart, (viewType, next) =>\n\t\t\tthis._viewPeriod(viewType, next),\n\t\t)\n\t\treturn m(CalendarDesktopToolbar, { navConfig })\n\t}\n\n\tprivate renderMobileHeader(header: AppHeaderAttrs) {\n\t\treturn m(CalendarMobileHeader, {\n\t\t\t...header,\n\t\t\tviewType: this.currentViewType,\n\t\t\tviewSlider: this.viewSlider,\n\t\t\tnavConfiguration: calendarNavConfiguration(this.currentViewType, this.viewModel.selectedDate(), this.viewModel.weekStart, (viewType, next) =>\n\t\t\t\tthis._viewPeriod(viewType, next),\n\t\t\t),\n\t\t\tonCreateEvent: () => this._createNewEventDialog(),\n\t\t\tonBack: () => this.handleBackButton(),\n\t\t})\n\t}\n\n\t_setupShortcuts(): Shortcut[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkey: Keys.ONE,\n\t\t\t\texec: () => this._setUrl(CalendarViewType.WEEK, this.viewModel.selectedDate()),\n\t\t\t\thelp: \"switchWeekView_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.TWO,\n\t\t\t\texec: () => this._setUrl(CalendarViewType.MONTH, this.viewModel.selectedDate()),\n\t\t\t\thelp: \"switchMonthView_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.THREE,\n\t\t\t\texec: () => this._setUrl(CalendarViewType.AGENDA, this.viewModel.selectedDate()),\n\t\t\t\thelp: \"switchAgendaView_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.T,\n\t\t\t\texec: () => this._setUrl(m.route.param(\"view\"), new Date()),\n\t\t\t\thelp: \"viewToday_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.J,\n\t\t\t\tenabled: () => this.currentViewType !== CalendarViewType.AGENDA,\n\t\t\t\texec: () => this._viewPeriod(this.currentViewType, true),\n\t\t\t\thelp: \"viewNextPeriod_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.K,\n\t\t\t\tenabled: () => this.currentViewType !== CalendarViewType.AGENDA,\n\t\t\t\texec: () => this._viewPeriod(this.currentViewType, false),\n\t\t\t\thelp: \"viewPrevPeriod_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.N,\n\t\t\t\texec: () => {\n\t\t\t\t\tthis._createNewEventDialog()\n\t\t\t\t},\n\t\t\t\thelp: \"newEvent_action\",\n\t\t\t},\n\t\t]\n\t}\n\n\tasync _createNewEventDialog(date: Date | null = null): Promise<void> {\n\t\tlet dateToUse: Date\n\t\tif (date != null) {\n\t\t\tdateToUse = date\n\t\t} else {\n\t\t\t// in agenda view, we always show today as the current date, so new event should be created today instead of the (invisibly) selected date in the model.\n\t\t\tdateToUse = this.currentViewType === CalendarViewType.AGENDA ? getStartOfDay(new Date()) : this.viewModel.selectedDate()\n\t\t}\n\n\t\t// Disallow creation of events when there is no existing calendar\n\t\tlet calendarInfos = this.viewModel.getCalendarInfosCreateIfNeeded()\n\n\t\tif (calendarInfos instanceof Promise) {\n\t\t\tawait showProgressDialog(\"pleaseWait_msg\", calendarInfos)\n\t\t}\n\n\t\tconst mailboxDetails = await locator.mailModel.getUserMailboxDetails()\n\t\tconst mailboxProperties = await locator.mailModel.getMailboxProperties(mailboxDetails.mailboxGroupRoot)\n\t\tconst model = await locator.calendarEventModel(getEventWithDefaultTimes(dateToUse), mailboxDetails, mailboxProperties, null)\n\t\tawait showNewCalendarEventEditDialog(model)\n\t}\n\n\t_viewPeriod(viewType: CalendarViewType, next: boolean) {\n\t\tlet duration\n\t\tlet unit: \"day\" | \"week\" | \"month\"\n\n\t\tswitch (viewType) {\n\t\t\tcase CalendarViewType.MONTH:\n\t\t\t\tduration = {\n\t\t\t\t\tmonth: 1,\n\t\t\t\t}\n\t\t\t\tunit = \"month\"\n\t\t\t\tbreak\n\n\t\t\tcase CalendarViewType.WEEK:\n\t\t\t\tduration = {\n\t\t\t\t\tweek: 1,\n\t\t\t\t}\n\t\t\t\tunit = \"week\"\n\t\t\t\tbreak\n\n\t\t\tcase CalendarViewType.DAY:\n\t\t\t\tduration = {\n\t\t\t\t\tday: 1,\n\t\t\t\t}\n\t\t\t\tunit = \"day\"\n\t\t\t\tbreak\n\n\t\t\tdefault:\n\t\t\t\tthrow new ProgrammingError(\"Invalid CalendarViewType: \" + viewType)\n\t\t}\n\n\t\tconst dateTime = DateTime.fromJSDate(this.viewModel.selectedDate())\n\t\tconst newDate = next ? dateTime.plus(duration).startOf(unit).toJSDate() : dateTime.minus(duration).startOf(unit).toJSDate()\n\n\t\tthis.viewModel.selectedDate(newDate)\n\n\t\tm.redraw()\n\n\t\tthis._setUrl(viewType, newDate)\n\t}\n\n\t_renderCalendarViewButtons(): Children {\n\t\tconst calendarViewValues: Array<{ name: string; value: CalendarViewType; icon: Icons; href: string }> = [\n\t\t\t{\n\t\t\t\tname: lang.get(\"month_label\"),\n\t\t\t\tvalue: CalendarViewType.MONTH,\n\t\t\t\ticon: Icons.Table,\n\t\t\t\thref: \"/calendar/month\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: lang.get(\"agenda_label\"),\n\t\t\t\tvalue: CalendarViewType.AGENDA,\n\t\t\t\ticon: Icons.ListUnordered,\n\t\t\t\thref: \"/calendar/agenda\",\n\t\t\t},\n\t\t]\n\n\t\tif (styles.isDesktopLayout()) {\n\t\t\tcalendarViewValues.unshift({\n\t\t\t\tname: lang.get(\"week_label\"),\n\t\t\t\tvalue: CalendarViewType.WEEK,\n\t\t\t\ticon: Icons.TableColumns,\n\t\t\t\thref: \"/calendar/week\",\n\t\t\t})\n\t\t}\n\n\t\tif (client.isDesktopDevice()) {\n\t\t\tcalendarViewValues.unshift({\n\t\t\t\tname: lang.get(\"day_label\"),\n\t\t\t\tvalue: CalendarViewType.DAY,\n\t\t\t\ticon: Icons.TableSingle,\n\t\t\t\thref: \"/calendar/day\",\n\t\t\t})\n\t\t}\n\n\t\treturn calendarViewValues.map((viewType) =>\n\t\t\tm(\n\t\t\t\t\".folder-row.flex.flex-row\", // undo the padding of NavButton and prevent .folder-row > a from selecting NavButton\n\t\t\t\tm(\n\t\t\t\t\t\".flex-grow.mlr-button\",\n\t\t\t\t\tm(NavButton, {\n\t\t\t\t\t\tlabel: () => viewType.name,\n\t\t\t\t\t\ticon: () => viewType.icon,\n\t\t\t\t\t\thref: m.route.get(),\n\t\t\t\t\t\tisSelectedPrefix: viewType.href,\n\t\t\t\t\t\tcolors: NavButtonColor.Nav,\n\t\t\t\t\t\t// Close side menu\n\t\t\t\t\t\tclick: () => {\n\t\t\t\t\t\t\tthis._setUrl(viewType.value, this.viewModel.selectedDate())\n\n\t\t\t\t\t\t\tthis.viewSlider.focus(this.contentColumn)\n\t\t\t\t\t\t},\n\t\t\t\t\t\tpersistentBackground: true,\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t}\n\n\thandleBackButton(): boolean {\n\t\tconst route = m.route.get()\n\n\t\tif (route.startsWith(\"/calendar/day\")) {\n\t\t\tm.route.set(route.replace(\"day\", \"month\"))\n\t\t\treturn true\n\t\t} else if (route.startsWith(\"/calendar/week\")) {\n\t\t\tm.route.set(route.replace(\"week\", \"month\"))\n\t\t\treturn true\n\t\t} else {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t_onPressedAddCalendar() {\n\t\tif (locator.logins.getUserController().getCalendarMemberships().length === 0) {\n\t\t\tthis._showCreateCalendarDialog()\n\t\t} else {\n\t\t\timport(\"../../misc/SubscriptionDialogs\")\n\t\t\t\t.then((SubscriptionDialogUtils) => SubscriptionDialogUtils.checkPaidSubscription())\n\t\t\t\t.then((ok) => {\n\t\t\t\t\tif (ok) {\n\t\t\t\t\t\tthis._showCreateCalendarDialog()\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t}\n\t}\n\n\t_showCreateCalendarDialog() {\n\t\tshowEditCalendarDialog(\n\t\t\t{\n\t\t\t\tname: \"\",\n\t\t\t\tcolor: Math.random().toString(16).slice(-6),\n\t\t\t},\n\t\t\t\"add_action\",\n\t\t\tfalse,\n\t\t\t(dialog, properties) => {\n\t\t\t\tlocator.calendarModel.createCalendar(properties.name, properties.color).then(() => dialog.close())\n\t\t\t},\n\t\t\t\"save_action\",\n\t\t)\n\t}\n\n\t_renderCalendars(shared: boolean): Children {\n\t\treturn this.viewModel.calendarInfos.isLoaded()\n\t\t\t? Array.from(this.viewModel.calendarInfos.getLoaded().values())\n\t\t\t\t\t.filter((calendarInfo) => calendarInfo.shared === shared)\n\t\t\t\t\t.map((calendarInfo) => {\n\t\t\t\t\t\tconst { userSettingsGroupRoot } = locator.logins.getUserController()\n\t\t\t\t\t\tconst existingGroupSettings = userSettingsGroupRoot.groupSettings.find((gc) => gc.group === calendarInfo.groupInfo.group) ?? null\n\t\t\t\t\t\tconst colorValue = \"#\" + (existingGroupSettings ? existingGroupSettings.color : defaultCalendarColor)\n\t\t\t\t\t\tconst groupRootId = calendarInfo.groupRoot._id\n\t\t\t\t\t\treturn m(\".folder-row.flex-start.plr-button\", [\n\t\t\t\t\t\t\tm(\".flex.flex-grow.center-vertically.button-height\", [\n\t\t\t\t\t\t\t\tm(\".calendar-checkbox\", {\n\t\t\t\t\t\t\t\t\tonclick: () => {\n\t\t\t\t\t\t\t\t\t\tconst newHiddenCalendars = new Set(this.viewModel.hiddenCalendars)\n\t\t\t\t\t\t\t\t\t\tthis.viewModel.hiddenCalendars.has(groupRootId)\n\t\t\t\t\t\t\t\t\t\t\t? newHiddenCalendars.delete(groupRootId)\n\t\t\t\t\t\t\t\t\t\t\t: newHiddenCalendars.add(groupRootId)\n\n\t\t\t\t\t\t\t\t\t\tthis.viewModel.setHiddenCalendars(newHiddenCalendars)\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\t\t\t\"border-color\": colorValue,\n\t\t\t\t\t\t\t\t\t\tbackground: this.viewModel.hiddenCalendars.has(groupRootId) ? \"\" : colorValue,\n\t\t\t\t\t\t\t\t\t\ttransition: \"all 0.3s\",\n\t\t\t\t\t\t\t\t\t\tcursor: \"pointer\",\n\t\t\t\t\t\t\t\t\t\tmarginLeft: px(size.hpad_button),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\t\t\".pl-m.b.flex-grow.text-ellipsis\",\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\t\t\t\twidth: 0,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tgetSharedGroupName(calendarInfo.groupInfo, locator.logins.getUserController(), shared),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\tthis._createCalendarActionDropdown(calendarInfo, colorValue, existingGroupSettings, userSettingsGroupRoot, shared),\n\t\t\t\t\t\t])\n\t\t\t\t\t})\n\t\t\t: null\n\t}\n\n\t_createCalendarActionDropdown(\n\t\tcalendarInfo: CalendarInfo,\n\t\tcolorValue: string,\n\t\texistingGroupSettings: GroupSettings | null,\n\t\tuserSettingsGroupRoot: UserSettingsGroupRoot,\n\t\tsharedCalendar: boolean,\n\t): Children {\n\t\tconst { group, groupInfo, groupRoot } = calendarInfo\n\t\tconst user = locator.logins.getUserController().user\n\t\treturn m(IconButton, {\n\t\t\ttitle: \"more_label\",\n\t\t\tcolors: ButtonColor.Nav,\n\t\t\ticon: Icons.More,\n\t\t\tsize: ButtonSize.Compact,\n\t\t\tclick: createDropdown({\n\t\t\t\tlazyButtons: () => [\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: \"edit_action\",\n\t\t\t\t\t\ticon: Icons.Edit,\n\t\t\t\t\t\tsize: ButtonSize.Compact,\n\t\t\t\t\t\tclick: () => this._onPressedEditCalendar(groupInfo, colorValue, existingGroupSettings, userSettingsGroupRoot, sharedCalendar),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: \"sharing_label\",\n\t\t\t\t\t\ticon: Icons.ContactImport,\n\t\t\t\t\t\tclick: () => {\n\t\t\t\t\t\t\tif (locator.logins.getUserController().isFreeAccount()) {\n\t\t\t\t\t\t\t\tshowNotAvailableForFreeDialog()\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tshowGroupSharingDialog(groupInfo, sharedCalendar)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t!isApp() && group.type === GroupType.Calendar && hasCapabilityOnGroup(user, group, ShareCapability.Write)\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tlabel: \"import_action\",\n\t\t\t\t\t\t\t\ticon: Icons.Import,\n\t\t\t\t\t\t\t\tclick: () => showCalendarImportDialog(groupRoot),\n\t\t\t\t\t\t  }\n\t\t\t\t\t\t: null,\n\t\t\t\t\t!isApp() && group.type === GroupType.Calendar && hasCapabilityOnGroup(user, group, ShareCapability.Read)\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tlabel: \"export_action\",\n\t\t\t\t\t\t\t\ticon: Icons.Export,\n\t\t\t\t\t\t\t\tclick: () => {\n\t\t\t\t\t\t\t\t\tconst alarmInfoList = user.alarmInfoList\n\t\t\t\t\t\t\t\t\talarmInfoList &&\n\t\t\t\t\t\t\t\t\t\texportCalendar(\n\t\t\t\t\t\t\t\t\t\t\tgetSharedGroupName(groupInfo, locator.logins.getUserController(), sharedCalendar),\n\t\t\t\t\t\t\t\t\t\t\tgroupRoot,\n\t\t\t\t\t\t\t\t\t\t\talarmInfoList.alarms,\n\t\t\t\t\t\t\t\t\t\t\tnew Date(),\n\t\t\t\t\t\t\t\t\t\t\tgetTimeZone(),\n\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t  }\n\t\t\t\t\t\t: null,\n\t\t\t\t\t!sharedCalendar\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tlabel: \"delete_action\",\n\t\t\t\t\t\t\t\ticon: Icons.Trash,\n\t\t\t\t\t\t\t\tclick: () => this._confirmDeleteCalendar(calendarInfo),\n\t\t\t\t\t\t  }\n\t\t\t\t\t\t: null,\n\t\t\t\t],\n\t\t\t}),\n\t\t})\n\t}\n\n\t_confirmDeleteCalendar(calendarInfo: CalendarInfo) {\n\t\tconst calendarName = getSharedGroupName(calendarInfo.groupInfo, locator.logins.getUserController(), false)\n\t\tloadGroupMembers(calendarInfo.group, locator.entityClient).then((members) => {\n\t\t\tconst ownerMail = locator.logins.getUserController().userGroupInfo.mailAddress\n\t\t\tconst otherMembers = members.filter((member) => member.info.mailAddress !== ownerMail)\n\t\t\tDialog.confirm(\n\t\t\t\t() =>\n\t\t\t\t\t(otherMembers.length > 0\n\t\t\t\t\t\t? lang.get(\"deleteSharedCalendarConfirm_msg\", {\n\t\t\t\t\t\t\t\t\"{calendar}\": calendarName,\n\t\t\t\t\t\t  }) + \" \"\n\t\t\t\t\t\t: \"\") +\n\t\t\t\t\tlang.get(\"deleteCalendarConfirm_msg\", {\n\t\t\t\t\t\t\"{calendar}\": calendarName,\n\t\t\t\t\t}),\n\t\t\t).then((confirmed) => {\n\t\t\t\tif (confirmed) {\n\t\t\t\t\tthis.viewModel.deleteCalendar(calendarInfo).catch(ofClass(NotFoundError, () => console.log(\"Calendar to be deleted was not found.\")))\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n\n\t_onPressedEditCalendar(\n\t\tgroupInfo: GroupInfo,\n\t\tcolorValue: string,\n\t\texistingGroupSettings: GroupSettings | null,\n\t\tuserSettingsGroupRoot: UserSettingsGroupRoot,\n\t\tshared: boolean,\n\t) {\n\t\tshowEditCalendarDialog(\n\t\t\t{\n\t\t\t\tname: getSharedGroupName(groupInfo, locator.logins.getUserController(), shared),\n\t\t\t\tcolor: colorValue.substring(1),\n\t\t\t},\n\t\t\t\"edit_action\",\n\t\t\tshared,\n\t\t\t(dialog, properties) => {\n\t\t\t\tif (!shared) {\n\t\t\t\t\tgroupInfo.name = properties.name\n\t\t\t\t\tlocator.entityClient.update(groupInfo)\n\t\t\t\t}\n\n\t\t\t\t// color always set for existing calendar\n\t\t\t\tif (existingGroupSettings) {\n\t\t\t\t\texistingGroupSettings.color = properties.color\n\t\t\t\t\texistingGroupSettings.name = shared && properties.name !== groupInfo.name ? properties.name : null\n\t\t\t\t} else {\n\t\t\t\t\tconst newGroupSettings = Object.assign(createGroupSettings(), {\n\t\t\t\t\t\tgroup: groupInfo.group,\n\t\t\t\t\t\tcolor: properties.color,\n\t\t\t\t\t\tname: shared && properties.name !== groupInfo.name ? properties.name : null,\n\t\t\t\t\t})\n\t\t\t\t\tuserSettingsGroupRoot.groupSettings.push(newGroupSettings)\n\t\t\t\t}\n\n\t\t\t\tlocator.entityClient.update(userSettingsGroupRoot)\n\t\t\t\tdialog.close()\n\t\t\t},\n\t\t\t\"save_action\",\n\t\t)\n\t}\n\n\tview({ attrs }: Vnode<CalendarViewAttrs>): Children {\n\t\treturn m(\n\t\t\t\".main-view\",\n\t\t\tm(this.viewSlider, {\n\t\t\t\theader: m(Header, {\n\t\t\t\t\thandleBackPress: () => this.handleBackButton(),\n\t\t\t\t\t...attrs.header,\n\t\t\t\t}),\n\t\t\t\tbottomNav: m(BottomNav),\n\t\t\t}),\n\t\t)\n\t}\n\n\tonNewUrl(args: Record<string, any>) {\n\t\tif (!args.view) {\n\t\t\tthis._setUrl(this.currentViewType, this.viewModel.selectedDate(), true)\n\t\t} else {\n\t\t\t// @ts-ignore\n\t\t\tthis.currentViewType = CalendarViewTypeByValue[args.view] ? args.view : CalendarViewType.MONTH\n\t\t\tconst urlDateParam = args.date\n\n\t\t\tif (urlDateParam && this.currentViewType !== CalendarViewType.AGENDA) {\n\t\t\t\t// Unlike JS Luxon assumes local time zone when parsing and not UTC. That's what we want\n\t\t\t\tconst luxonDate = DateTime.fromISO(urlDateParam)\n\t\t\t\tlet date = new Date()\n\n\t\t\t\tif (luxonDate.isValid) {\n\t\t\t\t\tdate = luxonDate.toJSDate()\n\t\t\t\t}\n\n\t\t\t\tif (this.viewModel.selectedDate().getTime() !== date.getTime()) {\n\t\t\t\t\tthis.viewModel.selectedDate(date)\n\n\t\t\t\t\tm.redraw()\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdeviceConfig.setDefaultCalendarView(locator.logins.getUserController().user._id, this.currentViewType)\n\t\t}\n\t}\n\n\tgetViewSlider(): ViewSlider {\n\t\treturn this.viewSlider\n\t}\n\n\t_setUrl(view: string, date: Date, replace: boolean = false) {\n\t\tconst dateString = DateTime.fromJSDate(date).toISODate()\n\t\tm.route.set(\n\t\t\t\"/calendar/:view/:date\",\n\t\t\t{\n\t\t\t\tview,\n\t\t\t\tdate: dateString,\n\t\t\t},\n\t\t\t{\n\t\t\t\treplace,\n\t\t\t},\n\t\t)\n\t}\n\n\tasync _onEventSelected(selectedEvent: CalendarEvent, domEvent: MouseOrPointerEvent, htmlSanitizerPromise: Promise<HtmlSanitizer>) {\n\t\tconst domTarget = domEvent.currentTarget\n\n\t\tif (domTarget == null || !(domTarget instanceof HTMLElement)) {\n\t\t\treturn\n\t\t}\n\n\t\tconst x = domEvent.clientX\n\t\tconst y = domEvent.clientY\n\t\tconst [calendars, mailboxDetails, htmlSanitizer] = await Promise.all([\n\t\t\tthis.viewModel.calendarInfos.getAsync(),\n\t\t\tlocator.mailModel.getUserMailboxDetails(),\n\t\t\thtmlSanitizerPromise,\n\t\t])\n\t\tconst mailboxProperties = await locator.mailModel.getMailboxProperties(mailboxDetails.mailboxGroupRoot)\n\t\t// We want the popup to show at the users mouse\n\t\tconst rect = {\n\t\t\tbottom: y,\n\t\t\theight: 0,\n\t\t\twidth: 0,\n\t\t\ttop: y,\n\t\t\tleft: x,\n\t\t\tright: x,\n\t\t}\n\t\tconst userController = locator.logins.getUserController()\n\t\tconst customer = await userController.loadCustomer()\n\t\tconst ownMailAddresses = getEnabledMailAddressesWithUser(mailboxDetails, userController.userGroupInfo)\n\t\tconst ownAttendee: CalendarEventAttendee | null = findAttendeeInAddresses(selectedEvent.attendees, ownMailAddresses)\n\t\tconst eventType = getEventType(selectedEvent, calendars, ownMailAddresses, userController.user)\n\t\tconst hasBusinessFeature = isCustomizationEnabledForCustomer(customer, FeatureType.BusinessFeatureEnabled) || (await userController.isNewPaidPlan())\n\t\tconst lazyProgenitor = () => resolveCalendarEventProgenitor(selectedEvent, locator.entityClient)\n\t\tconst popupModel = new CalendarEventPopupViewModel(\n\t\t\tselectedEvent,\n\t\t\tlocator.calendarModel,\n\t\t\teventType,\n\t\t\thasBusinessFeature,\n\t\t\townAttendee,\n\t\t\tlazyProgenitor,\n\t\t\tasync (mode: CalendarEventEditMode) => {\n\t\t\t\tif (mode === CalendarEventEditMode.All) {\n\t\t\t\t\treturn locator.calendarEventModel(await lazyProgenitor(), mailboxDetails, mailboxProperties, null)\n\t\t\t\t} else {\n\t\t\t\t\treturn locator.calendarEventModel(selectedEvent, mailboxDetails, mailboxProperties, null)\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\t\tnew CalendarEventPopup(popupModel, rect, htmlSanitizer).show()\n\t}\n}\n","import {\n\t$Promisable,\n\tclone,\n\tDAY_IN_MILLIS,\n\tfindAllAndRemove,\n\tfindAndRemove,\n\tfreezeMap,\n\tgetStartOfDay,\n\tgroupBy,\n\tgroupByAndMapUniquely,\n\tLazyLoaded,\n\tneverNull,\n\tnoOp,\n\tofClass,\n\tpromiseMap,\n\tsymmetricDifference,\n} from \"@tutao/tutanota-utils\"\nimport { CalendarEvent, CalendarEventTypeRef, UserSettingsGroupRootTypeRef } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { getWeekStart, OperationType, WeekStart } from \"../../api/common/TutanotaConstants\"\nimport { NotAuthorizedError, NotFoundError } from \"../../api/common/error/RestError\"\nimport { getListId, isSameId, listIdPart } from \"../../api/common/utils/EntityUtils\"\nimport { LoginController } from \"../../api/main/LoginController\"\nimport { IProgressMonitor, NoopProgressMonitor } from \"../../api/common/utils/ProgressMonitor\"\nimport type { ReceivedGroupInvitation } from \"../../api/entities/sys/TypeRefs.js\"\nimport { GroupInfoTypeRef, UserTypeRef } from \"../../api/entities/sys/TypeRefs.js\"\nimport stream from \"mithril/stream\"\nimport Stream from \"mithril/stream\"\nimport type { CalendarMonthTimeRange } from \"../date/CalendarUtils\"\nimport {\n\taddDaysForEvent,\n\taddDaysForLongEvent,\n\taddDaysForRecurringEvent,\n\tgetDiffIn60mIntervals,\n\tgetEventStart,\n\tgetMonth,\n\tgetTimeZone,\n\tisEventBetweenDays,\n\tisSameEvent,\n\tresolveCalendarEventProgenitor,\n} from \"../date/CalendarUtils\"\nimport { DateTime } from \"luxon\"\nimport { geEventElementMaxId, getEventElementMinId, isAllDayEvent } from \"../../api/common/utils/CommonCalendarUtils\"\nimport { CalendarEventModel, EventSaveResult, getNonOrganizerAttendees } from \"../date/eventeditor/CalendarEventModel.js\"\nimport { askIfShouldSendCalendarUpdatesToAttendees } from \"./CalendarGuiUtils\"\nimport { ReceivedGroupInvitationsModel } from \"../../sharing/model/ReceivedGroupInvitationsModel\"\nimport type { CalendarInfo, CalendarModel } from \"../model/CalendarModel\"\nimport type { EntityUpdateData } from \"../../api/main/EventController\"\nimport { EventController, isUpdateForTypeRef } from \"../../api/main/EventController\"\nimport { EntityClient } from \"../../api/common/EntityClient\"\nimport { ProgressTracker } from \"../../api/main/ProgressTracker\"\nimport { DeviceConfig } from \"../../misc/DeviceConfig\"\nimport type { EventDragHandlerCallbacks } from \"./EventDragHandler\"\nimport { ProgrammingError } from \"../../api/common/error/ProgrammingError.js\"\nimport { CalendarEventEditMode } from \"./eventeditor/CalendarEventEditDialog.js\"\n\nexport type EventsOnDays = {\n\tdays: Array<Date>\n\tshortEvents: Array<Array<CalendarEvent>>\n\tlongEvents: Array<CalendarEvent>\n}\n\n/** container to for the information needed to render & handle a reschedule with drag-and-drop */\nexport type DraggedEvent = {\n\t/** the event instance the user grabbed with the mouse */\n\toriginalEvent: CalendarEvent\n\t/** the temporary event that's shown during the drag */\n\teventClone: CalendarEvent\n}\n\nexport type MouseOrPointerEvent = MouseEvent | PointerEvent\nexport type CalendarEventBubbleClickHandler = (arg0: CalendarEvent, arg1: MouseOrPointerEvent) => unknown\ntype EventsForDays = Map<number, Array<CalendarEvent>>\nexport const LIMIT_PAST_EVENTS_YEARS = 100\nexport type CalendarEventEditModelsFactory = (event: CalendarEvent) => Promise<CalendarEventModel>\n\nexport class CalendarViewModel implements EventDragHandlerCallbacks {\n\t// Should not be changed directly but only through the URL\n\treadonly selectedDate: Stream<Date>\n\n\t/** Mmap from group/groupRoot ID to the calendar info */\n\t_calendarInfos: LazyLoaded<ReadonlyMap<Id, CalendarInfo>>\n\t_eventsForDays: EventsForDays\n\treadonly _loadedMonths: Set<number> // first ms of the month\n\n\t_hiddenCalendars: Set<Id>\n\treadonly _calendarInvitations: ReceivedGroupInvitationsModel\n\treadonly _calendarModel: CalendarModel\n\treadonly _entityClient: EntityClient\n\t// Events that have been dropped but still need to be rendered as temporary while waiting for entity updates.\n\treadonly _transientEvents: Array<CalendarEvent>\n\t_draggedEvent: DraggedEvent | null\n\treadonly _redrawStream: Stream<void>\n\treadonly _deviceConfig: DeviceConfig\n\treadonly _timeZone: string\n\n\tconstructor(\n\t\tprivate readonly logins: LoginController,\n\t\tprivate readonly createCalendarEventEditModel: CalendarEventEditModelsFactory,\n\t\tcalendarModel: CalendarModel,\n\t\tentityClient: EntityClient,\n\t\teventController: EventController,\n\t\tprogressTracker: ProgressTracker,\n\t\tdeviceConfig: DeviceConfig,\n\t\tcalendarInvitations: ReceivedGroupInvitationsModel,\n\t) {\n\t\tthis._calendarModel = calendarModel\n\t\tthis._entityClient = entityClient\n\t\tthis._transientEvents = []\n\n\t\tconst userId = logins.getUserController().user._id\n\n\t\tthis._loadedMonths = new Set()\n\t\tthis._eventsForDays = freezeMap(new Map())\n\t\tthis._deviceConfig = deviceConfig\n\t\tthis._hiddenCalendars = new Set(this._deviceConfig.getHiddenCalendars(userId))\n\t\tthis.selectedDate = stream(getStartOfDay(new Date()))\n\t\tthis._redrawStream = stream()\n\t\tthis._draggedEvent = null\n\t\tthis._timeZone = getTimeZone()\n\t\tthis._calendarInvitations = calendarInvitations\n\t\t// load all calendars. if there is no calendar yet, create one\n\t\t// we load three instances per calendar / CalendarGroupRoot / GroupInfo / Group + 3\n\t\t// for each calendar we load short events for three months +3\n\t\tconst workPerCalendar = 3 + 3\n\t\tconst totalWork = logins.getUserController().getCalendarMemberships().length * workPerCalendar\n\t\tconst monitorHandle = progressTracker.registerMonitorSync(totalWork)\n\t\tlet progressMonitor: IProgressMonitor = neverNull(progressTracker.getMonitor(monitorHandle))\n\t\tthis._calendarInfos = new LazyLoaded(() =>\n\t\t\tthis._calendarModel.loadOrCreateCalendarInfo(progressMonitor).then((it) => {\n\t\t\t\tthis._redraw()\n\n\t\t\t\treturn it\n\t\t\t}),\n\t\t).load()\n\t\tthis.selectedDate.map((d) => {\n\t\t\tconst thisMonthStart = getMonth(d, this._timeZone).start\n\t\t\tconst previousMonthDate = new Date(thisMonthStart)\n\t\t\tpreviousMonthDate.setMonth(thisMonthStart.getMonth() - 1)\n\t\t\tconst nextMonthDate = new Date(thisMonthStart)\n\t\t\tnextMonthDate.setMonth(thisMonthStart.getMonth() + 1)\n\n\t\t\tthis._loadMonthIfNeeded(thisMonthStart)\n\t\t\t\t.then(() => progressMonitor.workDone(1))\n\t\t\t\t.then(() => this._loadMonthIfNeeded(nextMonthDate))\n\t\t\t\t.then(() => progressMonitor.workDone(1))\n\t\t\t\t.then(() => this._loadMonthIfNeeded(previousMonthDate))\n\t\t\t\t.finally(() => {\n\t\t\t\t\tprogressMonitor.completed()\n\t\t\t\t\t// We don't want to report progress after initial month, it shows completed progress bar for a second every time the\n\t\t\t\t\t// month is switched. Doesn't make sense to report more than 100% completion anyway.\n\t\t\t\t\tprogressMonitor = new NoopProgressMonitor()\n\t\t\t\t})\n\t\t})\n\t\teventController.addEntityListener((updates, eventOwnerGroupId) => {\n\t\t\treturn this._entityEventReceived(updates, eventOwnerGroupId)\n\t\t})\n\t}\n\n\tget calendarInvitations(): Stream<Array<ReceivedGroupInvitation>> {\n\t\treturn this._calendarInvitations.invitations\n\t}\n\n\tget calendarInfos(): LazyLoaded<ReadonlyMap<Id, CalendarInfo>> {\n\t\treturn this._calendarInfos\n\t}\n\n\tget hiddenCalendars(): ReadonlySet<Id> {\n\t\treturn this._hiddenCalendars\n\t}\n\n\tget eventsForDays(): EventsForDays {\n\t\treturn this._eventsForDays\n\t}\n\n\tget redraw(): Stream<void> {\n\t\treturn this._redrawStream\n\t}\n\n\tget weekStart(): WeekStart {\n\t\treturn getWeekStart(this.logins.getUserController().userSettingsGroupRoot)\n\t}\n\n\tonDragStart(originalEvent: CalendarEvent, timeToMoveBy: number) {\n\t\tlet eventClone = clone(originalEvent)\n\t\tupdateTemporaryEventWithDiff(eventClone, originalEvent, timeToMoveBy)\n\t\tthis._draggedEvent = {\n\t\t\toriginalEvent,\n\t\t\teventClone,\n\t\t}\n\t}\n\n\tonDragUpdate(timeToMoveBy: number) {\n\t\tif (this._draggedEvent) {\n\t\t\tupdateTemporaryEventWithDiff(this._draggedEvent.eventClone, this._draggedEvent.originalEvent, timeToMoveBy)\n\t\t}\n\t}\n\n\t/**\n\t * This is called when the event is dropped.\n\t */\n\tasync onDragEnd(timeToMoveBy: number): Promise<void> {\n\t\t//if the time of the dragged event is the same as of the original we only cancel the drag\n\t\tif (timeToMoveBy !== 0) {\n\t\t\tif (this._draggedEvent == null) return\n\n\t\t\tconst { originalEvent, eventClone } = this._draggedEvent\n\t\t\tthis._draggedEvent = null\n\t\t\tupdateTemporaryEventWithDiff(eventClone, originalEvent, timeToMoveBy)\n\n\t\t\tthis._addTransientEvent(eventClone)\n\n\t\t\tconst progenitor = await resolveCalendarEventProgenitor(originalEvent, this._entityClient)\n\n\t\t\ttry {\n\t\t\t\tconst didUpdate = await this.moveEvent(progenitor, timeToMoveBy)\n\n\t\t\t\tif (didUpdate !== EventSaveResult.Saved) {\n\t\t\t\t\tthis._removeTransientEvent(eventClone)\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tthis._removeTransientEvent(eventClone)\n\n\t\t\t\tthrow e\n\t\t\t}\n\t\t} else {\n\t\t\tthis._draggedEvent = null\n\t\t}\n\t}\n\n\tget temporaryEvents(): Array<CalendarEvent> {\n\t\treturn this._transientEvents.concat(this._draggedEvent ? [this._draggedEvent.eventClone] : [])\n\t}\n\n\tsetHiddenCalendars(newHiddenCalendars: Set<Id>) {\n\t\tthis._hiddenCalendars = newHiddenCalendars\n\n\t\tthis._deviceConfig.setHiddenCalendars(this.logins.getUserController().user._id, [...newHiddenCalendars])\n\t}\n\n\t/**\n\t * Get calendar infos, creating a new calendar info if none exist\n\t * Not async because we want to return the result directly if it is available when called\n\t * otherwise we return a promise\n\t */\n\tgetCalendarInfosCreateIfNeeded(): $Promisable<ReadonlyMap<Id, CalendarInfo>> {\n\t\tif (this._calendarInfos.isLoaded() && this.calendarInfos.getLoaded().size > 0) {\n\t\t\treturn this._calendarInfos.getLoaded()\n\t\t}\n\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst calendars = await this._calendarInfos.getAsync()\n\n\t\t\tif (calendars.size > 0) {\n\t\t\t\treturn calendars\n\t\t\t} else {\n\t\t\t\tawait this._calendarModel.createCalendar(\"\", null)\n\t\t\t\tthis._calendarInfos = new LazyLoaded(() => this._calendarModel.loadCalendarInfos(new NoopProgressMonitor()))\n\t\t\t\treturn this._calendarInfos.getAsync()\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Given a events and days, return the long and short events of that range of days\n\t *we detect events that should be removed based on their UID + start and end time\n\t *\n\t * @param days: The range of days from which events should be returned\n\t * @returns    shortEvents: Array<Array<CalendarEvent>>, short events per day.,\n\t *             longEvents: Array<CalendarEvent>: long events over the whole range,\n\t *             days: Array<Date>: the original days that were passed in\n\t */\n\tgetEventsOnDays(days: Array<Date>): EventsOnDays {\n\t\tconst longEvents: Set<CalendarEvent> = new Set()\n\t\tlet shortEvents: Array<Array<CalendarEvent>> = []\n\t\t// It might be the case that a UID is shared by events across calendars, so we need to differentiate them by list ID aswell\n\t\tconst transientEventUidsByCalendar = groupByAndMapUniquely(\n\t\t\tthis._transientEvents,\n\t\t\t(event) => getListId(event),\n\t\t\t(event) => event.uid,\n\t\t)\n\n\t\tfor (let day of days) {\n\t\t\tconst shortEventsForDay: CalendarEvent[] = []\n\t\t\tconst events = this._eventsForDays.get(day.getTime()) || []\n\n\t\t\tconst sortEvent = (event: CalendarEvent) => {\n\t\t\t\tif (isAllDayEvent(event) || getDiffIn60mIntervals(event.startTime, event.endTime) >= 24) {\n\t\t\t\t\tlongEvents.add(event)\n\t\t\t\t} else {\n\t\t\t\t\tshortEventsForDay.push(event)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (let event of events) {\n\t\t\t\tif (transientEventUidsByCalendar.get(getListId(event))?.has(event.uid)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif (this._draggedEvent?.originalEvent !== event && !this._hiddenCalendars.has(neverNull(event._ownerGroup))) {\n\t\t\t\t\tsortEvent(event)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis._transientEvents.filter((event) => isEventBetweenDays(event, day, day, this._timeZone)).forEach(sortEvent)\n\n\t\t\tconst temporaryEvent = this._draggedEvent?.eventClone\n\n\t\t\tif (temporaryEvent && isEventBetweenDays(temporaryEvent, day, day, this._timeZone)) {\n\t\t\t\tsortEvent(temporaryEvent)\n\t\t\t}\n\n\t\t\tshortEvents.push(shortEventsForDay)\n\t\t}\n\n\t\tconst longEventsArray = Array.from(longEvents)\n\t\treturn {\n\t\t\tdays,\n\t\t\tlongEvents: longEventsArray,\n\t\t\tshortEvents: shortEvents.map((innerShortEvents) => innerShortEvents.filter((event) => !longEvents.has(event))),\n\t\t}\n\t}\n\n\tasync deleteCalendar(calendar: CalendarInfo): Promise<void> {\n\t\tawait this._calendarModel.deleteCalendar(calendar)\n\t}\n\n\t_addTransientEvent(event: CalendarEvent) {\n\t\tthis._transientEvents.push(event)\n\t}\n\n\t_removeTransientEvent(event: CalendarEvent) {\n\t\tfindAndRemove(this._transientEvents, (transient) => transient.uid === event.uid)\n\t}\n\n\t/**\n\t * move an event to a new start time\n\t * @param event the actually dragged event (may be a repeated instance)\n\t * @param diff the amount of milliseconds to shift the event by\n\t * @param mode which parts of the series should be rescheduled?\n\t */\n\tprivate async moveEvent(event: CalendarEvent, diff: number, mode: CalendarEventEditMode = CalendarEventEditMode.All): Promise<EventSaveResult> {\n\t\tif (event.uid == null) {\n\t\t\tthrow new ProgrammingError(\"called moveEvent for an event without uid\")\n\t\t}\n\n\t\tif (mode !== CalendarEventEditMode.All) {\n\t\t\tthrow new ProgrammingError(\"single-instance-rescheduling is not implemented.\")\n\t\t}\n\n\t\tconst editModel = await this.createCalendarEventEditModel(event)\n\t\teditModel.editModels.whenModel.rescheduleEvent({ millisecond: diff })\n\n\t\tif (getNonOrganizerAttendees(event).length > 0) {\n\t\t\tconst response = await askIfShouldSendCalendarUpdatesToAttendees()\n\t\t\tif (response === \"yes\") {\n\t\t\t\teditModel.shouldSendUpdates = true\n\t\t\t} else if (response === \"cancel\") {\n\t\t\t\treturn EventSaveResult.Failed\n\t\t\t}\n\t\t}\n\n\t\t// Errors are handled in the individual views\n\t\treturn await editModel.updateExistingEvent()\n\t}\n\n\t_addOrUpdateEvent(calendarInfo: CalendarInfo | null, event: CalendarEvent) {\n\t\tif (calendarInfo) {\n\t\t\tconst eventListId = getListId(event)\n\t\t\tconst eventMonth = getMonth(getEventStart(event, this._timeZone), this._timeZone)\n\n\t\t\tif (isSameId(calendarInfo.groupRoot.shortEvents, eventListId)) {\n\t\t\t\t// If the month is not loaded, we don't want to put it into events.\n\t\t\t\t// We will put it there when we load the month\n\t\t\t\tif (!this._loadedMonths.has(eventMonth.start.getTime())) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tthis._addDaysForEvent(event, eventMonth)\n\t\t\t} else if (isSameId(calendarInfo.groupRoot.longEvents, eventListId)) {\n\t\t\t\tthis._removeExistingEvent(calendarInfo.longEvents.getLoaded(), event)\n\n\t\t\t\tcalendarInfo.longEvents.getLoaded().push(event)\n\n\t\t\t\tthis._loadedMonths.forEach((firstDayTimestamp) => {\n\t\t\t\t\tconst loadedMonth = getMonth(new Date(firstDayTimestamp), this._timeZone)\n\n\t\t\t\t\tif (event.repeatRule) {\n\t\t\t\t\t\tthis._addDaysForRecurringEvent(event, loadedMonth)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis._addDaysForLongEvent(event, loadedMonth)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\t_entityEventReceived<T>(updates: ReadonlyArray<EntityUpdateData>, eventOwnerGroupId: Id): Promise<void> {\n\t\treturn this._calendarInfos.getAsync().then((calendarEvents) => {\n\t\t\tconst addedOrUpdatedEventsUpdates: EntityUpdateData[] = [] // we try to make get multiple requests for calendar events potentially created by post multiple\n\n\t\t\treturn promiseMap(updates, (update) => {\n\t\t\t\tif (isUpdateForTypeRef(UserSettingsGroupRootTypeRef, update)) {\n\t\t\t\t\tthis._redraw()\n\t\t\t\t}\n\n\t\t\t\tif (isUpdateForTypeRef(CalendarEventTypeRef, update)) {\n\t\t\t\t\tif (update.operation === OperationType.CREATE || update.operation === OperationType.UPDATE) {\n\t\t\t\t\t\taddedOrUpdatedEventsUpdates.push(update)\n\t\t\t\t\t} else if (update.operation === OperationType.DELETE) {\n\t\t\t\t\t\tthis._removeDaysForEvent([update.instanceListId, update.instanceId], eventOwnerGroupId)\n\n\t\t\t\t\t\tthis._redraw()\n\t\t\t\t\t}\n\t\t\t\t} else if (\n\t\t\t\t\tisUpdateForTypeRef(UserTypeRef, update) && // only process update event received for the user group - to not process user update from admin membership.\n\t\t\t\t\tisSameId(eventOwnerGroupId, this.logins.getUserController().user.userGroup.group)\n\t\t\t\t) {\n\t\t\t\t\tif (update.operation === OperationType.UPDATE) {\n\t\t\t\t\t\tconst calendarMemberships = this.logins.getUserController().getCalendarMemberships()\n\t\t\t\t\t\treturn this._calendarInfos.getAsync().then((calendarInfos) => {\n\t\t\t\t\t\t\t// Remove calendars we no longer have membership in\n\t\t\t\t\t\t\tcalendarInfos.forEach((ci, group) => {\n\t\t\t\t\t\t\t\tif (calendarMemberships.every((mb) => group !== mb.group)) {\n\t\t\t\t\t\t\t\t\tthis._hiddenCalendars.delete(group)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tconst oldGroupIds = new Set(calendarInfos.keys())\n\t\t\t\t\t\t\tconst newGroupIds = new Set(calendarMemberships.map((m) => m.group))\n\t\t\t\t\t\t\tconst diff = symmetricDifference(oldGroupIds, newGroupIds)\n\n\t\t\t\t\t\t\tif (diff.size !== 0) {\n\t\t\t\t\t\t\t\tthis._loadedMonths.clear()\n\n\t\t\t\t\t\t\t\tthis._replaceEvents(new Map())\n\n\t\t\t\t\t\t\t\tthis._calendarInfos = new LazyLoaded(() => this._calendarModel.loadCalendarInfos(new NoopProgressMonitor())).load()\n\t\t\t\t\t\t\t\treturn this._calendarInfos\n\t\t\t\t\t\t\t\t\t.getAsync()\n\t\t\t\t\t\t\t\t\t.then(() => {\n\t\t\t\t\t\t\t\t\t\tconst selectedDate = this.selectedDate()\n\t\t\t\t\t\t\t\t\t\tconst previousMonthDate = new Date(selectedDate)\n\t\t\t\t\t\t\t\t\t\tpreviousMonthDate.setMonth(selectedDate.getMonth() - 1)\n\t\t\t\t\t\t\t\t\t\tconst nextMonthDate = new Date(selectedDate)\n\t\t\t\t\t\t\t\t\t\tnextMonthDate.setMonth(selectedDate.getMonth() + 1)\n\t\t\t\t\t\t\t\t\t\treturn this._loadMonthIfNeeded(selectedDate)\n\t\t\t\t\t\t\t\t\t\t\t.then(() => this._loadMonthIfNeeded(nextMonthDate))\n\t\t\t\t\t\t\t\t\t\t\t.then(() => this._loadMonthIfNeeded(previousMonthDate))\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t.then(() => this._redraw())\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t} else if (isUpdateForTypeRef(GroupInfoTypeRef, update)) {\n\t\t\t\t\tthis._calendarInfos.getAsync().then((calendarInfos) => {\n\t\t\t\t\t\tconst calendarInfo = calendarInfos.get(eventOwnerGroupId)\n\n\t\t\t\t\t\t//only process the GroupInfo update if the id is the same as calendarInfo.groupInfo._id\n\t\t\t\t\t\tif (calendarInfo && isSameId(calendarInfo.groupInfo._id, [update.instanceListId, update.instanceId])) {\n\t\t\t\t\t\t\treturn this._entityClient.load(GroupInfoTypeRef, [update.instanceListId, update.instanceId]).then((groupInfo) => {\n\t\t\t\t\t\t\t\tcalendarInfo.groupInfo = groupInfo\n\t\t\t\t\t\t\t\tthis._redraw()\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}).then(() => {\n\t\t\t\t// handle potential post multiple updates in get multiple requests\n\t\t\t\t// this is only necessary until post multiple updates are dealt with in EntityRestCache\n\t\t\t\tconst updatesPerList = groupBy(addedOrUpdatedEventsUpdates, (update) => update.instanceListId)\n\t\t\t\treturn promiseMap(updatesPerList, ([instanceListId, updates]) => {\n\t\t\t\t\tconst ids = updates.map((update) => update.instanceId)\n\t\t\t\t\treturn this._entityClient\n\t\t\t\t\t\t.loadMultiple(CalendarEventTypeRef, instanceListId, ids)\n\t\t\t\t\t\t.then((events) => {\n\t\t\t\t\t\t\tevents.forEach((event) => {\n\t\t\t\t\t\t\t\tthis._addOrUpdateEvent(calendarEvents.get(neverNull(event._ownerGroup)) ?? null, event)\n\n\t\t\t\t\t\t\t\tthis._removeTransientEvent(event)\n\t\t\t\t\t\t\t})\n\n\t\t\t\t\t\t\tthis._redraw()\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(\n\t\t\t\t\t\t\tofClass(NotAuthorizedError, (e) => {\n\t\t\t\t\t\t\t\t// return updates that are not in cache Range if NotAuthorizedError (for those updates that are in cache range)\n\t\t\t\t\t\t\t\tconsole.log(\"NotAuthorizedError for event in entityEventsReceived of view\", e)\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.catch(\n\t\t\t\t\t\t\tofClass(NotFoundError, (e) => {\n\t\t\t\t\t\t\t\tconsole.log(\"Not found event in entityEventsReceived of view\", e)\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t}).then(noOp)\n\t\t\t})\n\t\t})\n\t}\n\n\tasync _loadMonthIfNeeded(dayInMonth: Date): Promise<void> {\n\t\tconst month = getMonth(dayInMonth, this._timeZone)\n\n\t\tif (!this._loadedMonths.has(month.start.getTime())) {\n\t\t\tthis._loadedMonths.add(month.start.getTime())\n\n\t\t\ttry {\n\t\t\t\tawait this._loadEvents(month)\n\t\t\t} catch (e) {\n\t\t\t\tthis._loadedMonths.delete(month.start.getTime())\n\n\t\t\t\tthrow e\n\t\t\t} finally {\n\t\t\t\tthis._redraw()\n\t\t\t}\n\t\t}\n\t}\n\n\t_loadEvents(month: CalendarMonthTimeRange): Promise<any> {\n\t\treturn this._calendarInfos.getAsync().then((calendarInfos) => {\n\t\t\t// Because of the timezones and all day events, we might not load an event which we need to display.\n\t\t\t// So we add a margin on 24 hours to be sure we load everything we need. We will filter matching\n\t\t\t// events anyway.\n\t\t\tconst startId = getEventElementMinId(month.start.getTime() - DAY_IN_MILLIS)\n\t\t\tconst endId = geEventElementMaxId(month.end.getTime() + DAY_IN_MILLIS)\n\t\t\t// We collect events from all calendars together and then replace map synchronously.\n\t\t\t// This is important to replace the map synchronously to not get race conditions because we load different months in parallel.\n\t\t\t// We could replace map more often instead of aggregating events but this would mean creating even more (cals * months) maps.\n\t\t\t//\n\t\t\t// Note: there may be issues if we get entity update before other calendars finish loading but the chance is low and we do not\n\t\t\t// take care of this now.\n\t\t\tconst aggregateShortEvents: CalendarEvent[] = []\n\t\t\tconst aggregateLongEvents: CalendarEvent[] = []\n\t\t\treturn promiseMap(calendarInfos.values(), (calendarInfo) => {\n\t\t\t\tconst { groupRoot, longEvents } = calendarInfo\n\t\t\t\treturn Promise.all([\n\t\t\t\t\tthis._entityClient.loadReverseRangeBetween(CalendarEventTypeRef, groupRoot.shortEvents, endId, startId, 200),\n\t\t\t\t\tlongEvents.getAsync(),\n\t\t\t\t]).then(([shortEventsResult, longEvents]) => {\n\t\t\t\t\taggregateShortEvents.push(...shortEventsResult.elements)\n\t\t\t\t\taggregateLongEvents.push(...longEvents)\n\t\t\t\t})\n\t\t\t}).then(() => {\n\t\t\t\tconst newEvents = this._cloneEvents()\n\n\t\t\t\taggregateShortEvents\n\t\t\t\t\t.filter((e) => {\n\t\t\t\t\t\tconst eventStart = getEventStart(e, this._timeZone).getTime()\n\t\t\t\t\t\treturn eventStart >= month.start.getTime() && eventStart < month.end.getTime()\n\t\t\t\t\t}) // only events for the loaded month\n\t\t\t\t\t.forEach((e) => {\n\t\t\t\t\t\taddDaysForEvent(newEvents, e, month)\n\t\t\t\t\t})\n\t\t\t\tconst zone = this._timeZone\n\t\t\t\taggregateLongEvents.forEach((e) => {\n\t\t\t\t\tif (e.repeatRule) {\n\t\t\t\t\t\taddDaysForRecurringEvent(newEvents, e, month, zone)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Event through we get the same set of long events for each month we have to invoke this for each month\n\t\t\t\t\t\t// because addDaysForLongEvent adds days only for the specified month.\n\t\t\t\t\t\taddDaysForLongEvent(newEvents, e, month, zone)\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\tthis._replaceEvents(newEvents)\n\t\t\t})\n\t\t})\n\t}\n\n\t/**\n\t * Removes {@param eventToRemove} from {@param events} using isSameEvent()\n\t * also removes it from {@code this._eventsForDays} if end time does not match\n\t */\n\t_removeExistingEvent(events: Array<CalendarEvent>, eventToRemove: CalendarEvent) {\n\t\tconst indexOfOldEvent = events.findIndex((el) => isSameEvent(el, eventToRemove))\n\n\t\tif (indexOfOldEvent !== -1) {\n\t\t\tconst oldEvent = events[indexOfOldEvent]\n\n\t\t\t// If the old and new event end times do not match, we need to remove all occurrences of old event, otherwise iterating\n\t\t\t// occurrences of new event won't replace all occurrences of old event. Changes of start or repeat rule already change\n\t\t\t// ID of the event so it is not a problem.\n\t\t\tif (oldEvent.endTime.getTime() !== eventToRemove.endTime.getTime()) {\n\t\t\t\tconst newMap = this._cloneEvents()\n\n\t\t\t\tnewMap.forEach(\n\t\t\t\t\t(\n\t\t\t\t\t\tdayEvents, // finding all because event can overlap with itself so a day can have multiple occurrences of the same event in it\n\t\t\t\t\t) => findAllAndRemove(dayEvents, (e) => isSameId(e._id, oldEvent._id)),\n\t\t\t\t)\n\n\t\t\t\tthis._replaceEvents(newMap)\n\t\t\t}\n\n\t\t\tevents.splice(indexOfOldEvent, 1)\n\t\t}\n\t}\n\n\t_addDaysForEvent(event: CalendarEvent, month: CalendarMonthTimeRange) {\n\t\tconst newMap = this._cloneEvents()\n\n\t\taddDaysForEvent(newMap, event, month)\n\n\t\tthis._replaceEvents(newMap)\n\t}\n\n\t_replaceEvents(newMap: EventsForDays) {\n\t\tthis._eventsForDays = freezeMap(newMap)\n\t}\n\n\t_cloneEvents(): EventsForDays {\n\t\treturn new Map(this._eventsForDays)\n\t}\n\n\t_addDaysForRecurringEvent(event: CalendarEvent, month: CalendarMonthTimeRange) {\n\t\tif (-DateTime.fromJSDate(event.startTime).diffNow(\"year\").years > LIMIT_PAST_EVENTS_YEARS) {\n\t\t\tconsole.log(\"repeating event is too far into the past\", event)\n\t\t\treturn\n\t\t}\n\n\t\tconst newMap = this._cloneEvents()\n\n\t\taddDaysForRecurringEvent(newMap, event, month, this._timeZone)\n\n\t\tthis._replaceEvents(newMap)\n\t}\n\n\t_removeDaysForEvent(id: IdTuple, ownerGroupId: Id) {\n\t\tconst newMap = this._cloneEvents()\n\n\t\tnewMap.forEach((dayEvents) => findAllAndRemove(dayEvents, (e) => isSameId(e._id, id)))\n\n\t\tthis._replaceEvents(newMap)\n\n\t\tif (this._calendarInfos.isLoaded()) {\n\t\t\tconst infos = this._calendarInfos.getLoaded()\n\n\t\t\tconst info = infos.get(ownerGroupId)\n\n\t\t\tif (info) {\n\t\t\t\tif (isSameId(listIdPart(id), info.groupRoot.longEvents)) {\n\t\t\t\t\tfindAndRemove(info.longEvents.getLoaded(), (e) => isSameId(e._id, id))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t_addDaysForLongEvent(event: CalendarEvent, month: CalendarMonthTimeRange) {\n\t\tconst newMap = this._cloneEvents()\n\n\t\taddDaysForLongEvent(newMap, event, month)\n\n\t\tthis._replaceEvents(newMap)\n\t}\n\n\t_redraw() {\n\t\t// Need to pass some argument to make it a \"set\" operation\n\t\tthis._redrawStream(undefined)\n\t}\n}\n\nfunction updateTemporaryEventWithDiff(eventClone: CalendarEvent, originalEvent: CalendarEvent, mouseDiff: number) {\n\teventClone.startTime = new Date(originalEvent.startTime.getTime() + mouseDiff)\n\teventClone.endTime = new Date(originalEvent.endTime.getTime() + mouseDiff)\n}\n"],"names":["lineHeight","size","calendar_line_height","lineHeightPx","px","CalendarEventBubble","constructor","this","_hasFinishedInitialRender","oncreate","vnode","view","attrs","doFadeIn","fadeIn","enablePointerEvents","m","noBorderLeft","noBorderRight","style","background","color","colorForBg","minHeight","height","Math","max","verticalPadding","opacity","pointerEvents","onclick","e","stopPropagation","click","target","hasAlarm","Icon","icon","fill","class","width","renderContent","maybeHeight","text","secondLineText","linesInBubble","floor","topSectionMaxLines","topSectionClass","renderTextSection","classes","maxHeight","ContinuingCalendarEventBubble","startsBefore","showTime","formatEventTime","event","summary","onEventClicked","endsAfter","hasAlarmsForTheUser","user","EventDragHandler","draggingArea","callbacks","_data","_isDragging","_lastDiffBetweenDates","_hasChanged","_draggingArea","_eventDragCallbacks","isDragging","originalEvent","_b","_a","queryHasChanged","isChanged","prepareDrag","calendarEvent","dateUnderMouse","mousePos","keepTime","classList","add","originalDateUnderMouse","adjustDateUnderMouse","startTime","originalMousePos","handleDrag","dragData","adjustedDateUnderMouse","distanceX","x","distanceY","y","distance","sqrt","diffBetweenDates","getDayUnderMouseDiff","onDragUpdate","redraw","onDragStart","async","remove","onDragEnd","cancelDrag","eventStart","Time","fromDate","toDate","isAllDayEvent","getAllDayDateUTC","getTime","renderCalendarSwitchLeftButton","label","IconButton","title","renderCalendarSwitchRightButton","weekTitle","date","weekStart","startOfTheWeekOffset","getStartOfTheWeekOffset","firstDate","getStartOfWeek","lastDate","incrementDate","Date","getMonth","getFullYear","lang","formats","monthLong","format","yearNumeric","getNextFourteenDays","startOfToday","calculationDate","days","i","push","agendaTitle","lastDay","lastThrow","dateRangeText","dateWithWeekday","dateWithWeekdayAndYear","get","calendarWeek","String","getWeekNumber","CalendarViewType","calendarNavConfiguration","viewType","switcher","onBack","onForward","DAY","back","forward","formatDateWithWeekday","week","MONTH","formatMonthWithFullYear","WEEK","AGENDA","askIfShouldSendCalendarUpdatesToAttendees","Promise","resolve","alertDialog","cancelButton","close","type","noButton","yesButton","Dialog","confirmMultiple","positive","dayHeight","styles","isDesktopLayout","spaceBetweenEvents","CalendarMonthView","_monthDom","_dayUnderMouse","_lastMousePos","_resizeListener","_zone","getTimeZone","_lastWidth","_lastHeight","_eventDragHandler","neverNull","document","body","dragHandlerCallbacks","windowFacade","addResizeListener","onremove","removeResizeListener","startOfTheWeek","thisMonth","getCalendarMonth","selectedDate","previousMonthDate","setMonth","previousMonth","nextMonthDate","nextMonth","lastMontDate","incrementMonth","nextMontDate","PageView","previousPage","key","getFirstDayOfMonth","nodes","_renderCalendar","currentPage","nextPage","onChangePage","next","onChangeMonth","onbeforeupdate","newVnode","oldVnode","dom","different","eventsForDays","amPmFormat","groupColors","hiddenCalendars","offsetWidth","offsetHeight","month","currentlyVisibleMonth","zone","weekdays","weeks","today","getStartOfDayWithZone","map","wd","onupdate","onmousemove","mouseEvent","posAndBoundsFromMouseEvent","getPosAndBoundsFromMouseEvent","targetWidth","targetHeight","assert","length","unitHeight","currentSquareY","clamp","unitWidth","currentSquareX","getDateFromMousePos","day","onmouseup","_endDrag","onmouseleave","_renderDay","_renderWeekEvents","dayUnderMouse","originalDate","endDrag","catch","ofClass","UserError","showUserError","weekDayNumber","isSelectedDate","isSameDay","paddingDay","client","isDesktopDevice","newDate","hour","getHours","setHours","onDateSelected","onNewEvent","preventDefault","theme","content_accent","_renderDayHeader","getDateIndicator","eventsOnDays","getEventsOnDays","events","Set","longEvents","concat","flat","shortEvents","firstDayOfWeek","lastDayOfWeek","dayWidth","_getWidthForDay","weekHeight","_getHeightForWeek","eventHeight","maxEventsPerDay","eventsPerDay","moreEventsForDay","eventMargin","calendar_event_margin","calendar_event_margin_mobile","firstDayOfNextWeek","getStartOfNextDayWithZone","layOutEvents","Array","from","columns","columnIndex","eventIsAllDay","isAllDayEventByTimes","endTime","getAllDayDateForTimezone","eventEnd","getEventEnd","position","_getEventPosition","renderEvent","forEach","dayInWeek","index","eventsForDay","indexOf","moreEventsCount","weekday","isPadding","bottom","CALENDAR_EVENT_HEIGHT","left","isTemporary","temporaryEvents","includes","_id","top","right","isUsingBottomNavigation","onmousedown","lastMousePos","getEventColor","locator","logins","getUserController","domEvent","TEMPORARY_EVENT_OPACITY","calendarDayWidth","calendarDayHeight","dayOfStartDateInWeek","getDiffIn24IntervalsFast","dayOfEndDateInWeek","calendarEventMargin","getDate","getDiffIn24hIntervals","CalendarAgendaView","now","tomorrow","lastDayFormatted","formatDate","filter","has","_ownerGroup","ev","dateDescription","eventStartsBefore","timeFormat","getTimeTextFormatForLongEvent","formattedEventTime","eventLocation","location","toString","Boolean","showEditCalendarDialog","name","titleTextId","shared","okAction","okTextId","warningMessage","nameStream","stream","colorStream","showActionDialog","allowOkWithReturn","child","TextField","value","oninput","downcast","inputEvent","okActionTextId","dialog","substring","calendarDayTimes","numberRange","number","allHoursHeight","calendar_hour_height","CalendarDayEventsView","_dayDom","time","hourDivision","hourRounded","minutesInc","minute","getTimeFromMousePos","setTimeUnderMouse","onTimePressed","oncontextmenu","onTimeContextPressed","_renderEvents","_renderTimeIndicator","displayTimeIndicator","passedMillisInDay","getMinutes","DAY_IN_MILLIS","getTimeIndicatorPosition","_renderColumns","_renderEvent","columnWidth","startOfEvent","getStartOfDay","endOfEvent","eventEndsAfterDay","getEndOfDay","calendar_event_border","fullViewWidth","maxWidth","colSpan","expandEvent","isTemporaryEvent","setCurrentDraggedEvent","mapNullable","option","calendar_day_event_padding","clientWidth","column","MultiDayCalendarView","_redrawIntervalId","_longEventsDom","_domElements","_dateUnderMouse","_viewDom","_isHeaderEventBeingDragged","_scrollPosition","DEFAULT_HOUR_OF_DAY","startOfThisPeriod","daysInPeriod","startOfPreviousPeriod","startOfNextPeriod","previousRange","getRangeOfDays","currentRange","nextRange","previousPageEvents","currentPageEvents","nextPageEvents","_renderWeek","onChangeViewPeriod","_getTodayTimestamp","thisWeek","mainWeek","setInterval","clearInterval","renderHeaderDesktop","renderHeaderMobile","scrollTop","onscroll","calendar_hour_width","calendar_hour_width_mobile","content_border","formatTime","newEventHandler","hours","minutes","startEventDrag","combineDateWithTime","getBoundingClientRect","thisPageEvents","mainPageEvents","mainPageEventsCount","padding","vpad_small","paddingTop","transition","renderLongEvents","children","dayNumber","setMinutes","renderDayNamesRow","renderLongEventsSection","renderSelectedDateIndicatorRow","temporayEvents","thisPageLongEvents","mainPageLongEvents","maxEventsInColumn","dates","justifyContent","SELECTED_DATE_INDICATOR_THICKNESS","alignSelf","dayRange","renderLongEventsForSingleDay","renderLongEventsForMultipleDays","renderLongEventBubble","_","firstDay","rows","c","isAllDay","dayOfStartDate","getEventStart","dayOfEndDate","dayNumberClass","weekdayShort","margin","AttendeeListEditor","hasPlanWithInvites","externalPasswordVisibility","Map","renderInvitationField","renderGuestList","model","recipientsSearch","MailRecipientsTextField","onTextChanged","v","recipients","disabled","onRecipientAdded","address","contact","shouldShowSendInviteNotAvailable","getAvailablePlansWithEventInvites","module","import","then","n","S","plansWithEventInvites","showPlanUpgradeRequiredDialog","editModels","whoModel","addAttendee","onRecipientRemoved","noOp","injectionsRight","renderIsConfidentialToggle","search","renderSendUpdateCheckbox","guests","some","a","ToggleButton","isConfidential","onToggled","toggled","initiallyHadOtherAttendees","Checkbox","onChecked","shouldSendUpdates","checked","organizer","slice","attendeeRenderers","renderOrganizer","guest","renderGuest","externalGuestPasswords","password","strength","getPresharedPassword","autocompleteAs","helpLabel","CompletenessIndicator","percentageCompleted","newValue","setPresharedPassword","renderRevealIcon","r","set","status","isMe","ownGuest","editableOrganizer","possibleOrganizers","roleLabel","statusLine","renderStatusIcon","fullName","nameAndAddress","editModel","createDropdown","lazyButtons","showOrganizerDropdown","content_fg","renderAttendee","minWidth","DropDownSelector","items","createAttendingItems","selectedValue","selectionChangedHandler","setOwnAttendance","j","writeMail","content","canModifyGuests","removeAttendee","rightContent","spacer","button_height","borderBottom","marginTop","vpad","iconForAttendeeStatus","EventTimeEditor","renderTwoColumnsIfFits","DatePicker","startDate","nullSelectionText","TimePicker","onTimeSelected","endDate","RepeatRuleEditor","renderRepeatPeriod","repeatPeriod","renderRepeatInterval","renderEndCondition","renderExclusionCount","renderEndType","renderEndValue","excludedDates","renderDeleteExclusionButton","repeatValues","createRepeatRuleFrequencyValues","period","intervalValues","createIntervalValues","repeatInterval","interval","endTypeValues","createRepeatRuleEndTypeValues","repeatEndType","end","repeatEndOccurrences","endValue","repeatEndDateForDisplay","rightAlignDropdown","deleteExcludedDates","CalendarEventEditView","attendeesExpanded","transform","renderReadonlyMessage","renderHeading","renderAttendees","renderEventTimeEditor","renderRepeatRuleEditor","renderCalendarPicker","renderRemindersEditor","renderLocationField","renderDescriptionEditor","eventType","renderGuestsExpanderButton","ExpanderButton","expanded","onExpandedChange","InfoBanner","message","buttons","ExpanderPanel","whenModel","availableCalendars","getAvailableCalendars","calendarInfo","getSharedGroupName","groupInfo","userController","selectedCalendar","renderCalendarColor","group","defaultCalendarColor","alarmModel","canEditReminders","taken","available","splitTriggers","createAlarmIntervalItems","textFieldAttrs","removeAlarm","attachDropdown","mainButtonAttrs","childAttrs","addAlarm","encodeURIComponent","window","open","descriptionEditor","setEnabled","showCalendarEventEditDialog","responseMail","handler","recipientsSearchModel","HtmlEditor","d","userSettingsGroupRoot","groupSettings","reduce","acc","gc","setMinHeight","showBorders","setValue","description","setToolbarOptions","alignmentEnabled","fontSizeEnabled","enableToolbar","getTrimmedValue","headerDom","dialogHeaderBarAttrs","middle","trim","create","editDialog","getStartOfTheWeekOffsetForUser","getTimeFormatForUser","addShortcut","Keys","ESC","exec","help","ctrl","assertNotNull","isMobileDevice","setFocusOnLoadFunction","show","showExistingCalendarEventEditDialog","identity","finished","uid","ProgrammingError","posRect","finish","hasUpdateWorthyChanges","console","log","askUserIfUpdatesAreNeededOrCancel","askUserIfInsecurePasswordsAreOk","updateExistingEvent","UpgradeRequiredError","canUseInvites","plans","hasInsecurePasswords","confirm","ReplyButtons","pureComponent","participation","colors","borderColor","content_button","highlightColors","makeStatusButtonAttrs","Object","assign","setParticipation","ownAttendee","BannerButton","CalendarAttendeeStatus","ACCEPTED","TENTATIVE","DECLINED","EventPreviewView","getLocationUrl","memoized","sanitizedDescription","attendees","attendeesCopy","findAttendeeInAddresses","unshift","createCalendarEventAttendee","createEncryptedMailAddress","ADDED","prepareAttendees","renderSectionIndicator","formatEventDuration","renderRepeatRule","repeatRule","renderLocation","renderAttendeesSection","renderAttendanceSection","renderDescription","large","display","rule","frequency","getFrequencyTimeUnit","find","formatRepetitionFrequency","endType","repeatEndTime","getRepeatEndTimeForDisplay","formatDateWithMonth","formatRepetitionEnd","href","rel","attendee","attendeeField","hasError","cleanMailAddress","getAttendeeStatus","trust","osmHref","url","URL","RepeatPeriod","DAILY","WEEKLY","MONTHLY","ANNUALLY","Error","CalendarEventPopup","eventBubbleRect","htmlSanitizer","_shortcuts","handleDeleteButtonClick","receiver","isRepeatingForDeleting","createAsyncDropdown","deleteSingle","confirmDeleteClose","handleEditButtonClick","isRepeatingForEditing","editSingle","editAll","handleSendUpdatesClick","sendUpdates","prepareCalendarDescription","s","sanitizeHTML","blockExternalContent","html","setupShortcuts","bind","min","innerWidth","DROPDOWN_MARGIN","setTimeout","showDropdown","renderSendUpdateButton","renderEditButton","renderDeleteButton","renderCloseButton","canEdit","canDelete","canSendUpdates","Button","modal","backgroundClick","hideAnimation","onClose","shortcuts","popState","edit","E","MouseEvent","R","DELETE","deleteAll","CalendarEventPopupViewModel","calendarModel","hasBusinessFeature","lazyProgenitor","eventModelFactory","uiUpdateCallback","processing","_ownAttendee","clone","getNonOrganizerAttendees","progenitor","calendarEventHasMoreThanOneOccurrencesLeft","oldStatus","excludeDate","NotFoundError","deleteEvent","sequence","err","CalendarMobileHeader","BaseMobileHeader","MobileHeaderMenuButton","newsModel","backAction","viewSlider","focusPreviousColumn","center","MobileHeaderTitle","navConfiguration","OfflineIndicatorMobile","offlineIndicatorModel","getCurrentAttrs","onCreateEvent","injections","ProgressBar","progress","getProgress","CalendarDesktopToolbar","navConfig","renderWeekNumberLabel","backgroundColor","list_alternate_bg","CalendarViewTypeByValue","reverse","BaseTopLevelView","super","userId","viewModel","calendarViewModel","currentViewType","deviceConfig","getDefaultCalendarView","sidebarColumn","ViewColumn","FolderColumnView","drawer","drawerAttrs","button","_createNewEventDialog","SidebarSection","_setUrl","route","param","focus","contentColumn","_renderCalendarViewButtons","_onPressedAddCalendar","_renderCalendars","calendarInvitations","invitation","GroupInvitationFolderRow","ariaLabel","first_col_min_width","first_col_max_width","getGroupColors","BackgroundColumnLayout","navigation_bg","desktopToolbar","renderDesktopToolbar","mobileHeader","renderMobileHeader","header","columnLayout","_onEventSelected","calendarViewType","_viewPeriod","shouldDefaultToAmPmTimeFormat","second_col_min_width","third_col_min_width","third_col_max_width","ViewSlider","_setupShortcuts","streamListeners","keyManager","registerShortcuts","unregisterShortcuts","listener","handleBackButton","ONE","TWO","THREE","T","J","enabled","K","N","dateToUse","calendarInfos","getCalendarInfosCreateIfNeeded","showProgressDialog","mailboxDetails","mailModel","getUserMailboxDetails","mailboxProperties","getMailboxProperties","mailboxGroupRoot","calendarEventModel","getEventWithDefaultTimes","saveNewEvent","showNewCalendarEventEditDialog","duration","unit","dateTime","DateTime","fromJSDate","plus","startOf","toJSDate","minus","calendarViewValues","NavButton","isSelectedPrefix","persistentBackground","startsWith","replace","getCalendarMemberships","_showCreateCalendarDialog","bQ","SubscriptionDialogUtils","checkPaidSubscription","ok","random","properties","createCalendar","isLoaded","getLoaded","values","existingGroupSettings","colorValue","groupRootId","groupRoot","newHiddenCalendars","delete","setHiddenCalendars","cursor","marginLeft","hpad_button","_createCalendarActionDropdown","sharedCalendar","_onPressedEditCalendar","isFreeAccount","showNotAvailableForFreeDialog","showGroupSharingDialog","isApp","GroupType","Calendar","hasCapabilityOnGroup","showCalendarImportDialog","alarmInfoList","exportCalendar","alarms","_confirmDeleteCalendar","calendarName","loadGroupMembers","entityClient","members","ownerMail","userGroupInfo","mailAddress","otherMembers","member","info","confirmed","deleteCalendar","update","newGroupSettings","createGroupSettings","Header","handleBackPress","bottomNav","BottomNav","onNewUrl","args","urlDateParam","luxonDate","fromISO","isValid","setDefaultCalendarView","getViewSlider","dateString","toISODate","selectedEvent","htmlSanitizerPromise","domTarget","currentTarget","HTMLElement","clientX","clientY","calendars","all","getAsync","rect","customer","loadCustomer","ownMailAddresses","getEnabledMailAddressesWithUser","getEventType","isCustomizationEnabledForCustomer","FeatureType","BusinessFeatureEnabled","isNewPaidPlan","resolveCalendarEventProgenitor","popupModel","mode","updateTemporaryEventWithDiff","eventClone","mouseDiff","createCalendarEventEditModel","eventController","progressTracker","_calendarModel","_entityClient","_transientEvents","_loadedMonths","_eventsForDays","freezeMap","_deviceConfig","_hiddenCalendars","getHiddenCalendars","_redrawStream","_draggedEvent","_timeZone","_calendarInvitations","totalWork","monitorHandle","registerMonitorSync","progressMonitor","getMonitor","_calendarInfos","LazyLoaded","loadOrCreateCalendarInfo","it","_redraw","load","thisMonthStart","start","_loadMonthIfNeeded","workDone","finally","completed","NoopProgressMonitor","addEntityListener","updates","eventOwnerGroupId","_entityEventReceived","invitations","getWeekStart","timeToMoveBy","_addTransientEvent","moveEvent","_removeTransientEvent","loadCalendarInfos","transientEventUidsByCalendar","groupByAndMapUniquely","getListId","shortEventsForDay","sortEvent","getDiffIn60mIntervals","isEventBetweenDays","temporaryEvent","_c","innerShortEvents","calendar","findAndRemove","transient","diff","rescheduleEvent","millisecond","response","_addOrUpdateEvent","eventListId","eventMonth","isSameId","_addDaysForEvent","_removeExistingEvent","firstDayTimestamp","loadedMonth","_addDaysForRecurringEvent","_addDaysForLongEvent","calendarEvents","addedOrUpdatedEventsUpdates","promiseMap","isUpdateForTypeRef","UserSettingsGroupRootTypeRef","CalendarEventTypeRef","operation","_removeDaysForEvent","instanceListId","instanceId","UserTypeRef","userGroup","calendarMemberships","ci","every","mb","oldGroupIds","keys","newGroupIds","symmetricDifference","clear","_replaceEvents","GroupInfoTypeRef","updatesPerList","groupBy","ids","loadMultiple","NotAuthorizedError","dayInMonth","_loadEvents","startId","getEventElementMinId","endId","geEventElementMaxId","aggregateShortEvents","aggregateLongEvents","loadReverseRangeBetween","shortEventsResult","elements","newEvents","_cloneEvents","addDaysForEvent","addDaysForRecurringEvent","addDaysForLongEvent","eventToRemove","indexOfOldEvent","findIndex","el","isSameEvent","oldEvent","newMap","dayEvents","findAllAndRemove","splice","diffNow","years","id","ownerGroupId","listIdPart","undefined"],"mappings":"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"}