{"version":3,"file":"mail-view-d81c9caf.js","sources":["../../../src/mail/view/MailReportDialog.ts","../../../src/mail/view/MailGuiUtils.ts","../../../src/mail/view/MinimizedMailEditorOverlay.ts","../../../src/mail/view/MailViewerUtils.ts","../../../src/mail/view/MinimizedEditorOverlay.ts","../../../src/mail/view/MailRow.ts","../../../src/mail/export/Bundler.ts","../../../src/mail/export/Exporter.ts","../../../src/mail/view/MailListView.ts","../../../src/mail/view/MultiItemViewer.ts","../../../src/mail/view/MobileMailActionBar.ts","../../../src/mail/view/MailFolderRow.ts","../../../src/mail/view/MailFoldersView.ts","../../../src/mail/view/EditFoldersDialog.ts","../../../src/mail/view/EventBanner.ts","../../../src/mail/view/MailViewerHeader.ts","../../../src/mail/view/MailViewer.ts","../../../src/mail/view/CollapsedMailView.ts","../../../src/mail/view/ConversationViewer.ts","../../../src/mail/view/MailViewerToolbar.ts","../../../src/mail/view/MobileMailMultiselectionActionBar.ts","../../../src/mail/view/MailFilterButton.ts","../../../src/mail/view/MailView.ts","../../../src/mail/view/EditFolderDialog.ts","../../../src/mail/view/MailViewerViewModel.ts","../../../src/mail/view/MailViewModel.ts","../../../src/mail/view/ConversationViewModel.ts"],"sourcesContent":["import type { Mail, MailboxProperties } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { Checkbox } from \"../../gui/base/Checkbox.js\"\nimport { lang } from \"../../misc/LanguageViewModel\"\nimport m from \"mithril\"\nimport { MailReportType, ReportMovedMailsType } from \"../../api/common/TutanotaConstants\"\nimport { ButtonAttrs, ButtonType } from \"../../gui/base/Button.js\"\nimport { Dialog } from \"../../gui/base/Dialog\"\nimport type { MailboxDetail, MailModel } from \"../model/MailModel\"\nimport { showSnackBar } from \"../../gui/base/SnackBar\"\n\nfunction confirmMailReportDialog(mailModel: MailModel, mailboxDetails: MailboxDetail): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tlet shallRememberDecision = false\n\t\tconst child = () =>\n\t\t\tm(Checkbox, {\n\t\t\t\tlabel: () => lang.get(\"rememberDecision_msg\"),\n\t\t\t\tchecked: shallRememberDecision,\n\t\t\t\tonChecked: (v) => (shallRememberDecision = v),\n\t\t\t\thelpLabel: () => lang.get(\"changeMailSettings_msg\"),\n\t\t\t})\n\n\t\tasync function updateSpamReportSetting(areMailsReported: boolean) {\n\t\t\tif (shallRememberDecision) {\n\t\t\t\tconst reportMovedMails = areMailsReported ? ReportMovedMailsType.AUTOMATICALLY_ONLY_SPAM : ReportMovedMailsType.NEVER\n\t\t\t\tawait mailModel.saveReportMovedMails(mailboxDetails.mailboxGroupRoot, reportMovedMails)\n\t\t\t}\n\n\t\t\tresolve(areMailsReported)\n\t\t\tdialog.close()\n\t\t}\n\n\t\tconst yesButton: ButtonAttrs = {\n\t\t\tlabel: \"yes_label\",\n\t\t\tclick: () => updateSpamReportSetting(true),\n\t\t\ttype: ButtonType.Primary,\n\t\t}\n\t\tconst noButton: ButtonAttrs = {\n\t\t\tlabel: \"no_label\",\n\t\t\tclick: () => updateSpamReportSetting(false),\n\t\t\ttype: ButtonType.Secondary,\n\t\t}\n\n\t\t// onclose is called when dialog is closed by ESC or back button. In this case we don't want to report spam.\n\t\tconst onclose = () => {\n\t\t\tresolve(false)\n\t\t}\n\n\t\tconst dialog = Dialog.confirmMultiple(\n\t\t\t() => lang.get(\"unencryptedTransmission_msg\") + \" \" + lang.get(\"allowOperation_msg\"),\n\t\t\t[noButton, yesButton],\n\t\t\tonclose,\n\t\t\tchild,\n\t\t)\n\t})\n}\n\n/**\n * Check if the user wants to report mails as spam when they are moved to the spam folder and report them.\n * May open a dialog for confirmation and otherwise shows a Snackbar before reporting to the server.\n */\nexport async function reportMailsAutomatically(\n\tmailReportType: MailReportType,\n\tmailModel: MailModel,\n\tmailboxDetails: MailboxDetail,\n\tmails: ReadonlyArray<Mail>,\n): Promise<void> {\n\tif (mailReportType !== MailReportType.SPAM) {\n\t\treturn\n\t}\n\n\tconst mailboxProperties = await mailModel.getMailboxProperties(mailboxDetails.mailboxGroupRoot)\n\tlet allowUndoing = true // decides if a snackbar is shown to prevent the server request\n\n\tlet isReportable = false\n\n\tif (!mailboxProperties || mailboxProperties.reportMovedMails === ReportMovedMailsType.ALWAYS_ASK) {\n\t\tisReportable = await confirmMailReportDialog(mailModel, mailboxDetails)\n\t\tallowUndoing = false\n\t} else if (mailboxProperties.reportMovedMails === ReportMovedMailsType.AUTOMATICALLY_ONLY_SPAM) {\n\t\tisReportable = true\n\t} else if (mailboxProperties.reportMovedMails === ReportMovedMailsType.NEVER) {\n\t\t// no report\n\t}\n\n\tif (isReportable) {\n\t\t// only show the snackbar to undo the report if the user was not asked already\n\t\tif (allowUndoing) {\n\t\t\tlet undoClicked = false\n\t\t\tshowSnackBar({\n\t\t\t\tmessage: \"undoMailReport_msg\",\n\t\t\t\tbutton: {\n\t\t\t\t\tlabel: \"cancel_action\",\n\t\t\t\t\tclick: () => (undoClicked = true),\n\t\t\t\t},\n\t\t\t\tonClose: () => {\n\t\t\t\t\tif (!undoClicked) {\n\t\t\t\t\t\tmailModel.reportMails(mailReportType, mails)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t})\n\t\t} else {\n\t\t\tmailModel.reportMails(mailReportType, mails)\n\t\t}\n\t}\n}\n","import type { MailModel } from \"../model/MailModel\"\nimport type { File as TutanotaFile, Mail, MailFolder } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { createMail } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { LockedError, PreconditionFailedError } from \"../../api/common/error/RestError\"\nimport { Dialog } from \"../../gui/base/Dialog\"\nimport { locator } from \"../../api/main/MainLocator\"\nimport { getFolderIcon, getIndentedFolderNameForDropdown, getMoveTargetFolderSystems } from \"../model/MailUtils\"\nimport { AllIcons } from \"../../gui/base/Icon\"\nimport { Icons } from \"../../gui/base/icons/Icons\"\nimport type { InlineImages } from \"./MailViewer\"\nimport { isApp, isDesktop } from \"../../api/common/Env\"\nimport { assertNotNull, neverNull, noOp, promiseMap } from \"@tutao/tutanota-utils\"\nimport { MailFolderType, MailReportType } from \"../../api/common/TutanotaConstants\"\nimport { getElementId, getListId } from \"../../api/common/utils/EntityUtils\"\nimport { reportMailsAutomatically } from \"./MailReportDialog\"\nimport { DataFile } from \"../../api/common/DataFile\"\nimport { lang, TranslationKey } from \"../../misc/LanguageViewModel\"\nimport { FileController } from \"../../file/FileController\"\nimport { DomRectReadOnlyPolyfilled, Dropdown, PosRect } from \"../../gui/base/Dropdown.js\"\nimport { ButtonSize } from \"../../gui/base/ButtonSize.js\"\nimport { modal } from \"../../gui/base/Modal.js\"\nimport { assertSystemFolderOfType, isOfTypeOrSubfolderOf, isSpamOrTrashFolder } from \"../../api/common/mail/CommonMailUtils.js\"\nimport { ConversationViewModel } from \"./ConversationViewModel.js\"\nimport { size } from \"../../gui/size.js\"\n\nexport async function showDeleteConfirmationDialog(mails: ReadonlyArray<Mail>): Promise<boolean> {\n\tlet trashMails: Mail[] = []\n\tlet moveMails: Mail[] = []\n\tfor (let mail of mails) {\n\t\tconst folder = locator.mailModel.getMailFolder(mail._id[0])\n\t\tconst mailboxDetail = await locator.mailModel.getMailboxDetailsForMailListId(getListId(mail))\n\t\tif (mailboxDetail == null) {\n\t\t\tcontinue\n\t\t}\n\t\tconst isFinalDelete = folder && isSpamOrTrashFolder(mailboxDetail.folders, folder)\n\t\tisFinalDelete ? trashMails.push(mail) : moveMails.push(mail)\n\t}\n\n\tlet confirmationTextId: TranslationKey | null = null\n\n\tif (trashMails.length > 0) {\n\t\tif (moveMails.length > 0) {\n\t\t\tconfirmationTextId = \"finallyDeleteSelectedEmails_msg\"\n\t\t} else {\n\t\t\tconfirmationTextId = \"finallyDeleteEmails_msg\"\n\t\t}\n\t}\n\n\tif (confirmationTextId != null) {\n\t\treturn Dialog.confirm(confirmationTextId, \"ok_action\")\n\t} else {\n\t\treturn Promise.resolve(true)\n\t}\n}\n\n/**\n * @return whether emails were deleted\n */\nexport function promptAndDeleteMails(mailModel: MailModel, mails: ReadonlyArray<Mail>, onConfirm: () => void): Promise<boolean> {\n\treturn showDeleteConfirmationDialog(mails).then((confirmed) => {\n\t\tif (confirmed) {\n\t\t\tonConfirm()\n\t\t\treturn mailModel\n\t\t\t\t.deleteMails(mails)\n\t\t\t\t.then(() => true)\n\t\t\t\t.catch((e) => {\n\t\t\t\t\t//LockedError should no longer be thrown!?!\n\t\t\t\t\tif (e instanceof PreconditionFailedError || e instanceof LockedError) {\n\t\t\t\t\t\treturn Dialog.message(\"operationStillActive_msg\").then(() => false)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow e\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t} else {\n\t\t\treturn Promise.resolve(false)\n\t\t}\n\t})\n}\n\ninterface MoveMailsParams {\n\tmailModel: MailModel\n\tmails: ReadonlyArray<Mail>\n\ttargetMailFolder: MailFolder\n\tisReportable?: boolean\n}\n\n/**\n * Moves the mails and reports them as spam if the user or settings allow it.\n * @return whether mails were actually moved\n */\nexport async function moveMails({ mailModel, mails, targetMailFolder, isReportable = true }: MoveMailsParams): Promise<boolean> {\n\tconst details = await mailModel.getMailboxDetailsForMailListId(targetMailFolder.mails)\n\tif (details == null) {\n\t\treturn false\n\t}\n\tconst system = details.folders\n\treturn mailModel\n\t\t.moveMails(mails, targetMailFolder)\n\t\t.then(async () => {\n\t\t\tif (isOfTypeOrSubfolderOf(system, targetMailFolder, MailFolderType.SPAM) && isReportable) {\n\t\t\t\tconst reportableMails = mails.map((mail) => {\n\t\t\t\t\t// mails have just been moved\n\t\t\t\t\tconst reportableMail = createMail(mail)\n\t\t\t\t\treportableMail._id = [targetMailFolder.mails, getElementId(mail)]\n\t\t\t\t\treturn reportableMail\n\t\t\t\t})\n\t\t\t\tconst mailboxDetails = await mailModel.getMailboxDetailsForMailGroup(assertNotNull(targetMailFolder._ownerGroup))\n\t\t\t\tawait reportMailsAutomatically(MailReportType.SPAM, mailModel, mailboxDetails, reportableMails)\n\t\t\t}\n\n\t\t\treturn true\n\t\t})\n\t\t.catch((e) => {\n\t\t\t//LockedError should no longer be thrown!?!\n\t\t\tif (e instanceof LockedError || e instanceof PreconditionFailedError) {\n\t\t\t\treturn Dialog.message(\"operationStillActive_msg\").then(() => false)\n\t\t\t} else {\n\t\t\t\tthrow e\n\t\t\t}\n\t\t})\n}\n\nexport function archiveMails(mails: Mail[]): Promise<void> {\n\tif (mails.length > 0) {\n\t\t// assume all mails in the array belong to the same Mailbox\n\t\treturn locator.mailModel.getMailboxFolders(mails[0]).then((folders) => {\n\t\t\tfolders &&\n\t\t\t\tmoveMails({\n\t\t\t\t\tmailModel: locator.mailModel,\n\t\t\t\t\tmails: mails,\n\t\t\t\t\ttargetMailFolder: assertSystemFolderOfType(folders, MailFolderType.ARCHIVE),\n\t\t\t\t})\n\t\t})\n\t} else {\n\t\treturn Promise.resolve()\n\t}\n}\n\nexport function moveToInbox(mails: Mail[]): Promise<any> {\n\tif (mails.length > 0) {\n\t\t// assume all mails in the array belong to the same Mailbox\n\t\treturn locator.mailModel.getMailboxFolders(mails[0]).then((folders) => {\n\t\t\tfolders &&\n\t\t\t\tmoveMails({\n\t\t\t\t\tmailModel: locator.mailModel,\n\t\t\t\t\tmails: mails,\n\t\t\t\t\ttargetMailFolder: assertSystemFolderOfType(folders, MailFolderType.INBOX),\n\t\t\t\t})\n\t\t})\n\t} else {\n\t\treturn Promise.resolve()\n\t}\n}\n\nexport function getMailFolderIcon(mail: Mail): AllIcons {\n\tlet folder = locator.mailModel.getMailFolder(mail._id[0])\n\n\tif (folder) {\n\t\treturn getFolderIcon(folder)\n\t} else {\n\t\treturn Icons.Folder\n\t}\n}\n\nexport function replaceCidsWithInlineImages(\n\tdom: HTMLElement,\n\tinlineImages: InlineImages,\n\tonContext: (cid: string, arg1: MouseEvent | TouchEvent, arg2: HTMLElement) => unknown,\n): Array<HTMLElement> {\n\t// all image tags which have cid attribute. The cid attribute has been set by the sanitizer for adding a default image.\n\tconst imageElements: Array<HTMLElement> = Array.from(dom.querySelectorAll(\"img[cid]\"))\n\tif (dom.shadowRoot) {\n\t\tconst shadowImageElements: Array<HTMLElement> = Array.from(dom.shadowRoot.querySelectorAll(\"img[cid]\"))\n\t\timageElements.push(...shadowImageElements)\n\t}\n\tconst elementsWithCid: HTMLElement[] = []\n\timageElements.forEach((imageElement) => {\n\t\tconst cid = imageElement.getAttribute(\"cid\")\n\n\t\tif (cid) {\n\t\t\tconst inlineImage = inlineImages.get(cid)\n\n\t\t\tif (inlineImage) {\n\t\t\t\telementsWithCid.push(imageElement)\n\t\t\t\timageElement.setAttribute(\"src\", inlineImage.objectUrl)\n\t\t\t\timageElement.classList.remove(\"tutanota-placeholder\")\n\n\t\t\t\tif (isApp()) {\n\t\t\t\t\t// Add long press action for apps\n\t\t\t\t\tlet timeoutId: TimeoutID | null\n\t\t\t\t\tlet startCoords:\n\t\t\t\t\t\t| {\n\t\t\t\t\t\t\t\tx: number\n\t\t\t\t\t\t\t\ty: number\n\t\t\t\t\t\t  }\n\t\t\t\t\t\t| null\n\t\t\t\t\t\t| undefined\n\t\t\t\t\timageElement.addEventListener(\"touchstart\", (e: TouchEvent) => {\n\t\t\t\t\t\tconst touch = e.touches[0]\n\t\t\t\t\t\tif (!touch) return\n\t\t\t\t\t\tstartCoords = {\n\t\t\t\t\t\t\tx: touch.clientX,\n\t\t\t\t\t\t\ty: touch.clientY,\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\t\t\tonContext(inlineImage.cid, e, imageElement)\n\t\t\t\t\t\t}, 800)\n\t\t\t\t\t})\n\t\t\t\t\timageElement.addEventListener(\"touchmove\", (e: TouchEvent) => {\n\t\t\t\t\t\tconst touch = e.touches[0]\n\t\t\t\t\t\tif (!touch || !startCoords || !timeoutId) return\n\n\t\t\t\t\t\tif (Math.abs(touch.clientX - startCoords.x) > 40 || Math.abs(touch.clientY - startCoords.y) > 40) {\n\t\t\t\t\t\t\tclearTimeout(timeoutId)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\timageElement.addEventListener(\"touchend\", () => {\n\t\t\t\t\t\ttimeoutId && clearTimeout(timeoutId)\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tif (isDesktop()) {\n\t\t\t\t\t// add right click action for desktop apps\n\t\t\t\t\timageElement.addEventListener(\"contextmenu\", (e: MouseEvent) => {\n\t\t\t\t\t\tonContext(inlineImage.cid, e, imageElement)\n\t\t\t\t\t\te.preventDefault()\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t})\n\treturn elementsWithCid\n}\n\nexport function replaceInlineImagesWithCids(dom: HTMLElement): HTMLElement {\n\tconst domClone = dom.cloneNode(true) as HTMLElement\n\tconst inlineImages: Array<HTMLElement> = Array.from(domClone.querySelectorAll(\"img[cid]\"))\n\tinlineImages.forEach((inlineImage) => {\n\t\tconst cid = inlineImage.getAttribute(\"cid\")\n\t\tinlineImage.setAttribute(\"src\", \"cid:\" + (cid || \"\"))\n\t\tinlineImage.removeAttribute(\"cid\")\n\t})\n\treturn domClone\n}\n\nexport type InlineImageReference = {\n\tcid: string\n\tobjectUrl: string\n\tblob: Blob\n}\n\nexport function createInlineImage(file: DataFile): InlineImageReference {\n\tconst cid = Math.random().toString(30).substring(2)\n\tfile.cid = cid\n\treturn createInlineImageReference(file, cid)\n}\n\nfunction createInlineImageReference(file: DataFile, cid: string): InlineImageReference {\n\tconst blob = new Blob([file.data], {\n\t\ttype: file.mimeType,\n\t})\n\tconst objectUrl = URL.createObjectURL(blob)\n\treturn {\n\t\tcid,\n\t\tobjectUrl,\n\t\tblob,\n\t}\n}\n\nexport function cloneInlineImages(inlineImages: InlineImages): InlineImages {\n\tconst newMap = new Map()\n\tinlineImages.forEach((v, k) => {\n\t\tconst blob = new Blob([v.blob])\n\t\tconst objectUrl = URL.createObjectURL(blob)\n\t\tnewMap.set(k, {\n\t\t\tcid: v.cid,\n\t\t\tobjectUrl,\n\t\t\tblob,\n\t\t})\n\t})\n\treturn newMap\n}\n\nexport function revokeInlineImages(inlineImages: InlineImages): void {\n\tinlineImages.forEach((v, k) => {\n\t\tURL.revokeObjectURL(v.objectUrl)\n\t})\n}\n\nexport async function loadInlineImages(fileController: FileController, attachments: Array<TutanotaFile>, referencedCids: Array<string>): Promise<InlineImages> {\n\tconst filesToLoad = getReferencedAttachments(attachments, referencedCids)\n\tconst inlineImages = new Map()\n\treturn promiseMap(filesToLoad, async (file) => {\n\t\tlet dataFile = await fileController.getAsDataFile(file)\n\t\tconst { htmlSanitizer } = await import(\"../../misc/HtmlSanitizer\")\n\t\tdataFile = htmlSanitizer.sanitizeInlineAttachment(dataFile)\n\t\tconst inlineImageReference = createInlineImageReference(dataFile, neverNull(file.cid))\n\t\tinlineImages.set(inlineImageReference.cid, inlineImageReference)\n\t}).then(() => inlineImages)\n}\n\nexport function getReferencedAttachments(attachments: Array<TutanotaFile>, referencedCids: Array<string>): Array<TutanotaFile> {\n\treturn attachments.filter((file) => referencedCids.find((rcid) => file.cid === rcid))\n}\n\nexport async function showMoveMailsDropdown(\n\tmodel: MailModel,\n\torigin: PosRect,\n\tmails: readonly Mail[],\n\topts?: { width?: number; withBackground?: boolean; onSelected?: () => unknown },\n): Promise<void> {\n\tconst { width = 300, withBackground = false, onSelected = noOp } = opts ?? {}\n\tconst folders = await getMoveTargetFolderSystems(model, mails)\n\tif (folders.length === 0) return\n\tconst folderButtons = folders.map((f) => ({\n\t\tlabel: () => getIndentedFolderNameForDropdown(f),\n\t\tclick: () => {\n\t\t\tonSelected()\n\t\t\tmoveMails({ mailModel: model, mails: mails, targetMailFolder: f.folder })\n\t\t},\n\t\ticon: getFolderIcon(f.folder),\n\t\tsize: ButtonSize.Compact,\n\t}))\n\n\tconst dropdown = new Dropdown(() => folderButtons, width)\n\n\tdropdown.setOrigin(new DomRectReadOnlyPolyfilled(origin.left, origin.top, origin.width, origin.height))\n\tmodal.displayUnique(dropdown, withBackground)\n}\n\nexport function getConversationTitle(conversationViewModel: ConversationViewModel): string {\n\tif (!conversationViewModel.isFinished()) {\n\t\treturn lang.get(\"loading_msg\")\n\t}\n\tconst numberOfEmails = conversationViewModel.conversationItems().length\n\tif (numberOfEmails === 1) {\n\t\treturn lang.get(\"oneEmail_label\")\n\t} else {\n\t\treturn lang.get(\"nbrOrEmails_label\", { \"{number}\": numberOfEmails })\n\t}\n}\n\nexport function getMoveMailBounds(): PosRect {\n\t// just putting the move mail dropdown in the left side of the viewport with a bit of margin\n\treturn new DomRectReadOnlyPolyfilled(size.hpad_large, size.vpad_large, 0, 0)\n}\n","import m from \"mithril\"\nimport { px, size } from \"../../gui/size\"\nimport { displayOverlay } from \"../../gui/base/Overlay\"\nimport { DefaultAnimationTime, transform, TransformEnum } from \"../../gui/animation/Animations\"\nimport { EventController } from \"../../api/main/EventController\"\nimport { styles } from \"../../gui/styles\"\nimport { LayerType } from \"../../RootView\"\nimport type { Dialog } from \"../../gui/base/Dialog\"\nimport type { SendMailModel } from \"../editor/SendMailModel\"\nimport type { MinimizedEditor, SaveStatus } from \"../model/MinimizedMailEditorViewModel\"\nimport { MinimizedMailEditorViewModel } from \"../model/MinimizedMailEditorViewModel\"\nimport { MinimizedEditorOverlay } from \"./MinimizedEditorOverlay\"\nimport { windowFacade } from \"../../misc/WindowFacade\"\nimport { assertMainOrNode } from \"../../api/common/Env\"\nimport Stream from \"mithril/stream\"\nimport { getSafeAreaInsetBottom } from \"../../gui/HtmlUtils.js\"\n\nassertMainOrNode()\nconst MINIMIZED_OVERLAY_WIDTH_WIDE = 350\nconst MINIMIZED_OVERLAY_WIDTH_SMALL = 220\nconst MINIMIZED_EDITOR_HEIGHT = size.button_height + 2 * size.vpad_xs\n\nexport function showMinimizedMailEditor(\n\tdialog: Dialog,\n\tsendMailModel: SendMailModel,\n\tviewModel: MinimizedMailEditorViewModel,\n\teventController: EventController,\n\tdispose: () => void,\n\tsaveStatus: Stream<SaveStatus>,\n): void {\n\tlet closeOverlayFunction = () => Promise.resolve() // will be assigned with the actual close function when overlay is visible.\n\n\tconst minimizedEditor = viewModel.minimizeMailEditor(dialog, sendMailModel, dispose, saveStatus, () => closeOverlayFunction())\n\t// only show overlay once editor is gone\n\tsetTimeout(() => {\n\t\tcloseOverlayFunction = showMinimizedEditorOverlay(viewModel, minimizedEditor, eventController)\n\t}, DefaultAnimationTime)\n}\n\nfunction showMinimizedEditorOverlay(\n\tviewModel: MinimizedMailEditorViewModel,\n\tminimizedEditor: MinimizedEditor,\n\teventController: EventController,\n): () => Promise<void> {\n\tlet overlayDom: HTMLElement | null = null\n\n\tconst resizeListener = () => {\n\t\tif (overlayDom) {\n\t\t\toverlayDom.style.transform = `translateY(${px(-getVerticalOverlayPosition())})`\n\t\t}\n\t}\n\n\twindowFacade.addResizeListener(resizeListener)\n\treturn displayOverlay(\n\t\t() => getOverlayPosition(),\n\t\t{\n\t\t\tview: () =>\n\t\t\t\tm(MinimizedEditorOverlay, {\n\t\t\t\t\tviewModel,\n\t\t\t\t\tminimizedEditor,\n\t\t\t\t\teventController,\n\t\t\t\t}),\n\t\t},\n\t\t(dom) => {\n\t\t\toverlayDom = dom\n\t\t\treturn transform(TransformEnum.TranslateY, 0, -getVerticalOverlayPosition())\n\t\t},\n\t\t(dom) => {\n\t\t\twindowFacade.removeResizeListener(resizeListener)\n\t\t\treturn transform(TransformEnum.TranslateY, -getVerticalOverlayPosition(), 0)\n\t\t},\n\t\t\"minimized-shadow\",\n\t)\n}\n\n/** Position of the top edge of the overlay from the bottom of the containing element. */\nfunction getVerticalOverlayPosition(): number {\n\tconst bottomInset = getSafeAreaInsetBottom()\n\treturn (\n\t\tMINIMIZED_EDITOR_HEIGHT +\n\t\t(styles.isUsingBottomNavigation() // use size.hpad values to keep bottom and right space even\n\t\t\t? size.bottom_nav_bar + size.hpad + bottomInset\n\t\t\t: size.hpad_medium)\n\t)\n}\n\nfunction getOverlayPosition() {\n\treturn {\n\t\tbottom: px(-MINIMIZED_EDITOR_HEIGHT),\n\t\t// position will change with translateY\n\t\tright: styles.isUsingBottomNavigation() ? px(size.hpad) : px(size.hpad_medium),\n\t\twidth: px(styles.isSingleColumnLayout() ? MINIMIZED_OVERLAY_WIDTH_SMALL : MINIMIZED_OVERLAY_WIDTH_WIDE),\n\t\tzIndex: LayerType.LowPriorityOverlay,\n\t}\n}\n","import type { ImageHandler } from \"../model/MailUtils\"\nimport { getMailAddressDisplayText, loadMailDetails } from \"../model/MailUtils\"\nimport { ALLOWED_IMAGE_FORMATS, Keys, MailReportType, MAX_BASE64_IMAGE_SIZE } from \"../../api/common/TutanotaConstants\"\nimport { neverNull, ofClass, uint8ArrayToBase64 } from \"@tutao/tutanota-utils\"\nimport { InfoLink, lang } from \"../../misc/LanguageViewModel\"\nimport { Dialog } from \"../../gui/base/Dialog\"\nimport { DataFile } from \"../../api/common/DataFile\"\nimport { showFileChooser } from \"../../file/FileController.js\"\nimport m from \"mithril\"\nimport { Button, ButtonType } from \"../../gui/base/Button.js\"\nimport { progressIcon } from \"../../gui/base/Icon.js\"\nimport { checkApprovalStatus } from \"../../misc/LoginUtils.js\"\nimport { locator } from \"../../api/main/MainLocator.js\"\nimport { UserError } from \"../../api/main/UserError.js\"\nimport { showUserError } from \"../../misc/ErrorHandlerImpl.js\"\nimport { ContentBlockingStatus, MailViewerViewModel } from \"./MailViewerViewModel.js\"\nimport { DropdownButtonAttrs } from \"../../gui/base/Dropdown.js\"\nimport { BootIcons } from \"../../gui/base/icons/BootIcons.js\"\nimport { Icons } from \"../../gui/base/icons/Icons.js\"\nimport { client } from \"../../misc/ClientDetector.js\"\nimport { showProgressDialog } from \"../../gui/dialogs/ProgressDialog.js\"\nimport { LockedError } from \"../../api/common/error/RestError.js\"\nimport { ifAllowedTutanotaLinks } from \"../../gui/base/GuiUtils.js\"\n\nexport function insertInlineImageB64ClickHandler(ev: Event, handler: ImageHandler) {\n\tshowFileChooser(true, ALLOWED_IMAGE_FORMATS).then((files) => {\n\t\tconst tooBig: DataFile[] = []\n\n\t\tfor (let file of files) {\n\t\t\tif (file.size > MAX_BASE64_IMAGE_SIZE) {\n\t\t\t\ttooBig.push(file)\n\t\t\t} else {\n\t\t\t\tconst b64 = uint8ArrayToBase64(file.data)\n\t\t\t\tconst dataUrlString = `data:${file.mimeType};base64,${b64}`\n\t\t\t\thandler.insertImage(dataUrlString, {\n\t\t\t\t\tstyle: \"max-width: 100%\",\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tif (tooBig.length > 0) {\n\t\t\tDialog.message(() =>\n\t\t\t\tlang.get(\"tooBigInlineImages_msg\", {\n\t\t\t\t\t\"{size}\": MAX_BASE64_IMAGE_SIZE / 1024,\n\t\t\t\t}),\n\t\t\t)\n\t\t}\n\t})\n}\n\nexport async function showHeaderDialog(headersPromise: Promise<string | null>) {\n\tlet state: { state: \"loading\" } | { state: \"loaded\"; headers: string | null } = { state: \"loading\" }\n\n\theadersPromise.then((headers) => {\n\t\tstate = { state: \"loaded\", headers }\n\t\tm.redraw()\n\t})\n\n\tlet mailHeadersDialog: Dialog\n\tconst closeHeadersAction = () => {\n\t\tmailHeadersDialog?.close()\n\t}\n\n\tmailHeadersDialog = Dialog.largeDialog(\n\t\t{\n\t\t\tright: [\n\t\t\t\t{\n\t\t\t\t\tlabel: \"ok_action\",\n\t\t\t\t\tclick: closeHeadersAction,\n\t\t\t\t\ttype: ButtonType.Secondary,\n\t\t\t\t},\n\t\t\t],\n\t\t\tmiddle: () => lang.get(\"mailHeaders_title\"),\n\t\t},\n\t\t{\n\t\t\tview: () =>\n\t\t\t\tm(\n\t\t\t\t\t\".white-space-pre.pt.pb.selectable\",\n\t\t\t\t\tstate.state === \"loading\" ? m(\".center\", progressIcon()) : state.headers ?? m(\".center\", lang.get(\"noEntries_msg\")),\n\t\t\t\t),\n\t\t},\n\t)\n\t\t.addShortcut({\n\t\t\tkey: Keys.ESC,\n\t\t\texec: closeHeadersAction,\n\t\t\thelp: \"close_alt\",\n\t\t})\n\t\t.setCloseHandler(closeHeadersAction)\n\t\t.show()\n}\n\nexport async function editDraft(viewModel: MailViewerViewModel): Promise<void> {\n\tconst sendAllowed = await checkApprovalStatus(locator.logins, false)\n\tif (sendAllowed) {\n\t\t// check if to be opened draft has already been minimized, iff that is the case, re-open it\n\t\tconst minimizedEditor = locator.minimizedMailModel.getEditorForDraft(viewModel.mail)\n\n\t\tif (minimizedEditor) {\n\t\t\tlocator.minimizedMailModel.reopenMinimizedEditor(minimizedEditor)\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tconst [mailboxDetails, { newMailEditorFromDraft }] = await Promise.all([\n\t\t\t\t\tviewModel.mailModel.getMailboxDetailsForMail(viewModel.mail),\n\t\t\t\t\timport(\"../editor/MailEditor\"),\n\t\t\t\t])\n\t\t\t\tif (mailboxDetails == null) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst editorDialog = await newMailEditorFromDraft(\n\t\t\t\t\tviewModel.getAttachments(),\n\t\t\t\t\tawait loadMailDetails(locator.entityClient, viewModel.mail),\n\t\t\t\t\tviewModel.isBlockingExternalImages(),\n\t\t\t\t\tviewModel.getLoadedInlineImages(),\n\t\t\t\t\tmailboxDetails,\n\t\t\t\t)\n\t\t\t\teditorDialog.show()\n\t\t\t} catch (e) {\n\t\t\t\tif (e instanceof UserError) {\n\t\t\t\t\tawait showUserError(e)\n\t\t\t\t} else {\n\t\t\t\t\tthrow e\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Make options for \"assign\" buttons (for cases for mails with restricted participants). */\nexport async function makeAssignMailsButtons(viewModel: MailViewerViewModel): Promise<DropdownButtonAttrs[]> {\n\tconst assignmentGroupInfos = await viewModel.getAssignmentGroupInfos()\n\n\treturn assignmentGroupInfos.map((userOrMailGroupInfo) => {\n\t\treturn {\n\t\t\tlabel: () => getMailAddressDisplayText(userOrMailGroupInfo.name, neverNull(userOrMailGroupInfo.mailAddress), true),\n\t\t\ticon: BootIcons.Contacts,\n\t\t\tclick: () => viewModel.assignMail(userOrMailGroupInfo),\n\t\t}\n\t})\n}\n\nexport function mailViewerMoreActions(viewModel: MailViewerViewModel, showReadButton: boolean = true): Array<DropdownButtonAttrs> {\n\tconst moreButtons: Array<DropdownButtonAttrs> = []\n\tif (showReadButton) {\n\t\tif (viewModel.isUnread()) {\n\t\t\tmoreButtons.push({\n\t\t\t\tlabel: \"markRead_action\",\n\t\t\t\tclick: () => viewModel.setUnread(false),\n\t\t\t\ticon: Icons.Eye,\n\t\t\t})\n\t\t} else {\n\t\t\tmoreButtons.push({\n\t\t\t\tlabel: \"markUnread_action\",\n\t\t\t\tclick: () => viewModel.setUnread(true),\n\t\t\t\ticon: Icons.NoEye,\n\t\t\t})\n\t\t}\n\t}\n\n\tif (!client.isMobileDevice() && viewModel.canExport()) {\n\t\tmoreButtons.push({\n\t\t\tlabel: \"export_action\",\n\t\t\tclick: () => showProgressDialog(\"pleaseWait_msg\", viewModel.exportMail()),\n\t\t\ticon: Icons.Export,\n\t\t})\n\t}\n\n\tif (!client.isMobileDevice() && typeof window.print === \"function\" && viewModel.canPrint()) {\n\t\tmoreButtons.push({\n\t\t\tlabel: \"print_action\",\n\t\t\tclick: () => window.print(),\n\t\t\ticon: Icons.Print,\n\t\t})\n\t}\n\n\tif (viewModel.isListUnsubscribe()) {\n\t\tmoreButtons.push({\n\t\t\tlabel: \"unsubscribe_action\",\n\t\t\tclick: () => unsubscribe(viewModel),\n\t\t\ticon: Icons.Cancel,\n\t\t})\n\t}\n\n\tif (viewModel.canShowHeaders()) {\n\t\tmoreButtons.push({\n\t\t\tlabel: \"showHeaders_action\",\n\t\t\tclick: () => showHeaderDialog(viewModel.getHeaders()),\n\t\t\ticon: Icons.ListUnordered,\n\t\t})\n\t}\n\n\tif (viewModel.canReport()) {\n\t\tmoreButtons.push({\n\t\t\tlabel: \"reportEmail_action\",\n\t\t\tclick: () => reportMail(viewModel),\n\t\t\ticon: Icons.Warning,\n\t\t})\n\t}\n\n\tif (viewModel.canPersistBlockingStatus() && viewModel.isShowingExternalContent()) {\n\t\tmoreButtons.push({\n\t\t\tlabel: \"disallowExternalContent_action\",\n\t\t\tclick: () => viewModel.setContentBlockingStatus(ContentBlockingStatus.Block),\n\t\t\ticon: Icons.Picture,\n\t\t})\n\t}\n\n\tif (viewModel.canPersistBlockingStatus() && viewModel.isBlockingExternalImages()) {\n\t\tmoreButtons.push({\n\t\t\tlabel: \"showImages_action\",\n\t\t\tclick: () => viewModel.setContentBlockingStatus(ContentBlockingStatus.Show),\n\t\t\ticon: Icons.Picture,\n\t\t})\n\t}\n\n\treturn moreButtons\n}\n\nfunction unsubscribe(viewModel: MailViewerViewModel): Promise<void> {\n\treturn showProgressDialog(\"pleaseWait_msg\", viewModel.unsubscribe())\n\t\t.then((success) => {\n\t\t\tif (success) {\n\t\t\t\treturn Dialog.message(\"unsubscribeSuccessful_msg\")\n\t\t\t}\n\t\t})\n\t\t.catch((e) => {\n\t\t\tif (e instanceof LockedError) {\n\t\t\t\treturn Dialog.message(\"operationStillActive_msg\")\n\t\t\t} else {\n\t\t\t\treturn Dialog.message(\"unsubscribeFailed_msg\")\n\t\t\t}\n\t\t})\n}\n\nfunction reportMail(viewModel: MailViewerViewModel) {\n\tconst sendReport = (reportType: MailReportType) => {\n\t\tviewModel\n\t\t\t.reportMail(reportType)\n\t\t\t.catch(ofClass(LockedError, () => Dialog.message(\"operationStillActive_msg\")))\n\t\t\t.finally(m.redraw)\n\t}\n\n\tconst dialog = Dialog.showActionDialog({\n\t\ttitle: lang.get(\"reportEmail_action\"),\n\t\tchild: () =>\n\t\t\tm(\n\t\t\t\t\".flex.col.mt-m\",\n\t\t\t\t{\n\t\t\t\t\t// So that space below buttons doesn't look huge\n\t\t\t\t\tstyle: {\n\t\t\t\t\t\tmarginBottom: \"-10px\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t[\n\t\t\t\t\tm(\"div\", lang.get(\"phishingReport_msg\")),\n\t\t\t\t\tifAllowedTutanotaLinks(locator.logins, InfoLink.Phishing, (link) =>\n\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\"a.mt-s\",\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\thref: link,\n\t\t\t\t\t\t\t\ttarget: \"_blank\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tlang.get(\"whatIsPhishing_msg\"),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t\tm(\".flex-wrap.flex-end\", [\n\t\t\t\t\t\tm(Button, {\n\t\t\t\t\t\t\tlabel: \"reportPhishing_action\",\n\t\t\t\t\t\t\tclick: () => {\n\t\t\t\t\t\t\t\tsendReport(MailReportType.PHISHING)\n\t\t\t\t\t\t\t\tdialog.close()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\ttype: ButtonType.Secondary,\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tm(Button, {\n\t\t\t\t\t\t\tlabel: \"reportSpam_action\",\n\t\t\t\t\t\t\tclick: () => {\n\t\t\t\t\t\t\t\tsendReport(MailReportType.SPAM)\n\t\t\t\t\t\t\t\tdialog.close()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\ttype: ButtonType.Secondary,\n\t\t\t\t\t\t}),\n\t\t\t\t\t]),\n\t\t\t\t],\n\t\t\t),\n\t\tokAction: null,\n\t})\n}\n","import m, { Children, Component, Vnode } from \"mithril\"\nimport { CounterBadge } from \"../../gui/base/CounterBadge\"\nimport { getNavButtonIconBackground, theme } from \"../../gui/theme\"\nimport { lang } from \"../../misc/LanguageViewModel\"\nimport { ButtonColor, Button, ButtonType } from \"../../gui/base/Button.js\"\nimport type { MinimizedEditor, MinimizedMailEditorViewModel } from \"../model/MinimizedMailEditorViewModel\"\nimport { SaveErrorReason, SaveStatus, SaveStatusEnum } from \"../model/MinimizedMailEditorViewModel\"\nimport { px } from \"../../gui/size\"\nimport { Icons } from \"../../gui/base/icons/Icons\"\nimport { styles } from \"../../gui/styles\"\nimport type { EntityEventsListener, EntityUpdateData, EventController } from \"../../api/main/EventController\"\nimport { isUpdateForTypeRef } from \"../../api/main/EventController\"\nimport { promptAndDeleteMails } from \"./MailGuiUtils\"\nimport { MailTypeRef } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { OperationType } from \"../../api/common/TutanotaConstants\"\nimport { isSameId } from \"../../api/common/utils/EntityUtils\"\nimport { noOp, promiseMap } from \"@tutao/tutanota-utils\"\n\nconst COUNTER_POS_OFFSET = px(-8)\nexport type MinimizedEditorOverlayAttrs = {\n\tviewModel: MinimizedMailEditorViewModel\n\tminimizedEditor: MinimizedEditor\n\teventController: EventController\n}\n\nexport class MinimizedEditorOverlay implements Component<MinimizedEditorOverlayAttrs> {\n\t_listener: EntityEventsListener\n\t_eventController: EventController\n\n\tconstructor(vnode: Vnode<MinimizedEditorOverlayAttrs>) {\n\t\tconst { minimizedEditor, viewModel, eventController } = vnode.attrs\n\t\tthis._eventController = eventController\n\n\t\tthis._listener = (updates: ReadonlyArray<EntityUpdateData>, eventOwnerGroupId: Id): Promise<unknown> => {\n\t\t\treturn promiseMap(updates, (update) => {\n\t\t\t\tif (isUpdateForTypeRef(MailTypeRef, update) && update.operation === OperationType.DELETE) {\n\t\t\t\t\tlet draft = minimizedEditor.sendMailModel.getDraft()\n\n\t\t\t\t\tif (draft && isSameId(draft._id, [update.instanceListId, update.instanceId])) {\n\t\t\t\t\t\tviewModel.removeMinimizedEditor(minimizedEditor)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\n\t\teventController.addEntityListener(this._listener)\n\t}\n\n\tonremove() {\n\t\tthis._eventController.removeEntityListener(this._listener)\n\t}\n\n\tview(vnode: Vnode<MinimizedEditorOverlayAttrs>): Children {\n\t\tconst { minimizedEditor, viewModel, eventController } = vnode.attrs\n\t\tconst subject = minimizedEditor.sendMailModel.getSubject()\n\t\treturn m(\".elevated-bg.pl.border-radius\", [\n\t\t\tm(CounterBadge, {\n\t\t\t\tcount: viewModel.getMinimizedEditors().indexOf(minimizedEditor) + 1,\n\t\t\t\tposition: {\n\t\t\t\t\ttop: COUNTER_POS_OFFSET,\n\t\t\t\t\tright: COUNTER_POS_OFFSET,\n\t\t\t\t},\n\t\t\t\tcolor: theme.navigation_button_icon,\n\t\t\t\tbackground: getNavButtonIconBackground(),\n\t\t\t}),\n\t\t\tm(\".flex.justify-between.pb-xs.pt-xs\", [\n\t\t\t\tm(\n\t\t\t\t\t\".flex.col.justify-center.min-width-0.flex-grow\",\n\t\t\t\t\t{\n\t\t\t\t\t\tonclick: () => viewModel.reopenMinimizedEditor(minimizedEditor),\n\t\t\t\t\t},\n\t\t\t\t\t[\n\t\t\t\t\t\tm(\".b.text-ellipsis\", subject ? subject : lang.get(\"newMail_action\")),\n\t\t\t\t\t\tm(\".small.text-ellipsis\", getStatusMessage(minimizedEditor.saveStatus())),\n\t\t\t\t\t],\n\t\t\t\t),\n\t\t\t\tm(\".flex.items-center.justify-right\", [\n\t\t\t\t\t!styles.isSingleColumnLayout()\n\t\t\t\t\t\t? m(Button, {\n\t\t\t\t\t\t\t\tlabel: \"edit_action\",\n\t\t\t\t\t\t\t\tclick: () => viewModel.reopenMinimizedEditor(minimizedEditor),\n\t\t\t\t\t\t\t\ttype: ButtonType.ActionLarge,\n\t\t\t\t\t\t\t\ticon: () => Icons.Edit,\n\t\t\t\t\t\t\t\tcolors: ButtonColor.DrawerNav,\n\t\t\t\t\t\t  })\n\t\t\t\t\t\t: null,\n\t\t\t\t\tm(Button, {\n\t\t\t\t\t\tlabel: \"delete_action\",\n\t\t\t\t\t\tclick: () => this._onDeleteClicked(minimizedEditor, viewModel),\n\t\t\t\t\t\ttype: ButtonType.ActionLarge,\n\t\t\t\t\t\ticon: () => Icons.Trash,\n\t\t\t\t\t\tcolors: ButtonColor.DrawerNav,\n\t\t\t\t\t}),\n\t\t\t\t\tm(Button, {\n\t\t\t\t\t\tlabel: \"close_alt\",\n\t\t\t\t\t\tclick: () => viewModel.removeMinimizedEditor(minimizedEditor),\n\t\t\t\t\t\ttype: ButtonType.ActionLarge,\n\t\t\t\t\t\ticon: () => Icons.Cancel,\n\t\t\t\t\t\tcolors: ButtonColor.DrawerNav,\n\t\t\t\t\t}),\n\t\t\t\t]),\n\t\t\t]),\n\t\t])\n\t}\n\n\tprivate _onDeleteClicked(minimizedEditor: MinimizedEditor, viewModel: MinimizedMailEditorViewModel) {\n\t\tconst model = minimizedEditor.sendMailModel\n\t\tviewModel.removeMinimizedEditor(minimizedEditor)\n\t\t// only delete once save has finished\n\t\tminimizedEditor.saveStatus.map(async ({ status }) => {\n\t\t\tif (status !== SaveStatusEnum.Saving) {\n\t\t\t\tconst draft = model.draft\n\n\t\t\t\tif (draft) {\n\t\t\t\t\tawait promptAndDeleteMails(model.mailModel, [draft], noOp)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunction getStatusMessage(saveStatus: SaveStatus): string {\n\tswitch (saveStatus.status) {\n\t\tcase SaveStatusEnum.Saving:\n\t\t\treturn lang.get(\"save_msg\")\n\t\tcase SaveStatusEnum.NotSaved:\n\t\t\tswitch (saveStatus.reason) {\n\t\t\t\tcase SaveErrorReason.ConnectionLost:\n\t\t\t\t\treturn lang.get(\"draftNotSavedConnectionLost_msg\")\n\t\t\t\tdefault:\n\t\t\t\t\treturn lang.get(\"draftNotSaved_msg\")\n\t\t\t}\n\t\tcase SaveStatusEnum.Saved:\n\t\t\treturn lang.get(\"draftSaved_msg\")\n\t\tdefault:\n\t\t\treturn \"\"\n\t}\n}\n","import { getMailFolderType, MailFolderType, MailState, ReplyType } from \"../../api/common/TutanotaConstants\"\nimport { FontIcons } from \"../../gui/base/icons/FontIcons\"\nimport type { Mail } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { formatTimeOrDateOrYesterday } from \"../../misc/Formatter\"\nimport { getSenderOrRecipientHeading, isTutanotaTeamMail } from \"../model/MailUtils\"\nimport { locator } from \"../../api/main/MainLocator\"\nimport m, { Children } from \"mithril\"\nimport Badge from \"../../gui/base/Badge\"\n\nimport {\n\tcheckboxOpacity,\n\tscaleXHide,\n\tscaleXShow,\n\tselectableRowAnimParams,\n\tSelectableRowContainer,\n\tSelectableRowSelectedSetter,\n\tsetVisibility,\n\tshouldAlwaysShowMultiselectCheckbox,\n} from \"../../gui/SelectableRowContainer.js\"\nimport { px, size } from \"../../gui/size.js\"\nimport { NBSP, noOp } from \"@tutao/tutanota-utils\"\nimport { VirtualRow } from \"../../gui/base/ListUtils.js\"\n\nconst iconMap: Record<MailFolderType, string> = {\n\t[MailFolderType.CUSTOM]: FontIcons.Folder,\n\t[MailFolderType.INBOX]: FontIcons.Inbox,\n\t[MailFolderType.SENT]: FontIcons.Sent,\n\t[MailFolderType.TRASH]: FontIcons.Trash,\n\t[MailFolderType.ARCHIVE]: FontIcons.Archive,\n\t[MailFolderType.SPAM]: FontIcons.Spam,\n\t[MailFolderType.DRAFT]: FontIcons.Draft,\n}\n\nexport const MAIL_ROW_V_MARGIN = 3\n\nconst shiftByForCheckbox = px(10)\nconst translateXHide = \"translateX(0)\"\nconst translateXShow = `translateX(${shiftByForCheckbox})`\n\nexport class MailRow implements VirtualRow<Mail> {\n\ttop: number\n\tdomElement: HTMLElement | null = null // set from List\n\n\tentity: Mail | null = null\n\tprivate subjectDom!: HTMLElement\n\tprivate senderDom!: HTMLElement\n\tprivate dateDom!: HTMLElement\n\tprivate iconsDom!: HTMLElement\n\tprivate unreadDom!: HTMLElement\n\tprivate folderIconsDom: Record<MailFolderType, HTMLElement>\n\tprivate teamLabelDom!: HTMLElement\n\tprivate checkboxDom!: HTMLInputElement\n\tprivate checkboxWasVisible = shouldAlwaysShowMultiselectCheckbox()\n\tprivate selectionSetter!: SelectableRowSelectedSetter\n\n\tconstructor(private readonly showFolderIcon: boolean, private readonly onSelected: (mail: Mail, selected: boolean) => unknown) {\n\t\tthis.top = 0\n\t\tthis.entity = null\n\t\tthis.folderIconsDom = {} as Record<MailFolderType, HTMLElement>\n\t}\n\n\tupdate(mail: Mail, selected: boolean, isInMultiSelect: boolean): void {\n\t\tthis.entity = mail\n\n\t\tthis.selectionSetter(selected, isInMultiSelect)\n\t\tthis.checkboxDom.checked = isInMultiSelect && selected\n\n\t\tthis.iconsDom.textContent = this.iconsText(mail)\n\t\tthis.dateDom.textContent = formatTimeOrDateOrYesterday(mail.receivedDate)\n\t\tthis.senderDom.textContent = getSenderOrRecipientHeading(mail, true)\n\t\tthis.subjectDom.textContent = mail.subject || NBSP\n\n\t\tif (mail.unread) {\n\t\t\tthis.unreadDom.classList.remove(\"hidden\")\n\n\t\t\tthis.subjectDom.classList.add(\"b\")\n\t\t\tthis.senderDom.classList.add(\"b\")\n\t\t} else {\n\t\t\tthis.unreadDom.classList.add(\"hidden\")\n\n\t\t\tthis.subjectDom.classList.remove(\"b\")\n\t\t\tthis.senderDom.classList.remove(\"b\")\n\t\t}\n\n\t\tsetVisibility(this.teamLabelDom, isTutanotaTeamMail(mail))\n\t\tthis.showCheckboxAnimated(shouldAlwaysShowMultiselectCheckbox() || isInMultiSelect)\n\n\t\tcheckboxOpacity(this.checkboxDom, selected)\n\t}\n\n\tprivate showCheckboxAnimated(show: boolean) {\n\t\t// this causes a slide animation where checkbox pops up and the text is shifted to make space for it.\n\t\t// we can't animate the width of the checkbox as it causes the layout shifts and is very slow so instead we change the padding of the text elements in\n\t\t// a single step and then shift them in an animation. The effect is almost the same as if we would expand/shrink the checkbox.\n\t\t// using requestAnimationFrame() because when we toggle it some elements might not be there yet. Could also for the end of the event loop too.\n\t\t// using web animations to be able to cancel them easily. Could probably use transition and listen for the end instead but it would be harder to\n\t\t// do the bookkeeping.\n\t\t// using noOp to catch rejection when the animation is cancelled\n\t\tconst shouldShowCheckbox = show\n\t\tif (this.checkboxWasVisible === shouldShowCheckbox) return\n\n\t\tif (shouldShowCheckbox) {\n\t\t\tthis.senderDom.style.paddingRight = shiftByForCheckbox\n\t\t\tthis.subjectDom.style.paddingRight = shiftByForCheckbox\n\n\t\t\tconst showTranslateTransform = { transform: [translateXHide, translateXShow] }\n\t\t\tconst senderAnim = this.senderDom.animate(showTranslateTransform, selectableRowAnimParams)\n\t\t\tconst subjectAnim = this.subjectDom.animate(showTranslateTransform, selectableRowAnimParams)\n\t\t\tconst badgeAnim = this.teamLabelDom.animate(showTranslateTransform, selectableRowAnimParams)\n\t\t\tconst checkboxAnim = this.checkboxDom.animate({ transform: [scaleXHide, scaleXShow] }, selectableRowAnimParams)\n\n\t\t\tPromise.all([senderAnim.finished, subjectAnim.finished, checkboxAnim.finished]).then(() => {\n\t\t\t\tthis.showCheckbox(true)\n\n\t\t\t\tsenderAnim.cancel()\n\t\t\t\tsubjectAnim.cancel()\n\t\t\t\tbadgeAnim.cancel()\n\t\t\t\tcheckboxAnim.cancel()\n\t\t\t}, noOp)\n\t\t} else {\n\t\t\tthis.senderDom.style.paddingRight = \"0\"\n\t\t\tthis.subjectDom.style.paddingRight = \"0\"\n\n\t\t\tconst hideTranslateTransform = { transform: [translateXShow, translateXHide] }\n\t\t\tconst senderAnim = this.senderDom.animate(hideTranslateTransform, selectableRowAnimParams)\n\t\t\tconst subjectAnim = this.subjectDom.animate(hideTranslateTransform, selectableRowAnimParams)\n\t\t\tconst badgeAnim = this.teamLabelDom.animate(hideTranslateTransform, selectableRowAnimParams)\n\t\t\tconst checkboxAnim = this.checkboxDom.animate({ transform: [scaleXShow, scaleXHide] }, selectableRowAnimParams)\n\n\t\t\tPromise.all([senderAnim.finished, subjectAnim.finished, checkboxAnim.finished]).then(() => {\n\t\t\t\tthis.showCheckbox(false)\n\n\t\t\t\tsenderAnim.cancel()\n\t\t\t\tsubjectAnim.cancel()\n\t\t\t\tbadgeAnim.cancel()\n\t\t\t\tcheckboxAnim.cancel()\n\t\t\t}, noOp)\n\t\t}\n\t\tthis.checkboxWasVisible = shouldShowCheckbox\n\t}\n\n\tprivate showCheckbox(show: boolean) {\n\t\tlet translate\n\t\tlet scale\n\t\tlet padding\n\t\tif (show) {\n\t\t\ttranslate = translateXShow\n\t\t\tscale = scaleXShow\n\t\t\tpadding = shiftByForCheckbox\n\t\t} else {\n\t\t\ttranslate = translateXHide\n\t\t\tscale = scaleXHide\n\t\t\tpadding = \"0\"\n\t\t}\n\t\tthis.senderDom.style.transform = translate\n\t\tthis.subjectDom.style.transform = translate\n\t\tthis.teamLabelDom.style.transform = translate\n\t\tthis.checkboxDom.style.transform = scale\n\n\t\tthis.senderDom.style.paddingRight = padding\n\t\tthis.subjectDom.style.paddingRight = padding\n\n\t\t// we effectively remove it from interaction\n\t\tthis.checkboxDom.disabled = !show\n\t\tthis.checkboxDom.tabIndex = show ? 0 : -1\n\t}\n\n\t/**\n\t * Only the structure is managed by mithril. We set all contents on our own (see update) in order to avoid the vdom overhead (not negligible on mobiles)\n\t */\n\trender(): Children {\n\t\treturn m(\n\t\t\tSelectableRowContainer,\n\t\t\t{\n\t\t\t\tonSelectedChangeRef: (changer) => {\n\t\t\t\t\tthis.selectionSetter = changer\n\t\t\t\t},\n\t\t\t\toncreate: () => {\n\t\t\t\t\t// doing it right away to avoid visual glitch of it appearing/disappearing\n\t\t\t\t\t// but doing it at the end of the event loop because we touch other DOM elements too which might not be there yet\n\t\t\t\t\tPromise.resolve().then(() => this.showCheckbox(shouldAlwaysShowMultiselectCheckbox()))\n\t\t\t\t},\n\t\t\t},\n\t\t\t[\n\t\t\t\tm(\n\t\t\t\t\t\".flex.col.items-center.flex-no-grow.no-shrink.pt-xs.abs\",\n\t\t\t\t\tm(\"input.checkbox.list-checkbox\", {\n\t\t\t\t\t\ttype: \"checkbox\",\n\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\tmarginBottom: \"7px\",\n\t\t\t\t\t\t\ttransformOrigin: \"left\",\n\t\t\t\t\t\t},\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\t// e.redraw = false\n\t\t\t\t\t\t},\n\t\t\t\t\t\tonchange: () => {\n\t\t\t\t\t\t\tthis.entity && this.onSelected(this.entity, this.checkboxDom.checked)\n\t\t\t\t\t\t},\n\t\t\t\t\t\toncreate: (vnode) => {\n\t\t\t\t\t\t\tthis.checkboxDom = vnode.dom as HTMLInputElement\n\t\t\t\t\t\t\tcheckboxOpacity(this.checkboxDom, false)\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t\tm(\".dot.bg-accent-fg.hidden\", {\n\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\tmarginTop: px(MAIL_ROW_V_MARGIN),\n\t\t\t\t\t\t},\n\t\t\t\t\t\toncreate: (vnode) => (this.unreadDom = vnode.dom as HTMLElement),\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t\tm(\n\t\t\t\t\t\".flex-grow.min-width-0\",\n\t\t\t\t\t{\n\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\tmarginLeft: px(size.checkbox_size + size.vpad_xs),\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(\".flex.badge-line-height\", [\n\t\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\tBadge,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tclasses: \".small.mr-s\",\n\t\t\t\t\t\t\t\t\toncreate: (vnode) => (this.teamLabelDom = vnode.dom as HTMLElement),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"Tutanota Team\",\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tm(\".text-ellipsis\", {\n\t\t\t\t\t\t\t\toncreate: (vnode) => (this.senderDom = vnode.dom as HTMLElement),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\tm(\".flex-grow\"),\n\t\t\t\t\t\t\tm(\"small.text-ellipsis.flex-fixed\", {\n\t\t\t\t\t\t\t\toncreate: (vnode) => (this.dateDom = vnode.dom as HTMLElement),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t]),\n\t\t\t\t\t\tm(\".flex.mt-xxs\", [\n\t\t\t\t\t\t\tm(\".smaller.text-ellipsis\", {\n\t\t\t\t\t\t\t\toncreate: (vnode) => (this.subjectDom = vnode.dom as HTMLElement),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\tm(\".flex-grow\"),\n\t\t\t\t\t\t\tm(\"span.ion.ml-s.list-font-icons\", {\n\t\t\t\t\t\t\t\toncreate: (vnode) => (this.iconsDom = vnode.dom as HTMLElement),\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\tprivate iconsText(mail: Mail): string {\n\t\tlet iconText = \"\"\n\n\t\tif (this.showFolderIcon) {\n\t\t\tlet folder = locator.mailModel.getMailFolder(mail._id[0])\n\t\t\ticonText += folder ? this.folderIcon(getMailFolderType(folder)) : \"\"\n\t\t}\n\n\t\ticonText += mail._errors ? FontIcons.Warning : \"\"\n\n\t\tif (mail.state === MailState.DRAFT) {\n\t\t\ticonText += FontIcons.Edit\n\t\t}\n\n\t\tswitch (mail.replyType) {\n\t\t\tcase ReplyType.REPLY:\n\t\t\t\ticonText += FontIcons.Reply\n\t\t\t\tbreak\n\n\t\t\tcase ReplyType.FORWARD:\n\t\t\t\ticonText += FontIcons.Forward\n\t\t\t\tbreak\n\n\t\t\tcase ReplyType.REPLY_FORWARD:\n\t\t\t\ticonText += FontIcons.Reply\n\t\t\t\ticonText += FontIcons.Forward\n\t\t\t\tbreak\n\t\t}\n\n\t\tif (mail.confidential) {\n\t\t\ticonText += FontIcons.Confidential\n\t\t}\n\n\t\tif (mail.attachments.length > 0) {\n\t\t\ticonText += FontIcons.Attach\n\t\t}\n\n\t\treturn iconText\n\t}\n\n\tprivate folderIcon(type: MailFolderType): string {\n\t\treturn iconMap[type]\n\t}\n}\n","import type { EncryptedMailAddress, Mail } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { FileTypeRef, MailAddress } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport type { EntityClient } from \"../../api/common/EntityClient\"\nimport { MailState } from \"../../api/common/TutanotaConstants\"\nimport { getLetId } from \"../../api/common/utils/EntityUtils\"\nimport type { HtmlSanitizer } from \"../../misc/HtmlSanitizer\"\nimport { promiseMap } from \"@tutao/tutanota-utils\"\nimport { DataFile } from \"../../api/common/DataFile\"\nimport { FileController } from \"../../file/FileController\"\nimport { loadMailDetails, loadMailHeaders } from \"../model/MailUtils.js\"\n\n/**\n * Used to pass all downloaded mail stuff to the desktop side to be exported as a file\n * Ideally this would just be {Mail, MailHeaders, MailBody, FileReference[]}\n * but we can't send Dates over to the native side so we may as well just extract everything here\n */\nexport type MailBundleRecipient = {\n\taddress: string\n\tname?: string\n}\nexport type MailBundle = {\n\tmailId: IdTuple\n\tsubject: string\n\tbody: string\n\tsender: MailBundleRecipient\n\tto: MailBundleRecipient[]\n\tcc: MailBundleRecipient[]\n\tbcc: MailBundleRecipient[]\n\treplyTo: MailBundleRecipient[]\n\tisDraft: boolean\n\tisRead: boolean\n\tsentOn: number\n\t// UNIX timestamp\n\treceivedOn: number // UNIX timestamp,\n\theaders: string | null\n\tattachments: DataFile[]\n}\n\n/**\n * Downloads the mail body and the attachments for an email, to prepare for exporting\n */\nexport async function makeMailBundle(mail: Mail, entityClient: EntityClient, fileController: FileController, sanitizer: HtmlSanitizer): Promise<MailBundle> {\n\tconst mailWrapper = await loadMailDetails(entityClient, mail)\n\tconst body = sanitizer.sanitizeHTML(mailWrapper.getMailBodyText(), {\n\t\tblockExternalContent: false,\n\t\tallowRelativeLinks: false,\n\t\tusePlaceholderForInlineImages: false,\n\t}).html\n\n\tconst attachments = await promiseMap(mail.attachments, async (fileId) => {\n\t\tconst file = await entityClient.load(FileTypeRef, fileId)\n\t\treturn await fileController.getAsDataFile(file)\n\t})\n\n\tconst headers = await loadMailHeaders(entityClient, mailWrapper)\n\tconst recipientMapper = ({ address, name }: MailAddress | EncryptedMailAddress) => ({ address, name })\n\n\treturn {\n\t\tmailId: getLetId(mail),\n\t\tsubject: mail.subject,\n\t\tbody,\n\t\tsender: recipientMapper(mail.sender),\n\t\tto: mailWrapper.getToRecipients().map(recipientMapper),\n\t\tcc: mailWrapper.getCcRecipients().map(recipientMapper),\n\t\tbcc: mailWrapper.getBccRecipients().map(recipientMapper),\n\t\treplyTo: mailWrapper.getReplyTos().map(recipientMapper),\n\t\tisDraft: mail.state === MailState.DRAFT,\n\t\tisRead: !mail.unread,\n\t\tsentOn: mailWrapper.getSentDate().getTime(),\n\t\treceivedOn: mail.receivedDate.getTime(),\n\t\theaders,\n\t\tattachments,\n\t}\n}\n","import {\n\tassertNotNull,\n\tformatSortableDateTime,\n\tnoOp,\n\tpad,\n\tpromiseMap,\n\tsortableTimestamp,\n\tstringToBase64,\n\tstringToUtf8Uint8Array,\n\tuint8ArrayToBase64,\n} from \"@tutao/tutanota-utils\"\nimport { createDataFile, DataFile, getCleanedMimeType } from \"../../api/common/DataFile\"\nimport type { MailBundle, MailBundleRecipient } from \"./Bundler\"\nimport { makeMailBundle } from \"./Bundler\"\nimport { isDesktop } from \"../../api/common/Env\"\nimport { sanitizeFilename } from \"../../api/common/utils/FileUtils\"\nimport type { Mail } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport type { EntityClient } from \"../../api/common/EntityClient\"\nimport { locator } from \"../../api/main/MainLocator\"\nimport { FileController, zipDataFiles } from \"../../file/FileController\"\n// .msg export is handled in DesktopFileExport because it uses APIs that can't be loaded web side\nexport type MailExportMode = \"msg\" | \"eml\"\n\nexport async function generateMailFile(bundle: MailBundle, fileName: string, mode: MailExportMode): Promise<DataFile> {\n\treturn mode === \"eml\" ? mailToEmlFile(bundle, fileName) : locator.fileApp.mailToMsg(bundle, fileName)\n}\n\nexport async function getMailExportMode(): Promise<MailExportMode> {\n\tif (isDesktop()) {\n\t\tconst ConfigKeys = await import(\"../../desktop/config/ConfigKeys\")\n\t\tconst mailExportMode = (await locator.desktopSettingsFacade\n\t\t\t.getStringConfigValue(ConfigKeys.DesktopConfigKey.mailExportMode)\n\t\t\t.catch(noOp)) as MailExportMode\n\t\treturn mailExportMode ?? \"eml\"\n\t} else {\n\t\treturn \"eml\"\n\t}\n}\n\nexport function generateExportFileName(subject: string, sentOn: Date, mode: MailExportMode): string {\n\tlet filename = [...formatSortableDateTime(sentOn).split(\" \"), subject].join(\"-\")\n\tfilename = filename.trim()\n\n\tif (filename.length === 0) {\n\t\tfilename = \"unnamed\"\n\t} else if (filename.length > 96) {\n\t\t// windows MAX_PATH is 260, this should be fairly safe.\n\t\tfilename = filename.substring(0, 95) + \"_\"\n\t}\n\n\treturn sanitizeFilename(`${filename}.${mode}`)\n}\n\n/**\n * export mails. a single one will be exported as is, multiple will be put into a zip file\n * a save dialog will then be shown\n * @returns {Promise<void>} resolved after the fileController\n * was instructed to open the new zip File containing the exported files\n */\nexport function exportMails(mails: Array<Mail>, entityClient: EntityClient, fileController: FileController): Promise<void> {\n\tconst downloadPromise = promiseMap(mails, (mail) =>\n\t\timport(\"../../misc/HtmlSanitizer\").then(({ htmlSanitizer }) => makeMailBundle(mail, entityClient, fileController, htmlSanitizer)),\n\t)\n\treturn Promise.all([getMailExportMode(), downloadPromise]).then(([mode, bundles]) => {\n\t\tpromiseMap(bundles, (bundle) => generateMailFile(bundle, generateExportFileName(bundle.subject, new Date(bundle.receivedOn), mode), mode)).then(\n\t\t\t(files) => {\n\t\t\t\tconst zipName = `${sortableTimestamp()}-${mode}-mail-export.zip`\n\t\t\t\tconst maybeZipPromise = files.length === 1 ? Promise.resolve(files[0]) : zipDataFiles(files, zipName)\n\t\t\t\tmaybeZipPromise.then((outputFile) => fileController.saveDataFile(outputFile))\n\t\t\t},\n\t\t)\n\t})\n}\n\nexport function mailToEmlFile(mail: MailBundle, fileName: string): DataFile {\n\tconst data = stringToUtf8Uint8Array(mailToEml(mail))\n\treturn createDataFile(fileName, \"message/rfc822\", data)\n}\n\n/**\n * Converts a mail into the plain text EML format.\n */\nexport function mailToEml(mail: MailBundle): string {\n\tconst lines: string[] = []\n\n\tif (mail.headers) {\n\t\tconst filteredHeaders = mail.headers.split(\"\\n\").filter((line) => !line.match(/^\\s*(Content-Type:|boundary=)/))\n\t\t// We join the headers back together with \\n, but the eml itself has \\r\\n line endings, so the headers are essentially one \"line\" of the eml\n\t\tlines.push(filteredHeaders.join(\"\\n\"))\n\t} else {\n\t\tlines.push(\"From: \" + mail.sender.address, \"MIME-Version: 1.0\")\n\n\t\tconst formatRecipients = (key: string, recipients: MailBundleRecipient[]) =>\n\t\t\t`${key}: ${recipients\n\t\t\t\t.map((recipient) => (recipient.name ? `${escapeSpecialCharacters(recipient.name)} ` : \"\") + `<${recipient.address}>`)\n\t\t\t\t.join(\",\")}`\n\n\t\tif (mail.to.length > 0) {\n\t\t\tlines.push(formatRecipients(\"To\", mail.to))\n\t\t}\n\n\t\tif (mail.cc.length > 0) {\n\t\t\tlines.push(formatRecipients(\"CC\", mail.cc))\n\t\t}\n\n\t\tif (mail.bcc.length > 0) {\n\t\t\tlines.push(formatRecipients(\"BCC\", mail.bcc))\n\t\t}\n\n\t\tlet subject = mail.subject.trim() === \"\" ? \"\" : `=?UTF-8?B?${uint8ArrayToBase64(stringToUtf8Uint8Array(mail.subject))}?=`\n\t\tlines.push(\n\t\t\t\"Subject: \" + subject,\n\t\t\t\"Date: \" + _formatSmtpDateTime(new Date(mail.sentOn)), // TODO (later) load conversation entries and write message id and references\n\t\t\t//\"Message-ID: \" + // <006e01cf442b$52864f10$f792ed30$@tutao.de>\n\t\t\t//References: <53074EB8.4010505@tutao.de> <DD374AF0-AC6D-4C58-8F38-7F6D8A0307F3@tutao.de> <530E3529.70503@tutao.de>\n\t\t)\n\t}\n\n\tlines.push(\n\t\t'Content-Type: multipart/related; boundary=\"------------79Bu5A16qPEYcVIZL@tutanota\"',\n\t\t\"\",\n\t\t\"--------------79Bu5A16qPEYcVIZL@tutanota\",\n\t\t\"Content-Type: text/html; charset=UTF-8\",\n\t\t\"Content-transfer-encoding: base64\",\n\t\t\"\",\n\t)\n\n\tfor (let bodyLine of breakIntoLines(stringToBase64(mail.body))) {\n\t\tlines.push(bodyLine)\n\t}\n\n\tlines.push(\"\")\n\n\tfor (let attachment of mail.attachments) {\n\t\tconst base64Filename = `=?UTF-8?B?${uint8ArrayToBase64(stringToUtf8Uint8Array(attachment.name))}?=`\n\t\tconst fileContentLines = breakIntoLines(uint8ArrayToBase64(attachment.data))\n\t\tlines.push(\n\t\t\t\"--------------79Bu5A16qPEYcVIZL@tutanota\",\n\t\t\t\"Content-Type: \" + getCleanedMimeType(attachment.mimeType) + \";\",\n\t\t\t\" name=\" + base64Filename + \"\",\n\t\t\t\"Content-Transfer-Encoding: base64\",\n\t\t\t\"Content-Disposition: attachment;\",\n\t\t\t\" filename=\" + base64Filename + \"\",\n\t\t)\n\n\t\tif (attachment.cid) {\n\t\t\tlines.push(\"Content-Id: <\" + attachment.cid + \">\")\n\t\t}\n\n\t\tlines.push(\"\")\n\n\t\t// don't use destructuring, big files can hit callstack limit\n\t\tfor (let fileLine of fileContentLines) {\n\t\t\tlines.push(fileLine)\n\t\t}\n\n\t\tlines.push(\"\")\n\t}\n\n\tlines.push(\"--------------79Bu5A16qPEYcVIZL@tutanota--\")\n\treturn lines.join(\"\\r\\n\")\n}\n\nfunction escapeSpecialCharacters(name: string): string {\n\t// There may be other special characters that need escaping\n\treturn name.replace(/[,<>]/gi, \"\\\\$&\")\n}\n\nexport function _formatSmtpDateTime(date: Date): string {\n\tconst dayNames = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\n\tconst monthNames = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\n\treturn (\n\t\tdayNames[date.getUTCDay()] +\n\t\t\", \" +\n\t\tdate.getUTCDate() +\n\t\t\" \" +\n\t\tmonthNames[date.getUTCMonth()] +\n\t\t\" \" +\n\t\tdate.getUTCFullYear() +\n\t\t\" \" +\n\t\tpad(date.getUTCHours(), 2) +\n\t\t\":\" +\n\t\tpad(date.getUTCMinutes(), 2) +\n\t\t\":\" +\n\t\tpad(date.getUTCSeconds(), 2) +\n\t\t\" +0000\"\n\t)\n}\n\n/**\n * Break up a long string into lines of up to 78 characters\n * @param string\n * @returns the lines, each as an individual array\n */\nfunction breakIntoLines(string: string): Array<string> {\n\treturn string.length > 0 ? assertNotNull(string.match(/.{1,78}/g)) : []\n}\n","import m, { Children, Component, Vnode } from \"mithril\"\nimport { lang } from \"../../misc/LanguageViewModel\"\n\nimport { MailFolderType, MailState } from \"../../api/common/TutanotaConstants\"\nimport type { Mail, MailFolder } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { canDoDragAndDropExport } from \"../model/MailUtils\"\nimport { size } from \"../../gui/size\"\nimport { styles } from \"../../gui/styles\"\nimport { Icon } from \"../../gui/base/Icon\"\nimport { Icons } from \"../../gui/base/icons/Icons\"\nimport type { ButtonAttrs } from \"../../gui/base/Button.js\"\nimport { Button, ButtonColor, ButtonType } from \"../../gui/base/Button.js\"\nimport { Dialog } from \"../../gui/base/Dialog\"\nimport { assertNotNull, AsyncResult, downcast, neverNull, promiseMap } from \"@tutao/tutanota-utils\"\nimport { locator } from \"../../api/main/MainLocator\"\nimport { getLetId, haveSameId } from \"../../api/common/utils/EntityUtils\"\nimport { moveMails, promptAndDeleteMails } from \"./MailGuiUtils\"\nimport { MailRow } from \"./MailRow\"\nimport { makeTrackedProgressMonitor } from \"../../api/common/utils/ProgressMonitor\"\nimport { generateExportFileName, generateMailFile, getMailExportMode } from \"../export/Exporter\"\nimport { deduplicateFilenames } from \"../../api/common/utils/FileUtils\"\nimport { makeMailBundle } from \"../export/Bundler\"\nimport { ListColumnWrapper } from \"../../gui/ListColumnWrapper\"\nimport { assertMainOrNode } from \"../../api/common/Env\"\nimport { FolderSystem } from \"../../api/common/mail/FolderSystem.js\"\nimport { assertSystemFolderOfType } from \"../../api/common/mail/CommonMailUtils.js\"\nimport { MailViewModel } from \"./MailViewModel.js\"\nimport { List, ListAttrs, ListSwipeDecision, MultiselectMode, RenderConfig, SwipeConfiguration } from \"../../gui/base/List.js\"\nimport ColumnEmptyMessageBox from \"../../gui/base/ColumnEmptyMessageBox.js\"\nimport { BootIcons } from \"../../gui/base/icons/BootIcons.js\"\nimport { theme } from \"../../gui/theme.js\"\nimport { VirtualRow } from \"../../gui/base/ListUtils.js\"\n\nassertMainOrNode()\n\nexport interface MailListViewAttrs {\n\t// We would like to not get and hold to the whole MailView eventually\n\t// but for that we need to rewrite the List\n\tonClearFolder: () => unknown\n\tmailViewModel: MailViewModel\n\tlistId: Id\n\tonSingleSelection: (mail: Mail) => unknown\n}\n\nexport class MailListView implements Component<MailListViewAttrs> {\n\tlistId: Id\n\t// Mails that are currently being or have already been downloaded/bundled/saved\n\t// Map of (Mail._id ++ MailExportMode) -> Promise<Filepath>\n\t// TODO this currently grows bigger and bigger and bigger if the user goes on an exporting spree.\n\t//  maybe we should deal with this, or maybe this never becomes an issue?\n\texportedMails: Map<\n\t\tstring,\n\t\t{\n\t\t\tfileName: string\n\t\t\tresult: AsyncResult<any>\n\t\t}\n\t>\n\t// Used for modifying the cursor during drag and drop\n\t_listDom: HTMLElement | null\n\tshowingSpamOrTrash: boolean = false\n\tshowingDraft: boolean = false\n\tprivate mailViewModel: MailViewModel\n\n\tprivate readonly renderConfig: RenderConfig<Mail, MailRow> = {\n\t\titemHeight: size.list_row_height,\n\t\tmultiselectionAllowed: MultiselectMode.Enabled,\n\t\tcreateElement: (dom: HTMLElement) => {\n\t\t\tconst mailRow = new MailRow(false, (entity) => this.mailViewModel.listModel?.onSingleExclusiveSelection(entity))\n\t\t\tm.render(dom, mailRow.render())\n\t\t\treturn mailRow\n\t\t},\n\t\tswipe: locator.logins.isInternalUserLoggedIn()\n\t\t\t? ({\n\t\t\t\t\trenderLeftSpacer: () => this.renderLeftSpacer(),\n\t\t\t\t\trenderRightSpacer: () => this.renderRightSpacer(),\n\t\t\t\t\tswipeLeft: (listElement: Mail) => this.onSwipeLeft(listElement),\n\t\t\t\t\tswipeRight: (listElement: Mail) => this.onSwipeRight(listElement),\n\t\t\t  } satisfies SwipeConfiguration<Mail>)\n\t\t\t: null,\n\t\tdragStart: (event, row, selected) => this._newDragStart(event, row, selected),\n\t}\n\n\tconstructor({ attrs }: Vnode<MailListViewAttrs>) {\n\t\tthis.mailViewModel = attrs.mailViewModel\n\t\tthis.listId = attrs.listId\n\t\tthis.exportedMails = new Map()\n\t\tthis._listDom = null\n\t\tthis.mailViewModel.showingTrashOrSpamFolder().then((result) => {\n\t\t\tthis.showingSpamOrTrash = result\n\t\t\tm.redraw()\n\t\t})\n\t\tthis.mailViewModel.showingDraftsFolder().then((result) => {\n\t\t\tthis.showingDraft = result\n\t\t\tm.redraw()\n\t\t})\n\t\t// \"this\" is incorrectly bound if we don't do it this way\n\t\tthis.view = this.view.bind(this)\n\t}\n\n\tprivate getRecoverFolder(mail: Mail, folders: FolderSystem): MailFolder {\n\t\tif (mail.state === MailState.DRAFT) {\n\t\t\treturn assertSystemFolderOfType(folders, MailFolderType.DRAFT)\n\t\t} else {\n\t\t\treturn assertSystemFolderOfType(folders, MailFolderType.INBOX)\n\t\t}\n\t}\n\n\t// NOTE we do all of the electron drag handling directly inside MailListView, because we currently have no need to generalise\n\t// would strongly suggest with starting generalising this first if we ever need to support dragging more than just mails\n\t_newDragStart(event: DragEvent, row: Mail, selected: ReadonlySet<Mail>) {\n\t\tif (!row) return\n\t\tconst mailUnderCursor = row\n\n\t\tif (isExportDragEvent(event)) {\n\t\t\t// We have to remove the drag mod key class here because once the dragstart has begun\n\t\t\t// we won't receive the keyup event that would normally remove it\n\t\t\tthis._listDom && this._listDom.classList.remove(\"drag-mod-key\")\n\t\t\t// We have to preventDefault or we get mysterious and inconsistent electron crashes at the call to startDrag in IPC\n\t\t\tevent.preventDefault()\n\t\t\t// if the mail being dragged is not included in the mails that are selected, then we only drag\n\t\t\t// the mail that is currently being dragged, to match the behaviour of regular in-app dragging and dropping\n\t\t\t// which seemingly behaves how it does just by default\n\t\t\t//const draggedMails = selected.find((mail) => haveSameId(mail, mailUnderCursor)) ? selected.slice() : [mailUnderCursor]\n\t\t\tconst draggedMails = selected.has(mailUnderCursor) ? [...selected] : [mailUnderCursor]\n\n\t\t\tthis._doExportDrag(draggedMails)\n\t\t} else if (styles.isDesktopLayout()) {\n\t\t\t// Desktop layout only because it doesn't make sense to drag mails to folders when the folder list and mail list aren't visible at the same time\n\t\t\tneverNull(event.dataTransfer).setData(\"text\", getLetId(neverNull(mailUnderCursor))[1])\n\t\t} else {\n\t\t\tevent.preventDefault()\n\t\t}\n\t}\n\n\t// NOTE we do all of the electron drag handling directly inside MailListView, because we currently have no need to generalise\n\t// would strongly suggest with starting generalising this first if we ever need to support dragging more than just mails\n\t_dragStart(event: DragEvent, row: VirtualRow<Mail>, selected: ReadonlyArray<Mail>) {\n\t\tif (!row.entity) return\n\t\tconst mailUnderCursor = row.entity\n\n\t\tif (isExportDragEvent(event)) {\n\t\t\t// We have to remove the drag mod key class here because once the dragstart has begun\n\t\t\t// we won't receive the keyup event that would normally remove it\n\t\t\tthis._listDom && this._listDom.classList.remove(\"drag-mod-key\")\n\t\t\t// We have to preventDefault or we get mysterious and inconsistent electron crashes at the call to startDrag in IPC\n\t\t\tevent.preventDefault()\n\t\t\t// if the mail being dragged is not included in the mails that are selected, then we only drag\n\t\t\t// the mail that is currently being dragged, to match the behaviour of regular in-app dragging and dropping\n\t\t\t// which seemingly behaves how it does just by default\n\t\t\tconst draggedMails = selected.find((mail) => haveSameId(mail, mailUnderCursor)) ? selected.slice() : [mailUnderCursor]\n\n\t\t\tthis._doExportDrag(draggedMails)\n\t\t} else if (styles.isDesktopLayout()) {\n\t\t\t// Desktop layout only because it doesn't make sense to drag mails to folders when the folder list and mail list aren't visible at the same time\n\t\t\tneverNull(event.dataTransfer).setData(\"text\", getLetId(neverNull(mailUnderCursor))[1])\n\t\t} else {\n\t\t\tevent.preventDefault()\n\t\t}\n\t}\n\n\tasync _doExportDrag(draggedMails: Array<Mail>): Promise<void> {\n\t\tassertNotNull(document.body).style.cursor = \"progress\"\n\t\t// We listen to mouseup to detect if the user released the mouse before the download was complete\n\t\t// we can't use dragend because we broke the DragEvent chain by calling prevent default\n\t\tconst mouseupPromise = new Promise((resolve) => {\n\t\t\tdocument.addEventListener(\"mouseup\", resolve, {\n\t\t\t\tonce: true,\n\t\t\t})\n\t\t})\n\n\t\tconst filePathsPromise = this._prepareMailsForDrag(draggedMails)\n\n\t\t// If the download completes before the user releases their mouse, then we can call electron start drag and do the operation\n\t\t// otherwise we have to give some kind of feedback to the user that the drop was unsuccessful\n\t\tconst [didComplete, fileNames] = await Promise.race([filePathsPromise.then((filePaths) => [true, filePaths]), mouseupPromise.then(() => [false, []])])\n\n\t\tif (didComplete) {\n\t\t\tawait locator.fileApp.startNativeDrag(fileNames as string[])\n\t\t} else {\n\t\t\tawait locator.desktopSystemFacade.focusApplicationWindow()\n\t\t\tDialog.message(\"unsuccessfulDrop_msg\")\n\t\t}\n\n\t\tneverNull(document.body).style.cursor = \"default\"\n\t}\n\n\t/**\n\t * Given a mail, will prepare it by downloading, bundling, saving, then returns the filepath of the saved file.\n\t * @returns {Promise<R>|Promise<string>}\n\t * @private\n\t * @param mails\n\t */\n\tasync _prepareMailsForDrag(mails: Array<Mail>): Promise<Array<string>> {\n\t\tconst exportMode = await getMailExportMode()\n\t\t// 3 actions per mail + 1 to indicate that something is happening (if the downloads take a while)\n\t\tconst progressMonitor = makeTrackedProgressMonitor(locator.progressTracker, 3 * mails.length + 1)\n\t\tprogressMonitor.workDone(1)\n\n\t\tconst mapKey = (mail: Mail) => `${getLetId(mail).join(\"\")}${exportMode}`\n\n\t\tconst notDownloaded: Array<{ mail: Mail; fileName: string }> = []\n\t\tconst downloaded: Array<{ fileName: string; promise: Promise<Mail> }> = []\n\n\t\tconst handleNotDownloaded = (mail: Mail) => {\n\t\t\tnotDownloaded.push({\n\t\t\t\tmail,\n\t\t\t\tfileName: generateExportFileName(mail.subject, mail.receivedDate, exportMode),\n\t\t\t})\n\t\t}\n\n\t\tconst handleDownloaded = (fileName: string, promise: Promise<Mail>) => {\n\t\t\t// we don't have to do anything else with the downloaded ones\n\t\t\t// so finish this chunk of work\n\t\t\tprogressMonitor.workDone(3)\n\t\t\tdownloaded.push({\n\t\t\t\tfileName,\n\t\t\t\tpromise: promise,\n\t\t\t})\n\t\t}\n\n\t\t// Gather up files that have been downloaded\n\t\t// and all files that need to be downloaded, or were already downloaded but have disappeared\n\t\tfor (let mail of mails) {\n\t\t\tconst key = mapKey(mail)\n\t\t\tconst existing = this.exportedMails.get(key)\n\n\t\t\tif (!existing || existing.result.state().status === \"failure\") {\n\t\t\t\t// Something went wrong last time we tried to drag this file,\n\t\t\t\t// so try again (not confident that it will work this time, though)\n\t\t\t\thandleNotDownloaded(mail)\n\t\t\t} else {\n\t\t\t\tconst state = existing.result.state()\n\n\t\t\t\tswitch (state.status) {\n\t\t\t\t\t// Mail is still being prepared, already has a file path assigned to it\n\t\t\t\t\tcase \"pending\": {\n\t\t\t\t\t\thandleDownloaded(existing.fileName, state.promise)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"complete\": {\n\t\t\t\t\t\t// We have downloaded it, but we need to check if it still exists\n\t\t\t\t\t\tconst exists = await locator.fileApp.checkFileExistsInExportDir(existing.fileName)\n\n\t\t\t\t\t\tif (exists) {\n\t\t\t\t\t\t\thandleDownloaded(existing.fileName, Promise.resolve(mail))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\thandleNotDownloaded(mail)\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\n\t\tconst deduplicatedNames = deduplicateFilenames(\n\t\t\tnotDownloaded.map((f) => f.fileName),\n\t\t\tnew Set(downloaded.map((f) => f.fileName)),\n\t\t)\n\t\tconst [newFiles, existingFiles] = await Promise.all([\n\t\t\t// Download all the files that need downloading, wait for them, and then return the filename\n\t\t\tpromiseMap(notDownloaded, async ({ mail, fileName }) => {\n\t\t\t\tconst name = assertNotNull(deduplicatedNames[fileName].shift())\n\t\t\t\tconst key = mapKey(mail)\n\t\t\t\tconst downloadPromise = Promise.resolve().then(async () => {\n\t\t\t\t\tconst { htmlSanitizer } = await import(\"../../misc/HtmlSanitizer\")\n\t\t\t\t\tconst bundle = await makeMailBundle(mail, locator.entityClient, locator.fileController, htmlSanitizer)\n\t\t\t\t\tprogressMonitor.workDone(1)\n\t\t\t\t\tconst file = await generateMailFile(bundle, name, exportMode)\n\t\t\t\t\tprogressMonitor.workDone(1)\n\t\t\t\t\tawait locator.fileApp.saveToExportDir(file)\n\t\t\t\t\tprogressMonitor.workDone(1)\n\t\t\t\t})\n\t\t\t\tthis.exportedMails.set(key, {\n\t\t\t\t\tfileName: name,\n\t\t\t\t\tresult: new AsyncResult(downloadPromise),\n\t\t\t\t})\n\t\t\t\tawait downloadPromise\n\t\t\t\treturn name\n\t\t\t}), // Wait for ones that already were downloading or have finished, and  then return their filenames too\n\t\t\tpromiseMap(downloaded, (result) => result.promise.then(() => result.fileName)),\n\t\t])\n\t\t// combine the list of newly downloaded and previously downloaded files\n\t\treturn newFiles.concat(existingFiles)\n\t}\n\n\tview(vnode: Vnode<MailListViewAttrs>): Children {\n\t\tthis.mailViewModel = vnode.attrs.mailViewModel\n\n\t\t// Save the folder before showing the dialog so that there's no chance that it will change\n\t\tconst folder = this.mailViewModel.getSelectedFolder()\n\t\tconst purgeButtonAttrs: ButtonAttrs = {\n\t\t\tlabel: \"clearFolder_action\",\n\t\t\ttype: ButtonType.Primary,\n\t\t\tcolors: ButtonColor.Nav,\n\t\t\tclick: async () => {\n\t\t\t\tvnode.attrs.onClearFolder()\n\t\t\t},\n\t\t}\n\n\t\t// listeners to indicate the when mod key is held, dragging will do something\n\t\tconst onKeyDown = (event: KeyboardEvent) => {\n\t\t\tif (isDragAndDropModifierHeld(event)) {\n\t\t\t\tthis._listDom && this._listDom.classList.add(\"drag-mod-key\")\n\t\t\t}\n\t\t}\n\n\t\tconst onKeyUp = (event: KeyboardEvent) => {\n\t\t\t// The event doesn't have a\n\t\t\tthis._listDom && this._listDom.classList.remove(\"drag-mod-key\")\n\t\t}\n\n\t\tconst listModel = vnode.attrs.mailViewModel.listModel!\n\t\treturn m(\n\t\t\t\".mail-list-wrapper\",\n\t\t\t{\n\t\t\t\toncreate: (vnode) => {\n\t\t\t\t\tthis._listDom = downcast(vnode.dom.firstChild)\n\n\t\t\t\t\tif (canDoDragAndDropExport()) {\n\t\t\t\t\t\tassertNotNull(document.body).addEventListener(\"keydown\", onKeyDown)\n\t\t\t\t\t\tassertNotNull(document.body).addEventListener(\"keyup\", onKeyUp)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tonbeforeremove: (vnode) => {\n\t\t\t\t\tif (canDoDragAndDropExport()) {\n\t\t\t\t\t\tassertNotNull(document.body).removeEventListener(\"keydown\", onKeyDown)\n\t\t\t\t\t\tassertNotNull(document.body).removeEventListener(\"keyup\", onKeyUp)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t// always render the wrapper so that the list is not re-created from scratch when\n\t\t\t// showingSpamOrTrash changes.\n\t\t\tm(\n\t\t\t\tListColumnWrapper,\n\t\t\t\t{\n\t\t\t\t\theaderContent: this.renderListHeader(purgeButtonAttrs),\n\t\t\t\t},\n\t\t\t\tlistModel.isEmptyAndDone()\n\t\t\t\t\t? m(ColumnEmptyMessageBox, {\n\t\t\t\t\t\t\ticon: BootIcons.Mail,\n\t\t\t\t\t\t\tmessage: \"noMails_msg\",\n\t\t\t\t\t\t\tcolor: theme.list_message_bg,\n\t\t\t\t\t  })\n\t\t\t\t\t: m(List, {\n\t\t\t\t\t\t\tstate: listModel.state,\n\t\t\t\t\t\t\trenderConfig: this.renderConfig,\n\t\t\t\t\t\t\tonLoadMore() {\n\t\t\t\t\t\t\t\tlistModel.loadMore()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tonRetryLoading() {\n\t\t\t\t\t\t\t\tlistModel.retryLoading()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tonSingleSelection: (item) => {\n\t\t\t\t\t\t\t\tlistModel.onSingleSelection(item)\n\t\t\t\t\t\t\t\tvnode.attrs.onSingleSelection(item)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tonSingleTogglingMultiselection: (item: Mail) => {\n\t\t\t\t\t\t\t\tlistModel.onSingleInclusiveSelection(item)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tonRangeSelectionTowards: (item: Mail) => {\n\t\t\t\t\t\t\t\tlistModel.selectRangeTowards(item)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tonStopLoading() {\n\t\t\t\t\t\t\t\tlistModel.stopLoading()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t  } satisfies ListAttrs<Mail, MailRow>),\n\t\t\t),\n\t\t)\n\t}\n\n\tprivate renderListHeader(purgeButtonAttrs: ButtonAttrs): Children {\n\t\treturn m(\".flex.col\", [\n\t\t\tthis.showingSpamOrTrash\n\t\t\t\t? [\n\t\t\t\t\t\tm(\".flex.flex-column.plr-l\", [\n\t\t\t\t\t\t\tm(\".small.flex-grow.pt\", lang.get(\"storageDeletion_msg\")),\n\t\t\t\t\t\t\tm(\".mr-negative-s.align-self-end\", m(Button, purgeButtonAttrs)),\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\tprivate targetInbox(): boolean {\n\t\tconst selectedFolder = this.mailViewModel.getSelectedFolder()\n\t\tif (selectedFolder) {\n\t\t\treturn selectedFolder.folderType === MailFolderType.ARCHIVE || selectedFolder.folderType === MailFolderType.TRASH\n\t\t} else {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tprivate async onSwipeLeft(listElement: Mail): Promise<ListSwipeDecision> {\n\t\tconst wereDeleted = await promptAndDeleteMails(locator.mailModel, [listElement], () => this.mailViewModel.listModel?.selectNone())\n\t\treturn wereDeleted ? ListSwipeDecision.Commit : ListSwipeDecision.Cancel\n\t}\n\n\tprivate async onSwipeRight(listElement: Mail): Promise<ListSwipeDecision> {\n\t\tif (this.showingDraft) {\n\t\t\t// just cancel selection if in drafts\n\t\t\tthis.mailViewModel.listModel?.selectNone()\n\t\t\treturn ListSwipeDecision.Cancel\n\t\t} else {\n\t\t\tconst folders = await locator.mailModel.getMailboxFolders(listElement)\n\t\t\tif (folders) {\n\t\t\t\tconst targetMailFolder = this.showingSpamOrTrash\n\t\t\t\t\t? this.getRecoverFolder(listElement, folders)\n\t\t\t\t\t: assertNotNull(folders.getSystemFolderByType(MailFolderType.ARCHIVE))\n\t\t\t\tconst wereMoved = await moveMails({\n\t\t\t\t\tmailModel: locator.mailModel,\n\t\t\t\t\tmails: [listElement],\n\t\t\t\t\ttargetMailFolder,\n\t\t\t\t})\n\t\t\t\treturn wereMoved ? ListSwipeDecision.Commit : ListSwipeDecision.Cancel\n\t\t\t} else {\n\t\t\t\treturn ListSwipeDecision.Cancel\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate renderLeftSpacer(): Children {\n\t\treturn this.showingDraft\n\t\t\t? [\n\t\t\t\t\tm(Icon, {\n\t\t\t\t\t\ticon: Icons.Cancel,\n\t\t\t\t\t}),\n\t\t\t\t\tm(\".pl-s\", lang.get(\"cancel_action\")), // if this is the drafts folder, we can only cancel the selection as we have nowhere else to put the mail\n\t\t\t  ]\n\t\t\t: [\n\t\t\t\t\tm(Icon, {\n\t\t\t\t\t\ticon: Icons.Folder,\n\t\t\t\t\t}),\n\t\t\t\t\tm(\n\t\t\t\t\t\t\".pl-s\",\n\t\t\t\t\t\tthis.showingSpamOrTrash\n\t\t\t\t\t\t\t? lang.get(\"recover_label\") // show \"recover\" if this is the trash/spam folder\n\t\t\t\t\t\t\t: this.targetInbox() // otherwise show \"inbox\" or \"archive\" depending on the folder\n\t\t\t\t\t\t\t? lang.get(\"received_action\")\n\t\t\t\t\t\t\t: lang.get(\"archive_action\"),\n\t\t\t\t\t),\n\t\t\t  ]\n\t}\n\n\tprivate renderRightSpacer(): Children {\n\t\treturn [\n\t\t\tm(Icon, {\n\t\t\t\ticon: Icons.Trash,\n\t\t\t}),\n\t\t\tm(\".pl-s\", lang.get(\"delete_action\")),\n\t\t]\n\t}\n}\n\nexport function isExportDragEvent(event: DragEvent): boolean {\n\treturn canDoDragAndDropExport() && isDragAndDropModifierHeld(event)\n}\n\nfunction isDragAndDropModifierHeld(event: DragEvent | KeyboardEvent): boolean {\n\treturn (\n\t\tevent.ctrlKey ||\n\t\tevent.altKey ||\n\t\t// @ts-ignore\n\t\t(!!event.key && [\"alt\", \"ctrl\"].includes(downcast(event.key).toLowerCase()))\n\t)\n}\n","import m, { Component, Vnode } from \"mithril\"\nimport { assertMainOrNode } from \"../../api/common/Env\"\nimport ColumnEmptyMessageBox from \"../../gui/base/ColumnEmptyMessageBox\"\nimport { lang } from \"../../misc/LanguageViewModel\"\nimport { BootIcons } from \"../../gui/base/icons/BootIcons\"\nimport { theme } from \"../../gui/theme\"\nimport type { Mail } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { Button, ButtonType } from \"../../gui/base/Button.js\"\nimport { progressIcon } from \"../../gui/base/Icon.js\"\n\nassertMainOrNode()\n\nexport type MultiItemViewerAttrs<T> = {\n\tselectedEntities: Array<T>\n\tselectNone: () => unknown\n\tloadingAll: \"can_load\" | \"loading\" | \"loaded\"\n\tloadAll: () => unknown\n\tstopLoadAll: () => unknown\n\tgetSelectionMessage: (entities: ReadonlyArray<T>) => string\n}\n\nexport class MultiItemViewer<T> implements Component<MultiItemViewerAttrs<T>> {\n\tview({ attrs }: Vnode<MultiItemViewerAttrs<T>>) {\n\t\tconst { selectedEntities } = attrs\n\t\treturn [\n\t\t\tm(\n\t\t\t\t\".flex.col.fill-absolute\",\n\t\t\t\tm(\n\t\t\t\t\t\".flex-grow.rel.overflow-hidden\",\n\t\t\t\t\tm(ColumnEmptyMessageBox, {\n\t\t\t\t\t\tmessage: () => attrs.getSelectionMessage(selectedEntities),\n\t\t\t\t\t\ticon: BootIcons.Mail,\n\t\t\t\t\t\tcolor: theme.content_message_bg,\n\t\t\t\t\t\tbackgroundColor: theme.navigation_bg,\n\t\t\t\t\t\tbottomContent: this.renderEmptyMessageButtons(attrs),\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\t]\n\t}\n\n\tprivate renderEmptyMessageButtons({ loadingAll, stopLoadAll, selectedEntities, selectNone, loadAll }: MultiItemViewerAttrs<T>) {\n\t\treturn loadingAll === \"loading\"\n\t\t\t? m(\".flex.items-center\", [\n\t\t\t\t\tm(Button, {\n\t\t\t\t\t\tlabel: \"cancel_action\",\n\t\t\t\t\t\ttype: ButtonType.Secondary,\n\t\t\t\t\t\tclick: () => {\n\t\t\t\t\t\t\tstopLoadAll()\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t\tm(\".flex.items-center.plr-button\", progressIcon()),\n\t\t\t  ])\n\t\t\t: selectedEntities.length === 0\n\t\t\t? null\n\t\t\t: m(\".flex\", [\n\t\t\t\t\tm(Button, {\n\t\t\t\t\t\tlabel: \"cancel_action\",\n\t\t\t\t\t\ttype: ButtonType.Secondary,\n\t\t\t\t\t\tclick: () => {\n\t\t\t\t\t\t\tselectNone()\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t\tloadingAll === \"can_load\"\n\t\t\t\t\t\t? m(Button, {\n\t\t\t\t\t\t\t\tlabel: \"loadAll_action\",\n\t\t\t\t\t\t\t\ttype: ButtonType.Secondary,\n\t\t\t\t\t\t\t\tclick: () => {\n\t\t\t\t\t\t\t\t\tloadAll()\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  ])\n\t}\n}\n\nexport function getMailSelectionMessage(selectedEntities: ReadonlyArray<Mail>): string {\n\tlet nbrOfSelectedMails = selectedEntities.length\n\n\tif (nbrOfSelectedMails === 0) {\n\t\treturn lang.get(\"noMail_msg\")\n\t} else if (nbrOfSelectedMails === 1) {\n\t\treturn lang.get(\"oneMailSelected_msg\")\n\t} else {\n\t\treturn lang.get(\"nbrOfMailsSelected_msg\", {\n\t\t\t\"{1}\": nbrOfSelectedMails,\n\t\t})\n\t}\n}\n","import m, { Children, Component, Vnode } from \"mithril\"\nimport { MailViewerViewModel } from \"./MailViewerViewModel.js\"\nimport { IconButton } from \"../../gui/base/IconButton.js\"\nimport { createAsyncDropdown, createDropdown, Dropdown, DROPDOWN_MARGIN, DropdownButtonAttrs } from \"../../gui/base/Dropdown.js\"\nimport { Icons } from \"../../gui/base/icons/Icons.js\"\nimport { UserError } from \"../../api/main/UserError.js\"\nimport { showUserError } from \"../../misc/ErrorHandlerImpl.js\"\nimport { promptAndDeleteMails, showMoveMailsDropdown } from \"./MailGuiUtils.js\"\nimport { noOp, ofClass } from \"@tutao/tutanota-utils\"\nimport { modal } from \"../../gui/base/Modal.js\"\nimport { editDraft, mailViewerMoreActions, makeAssignMailsButtons } from \"./MailViewerUtils.js\"\nimport { px, size } from \"../../gui/size.js\"\n\nexport interface MobileMailActionBarAttrs {\n\tviewModel: MailViewerViewModel\n}\n\nexport class MobileMailActionBar implements Component<MobileMailActionBarAttrs> {\n\tprivate dom: HTMLElement | null = null\n\n\tview(vnode: Vnode<MobileMailActionBarAttrs>): Children {\n\t\tconst { attrs } = vnode\n\t\tconst { viewModel } = attrs\n\t\tlet actions: Children[]\n\n\t\tif (viewModel.isAnnouncement()) {\n\t\t\tactions = [this.placeholder(), this.placeholder(), this.deleteButton(attrs), this.placeholder(), this.moreButton(attrs)]\n\t\t} else if (viewModel.isDraftMail()) {\n\t\t\tactions = [this.placeholder(), this.placeholder(), this.deleteButton(attrs), this.moveButton(attrs), this.editButton(attrs)]\n\t\t} else if (viewModel.canForwardOrMove()) {\n\t\t\tactions = [this.replyButton(attrs), this.forwardButton(attrs), this.deleteButton(attrs), this.moveButton(attrs), this.moreButton(attrs)]\n\t\t} else if (viewModel.canAssignMails()) {\n\t\t\tactions = [this.replyButton(attrs), this.assignButton(attrs), this.deleteButton(attrs), this.placeholder(), this.moreButton(attrs)]\n\t\t} else {\n\t\t\tactions = [this.replyButton(attrs), this.placeholder(), this.deleteButton(attrs), this.placeholder(), this.moreButton(attrs)]\n\t\t}\n\n\t\treturn m(\n\t\t\t\".bottom-nav.bottom-action-bar.flex.items-center.plr-l.justify-between\",\n\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},\n\t\t\t},\n\t\t\t[actions],\n\t\t)\n\t}\n\n\tprivate placeholder() {\n\t\treturn m(\"\", {\n\t\t\tstyle: {\n\t\t\t\twidth: px(size.button_height),\n\t\t\t},\n\t\t})\n\t}\n\n\tprivate moveButton({ viewModel }: MobileMailActionBarAttrs) {\n\t\treturn m(IconButton, {\n\t\t\ttitle: \"move_action\",\n\t\t\tclick: (e, dom) =>\n\t\t\t\tshowMoveMailsDropdown(viewModel.mailModel, dom.getBoundingClientRect(), [viewModel.mail], {\n\t\t\t\t\twidth: this.dropdownWidth(),\n\t\t\t\t\twithBackground: true,\n\t\t\t\t}),\n\t\t\ticon: Icons.Folder,\n\t\t})\n\t}\n\n\tprivate dropdownWidth() {\n\t\treturn this.dom?.offsetWidth ? this.dom.offsetWidth - DROPDOWN_MARGIN * 2 : undefined\n\t}\n\n\tprivate moreButton({ viewModel }: MobileMailActionBarAttrs) {\n\t\treturn m(IconButton, {\n\t\t\ttitle: \"more_label\",\n\t\t\tclick: createDropdown({\n\t\t\t\tlazyButtons: () => mailViewerMoreActions(viewModel),\n\t\t\t\twidth: this.dropdownWidth(),\n\t\t\t\twithBackground: true,\n\t\t\t}),\n\t\t\ticon: Icons.More,\n\t\t})\n\t}\n\n\tprivate deleteButton({ viewModel }: MobileMailActionBarAttrs): Children {\n\t\treturn m(IconButton, {\n\t\t\ttitle: \"delete_action\",\n\t\t\tclick: () => promptAndDeleteMails(viewModel.mailModel, [viewModel.mail], noOp),\n\t\t\ticon: Icons.Trash,\n\t\t})\n\t}\n\n\tprivate forwardButton({ viewModel }: MobileMailActionBarAttrs): Children {\n\t\treturn m(IconButton, {\n\t\t\ttitle: \"forward_action\",\n\t\t\tclick: () => viewModel.forward().catch(ofClass(UserError, showUserError)),\n\t\t\ticon: Icons.Forward,\n\t\t})\n\t}\n\n\tprivate replyButton({ viewModel }: MobileMailActionBarAttrs) {\n\t\treturn m(IconButton, {\n\t\t\ttitle: \"reply_action\",\n\t\t\tclick: viewModel.canReplyAll()\n\t\t\t\t? (e, dom) => {\n\t\t\t\t\t\tconst dropdown = new Dropdown(() => {\n\t\t\t\t\t\t\tconst buttons: DropdownButtonAttrs[] = []\n\t\t\t\t\t\t\tbuttons.push({\n\t\t\t\t\t\t\t\tlabel: \"replyAll_action\",\n\t\t\t\t\t\t\t\ticon: Icons.ReplyAll,\n\t\t\t\t\t\t\t\tclick: () => viewModel.reply(true),\n\t\t\t\t\t\t\t})\n\n\t\t\t\t\t\t\tbuttons.push({\n\t\t\t\t\t\t\t\tlabel: \"reply_action\",\n\t\t\t\t\t\t\t\ticon: Icons.Reply,\n\t\t\t\t\t\t\t\tclick: () => viewModel.reply(false),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\treturn buttons\n\t\t\t\t\t\t}, this.dropdownWidth() ?? 300)\n\n\t\t\t\t\t\tconst domRect = this.dom?.getBoundingClientRect() ?? dom.getBoundingClientRect()\n\t\t\t\t\t\tdropdown.setOrigin(domRect)\n\t\t\t\t\t\tmodal.displayUnique(dropdown, true)\n\t\t\t\t  }\n\t\t\t\t: () => viewModel.reply(false),\n\t\t\ticon: viewModel.canReplyAll() ? Icons.ReplyAll : Icons.Reply,\n\t\t})\n\t}\n\n\tprivate editButton(attrs: MobileMailActionBarAttrs) {\n\t\treturn m(IconButton, {\n\t\t\ttitle: \"edit_action\",\n\t\t\ticon: Icons.Edit,\n\t\t\tclick: () => editDraft(attrs.viewModel),\n\t\t})\n\t}\n\n\tprivate assignButton(attrs: MobileMailActionBarAttrs) {\n\t\treturn m(IconButton, {\n\t\t\ttitle: \"forward_action\",\n\t\t\ticon: Icons.Forward,\n\t\t\tclick: createAsyncDropdown({\n\t\t\t\tlazyButtons: () => makeAssignMailsButtons(attrs.viewModel),\n\t\t\t\twidth: this.dropdownWidth(),\n\t\t\t\twithBackground: true,\n\t\t\t}),\n\t\t})\n\t}\n}\n","import m, { Children, Component, Vnode } from \"mithril\"\nimport type { NavButtonAttrs } from \"../../gui/base/NavButton.js\"\nimport { isNavButtonSelected, NavButton } from \"../../gui/base/NavButton.js\"\nimport { CounterBadge } from \"../../gui/base/CounterBadge\"\nimport { getNavButtonIconBackground, theme } from \"../../gui/theme\"\nimport { px, size } from \"../../gui/size\"\nimport { IconButton, IconButtonAttrs } from \"../../gui/base/IconButton.js\"\nimport { AllIcons, Icon } from \"../../gui/base/Icon.js\"\nimport { Icons } from \"../../gui/base/icons/Icons.js\"\nimport { stateBgHover } from \"../../gui/builtinThemes.js\"\nimport { client } from \"../../misc/ClientDetector.js\"\n\nexport type MailFolderRowAttrs = {\n\tcount: number\n\tbutton: NavButtonAttrs\n\trightButton?: IconButtonAttrs | null\n\texpanded: boolean | null\n\tindentationLevel: number\n\tonExpanderClick: () => unknown\n\ticon: AllIcons\n\thasChildren: boolean\n\tonSelectedPath: boolean\n\tnumberOfPreviousRows: number\n\tisLastSibling: boolean\n\teditMode: boolean\n}\n\nexport class MailFolderRow implements Component<MailFolderRowAttrs> {\n\tprivate hovered: boolean = false\n\n\tview(vnode: Vnode<MailFolderRowAttrs>): Children {\n\t\tconst { count, button, rightButton, expanded, indentationLevel, icon, hasChildren, editMode } = vnode.attrs\n\n\t\tconst indentationMargin = indentationLevel * size.hpad\n\t\tconst paddingNeeded = size.hpad_button\n\t\tconst buttonWidth = size.icon_size_large + paddingNeeded * 2\n\n\t\treturn m(\n\t\t\t\".folder-row.flex.flex-row.mlr-button.border-radius-small\" + (editMode ? \"\" : \".state-bg\"),\n\t\t\t{\n\t\t\t\tstyle: {\n\t\t\t\t\tbackground: isNavButtonSelected(button) ? stateBgHover : \"\",\n\t\t\t\t},\n\t\t\t\tonmouseenter: () => {\n\t\t\t\t\tthis.hovered = true\n\t\t\t\t},\n\t\t\t\tonmouseleave: () => {\n\t\t\t\t\tthis.hovered = false\n\t\t\t\t},\n\t\t\t},\n\t\t\t[\n\t\t\t\thasChildren && !expanded\n\t\t\t\t\t? m(Icon, {\n\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\t\t\t\tbottom: px(9),\n\t\t\t\t\t\t\t\tleft: px(5 + indentationMargin + buttonWidth / 2),\n\t\t\t\t\t\t\t\tfill: isNavButtonSelected(button) ? theme.navigation_button_selected : theme.navigation_button,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\ticon: Icons.Add,\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\tstyle: {\n\t\t\t\t\t\tmarginLeft: px(indentationMargin),\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t\tthis.renderHierarchyLine(vnode.attrs, indentationMargin),\n\t\t\t\tm(\n\t\t\t\t\t\"button.flex.items-center.justify-end\" + (editMode || !hasChildren ? \".no-hover\" : \"\"),\n\t\t\t\t\t{\n\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\tleft: px(indentationMargin),\n\t\t\t\t\t\t\twidth: px(buttonWidth),\n\t\t\t\t\t\t\theight: px(size.button_height),\n\t\t\t\t\t\t\tpaddingLeft: px(paddingNeeded),\n\t\t\t\t\t\t\tpaddingRight: px(paddingNeeded),\n\t\t\t\t\t\t\t// the zIndex is so the hierarchy lines never get drawn over the icon\n\t\t\t\t\t\t\tzIndex: 3,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tonclick: vnode.attrs.onExpanderClick,\n\t\t\t\t\t},\n\t\t\t\t\tm(Icon, {\n\t\t\t\t\t\ticon,\n\t\t\t\t\t\tlarge: true,\n\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\tfill: isNavButtonSelected(button) ? theme.navigation_button_selected : theme.navigation_button,\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t\tm(NavButton, {\n\t\t\t\t\t...button,\n\t\t\t\t\tonfocus: () => (this.hovered = true),\n\t\t\t\t\tonblur: () => {\n\t\t\t\t\t\t// The setTimout is so that there is some time to tab to the rightButton\n\t\t\t\t\t\t// otherwise it disappears immediately and is unreachable on keyboard\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\tthis.hovered = false\n\t\t\t\t\t\t}, 5)\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t\t// show the edit button in either edit mode or on hover (excluding hover on mobile)\n\t\t\t\trightButton && (editMode || (!client.isMobileDevice() && this.hovered))\n\t\t\t\t\t? m(IconButton, {\n\t\t\t\t\t\t\t...rightButton,\n\t\t\t\t\t\t\tonblur: () => {\n\t\t\t\t\t\t\t\tm.redraw()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t  })\n\t\t\t\t\t: m(\"\", { style: { marginRight: px(size.hpad_button) } }, [\n\t\t\t\t\t\t\tm(CounterBadge, {\n\t\t\t\t\t\t\t\tcount,\n\t\t\t\t\t\t\t\tcolor: theme.navigation_button_icon,\n\t\t\t\t\t\t\t\tbackground: getNavButtonIconBackground(),\n\t\t\t\t\t\t\t\tshowFullCount: true,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t  ]),\n\t\t\t],\n\t\t)\n\t}\n\n\tprivate renderHierarchyLine({ indentationLevel, numberOfPreviousRows, isLastSibling, onSelectedPath }: MailFolderRowAttrs, indentationMargin: number) {\n\t\tconst lineSize = 2\n\t\tconst border = `${lineSize}px solid ${theme.content_border}`\n\t\tconst verticalOffsetInsideRow = size.button_height / 2 + 1\n\t\tconst verticalOffsetForParent = (size.button_height - size.icon_size_large) / 2\n\t\tconst lengthOfHorizontalLine = size.hpad - 2\n\t\tconst leftOffset = indentationMargin\n\n\t\treturn indentationLevel !== 0\n\t\t\t? [\n\t\t\t\t\tisLastSibling || onSelectedPath\n\t\t\t\t\t\t? // draw both vertical and horizontal lines\n\t\t\t\t\t\t  m(\".abs\", {\n\t\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\t\twidth: px(lengthOfHorizontalLine),\n\t\t\t\t\t\t\t\t\tborderBottomLeftRadius: \"3px\",\n\t\t\t\t\t\t\t\t\t// there's some subtle difference between border we use here and the top for the other element and this +1 is to\n\t\t\t\t\t\t\t\t\t// accommodate it\n\t\t\t\t\t\t\t\t\theight: px(1 + verticalOffsetInsideRow + verticalOffsetForParent + numberOfPreviousRows * size.button_height),\n\t\t\t\t\t\t\t\t\ttop: px(-verticalOffsetForParent - numberOfPreviousRows * size.button_height),\n\t\t\t\t\t\t\t\t\tleft: px(leftOffset),\n\t\t\t\t\t\t\t\t\tborderLeft: border,\n\t\t\t\t\t\t\t\t\tborderBottom: border,\n\t\t\t\t\t\t\t\t\t// we need to draw selected lines over everything else, even things that are drawn later\n\t\t\t\t\t\t\t\t\tzIndex: onSelectedPath ? 2 : 1,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t  })\n\t\t\t\t\t\t: // draw only the horizontal line\n\t\t\t\t\t\t  m(\".abs\", {\n\t\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\t\theight: px(lineSize),\n\t\t\t\t\t\t\t\t\ttop: px(verticalOffsetInsideRow),\n\t\t\t\t\t\t\t\t\tleft: px(leftOffset),\n\t\t\t\t\t\t\t\t\twidth: px(lengthOfHorizontalLine),\n\t\t\t\t\t\t\t\t\tbackgroundColor: theme.content_border,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t  }),\n\t\t\t  ]\n\t\t\t: null\n\t}\n}\n","import m, { Child, Children, Component, Vnode } from \"mithril\"\nimport { MailboxDetail } from \"../model/MailModel.js\"\nimport { locator } from \"../../api/main/MainLocator.js\"\nimport { SidebarSection } from \"../../gui/SidebarSection.js\"\nimport { IconButton, IconButtonAttrs } from \"../../gui/base/IconButton.js\"\nimport { FolderSubtree } from \"../../api/common/mail/FolderSystem.js\"\nimport { getElementId } from \"../../api/common/utils/EntityUtils.js\"\nimport { isSelectedPrefix, NavButtonAttrs, NavButtonColor } from \"../../gui/base/NavButton.js\"\nimport { getFolderIcon, getFolderName, MAX_FOLDER_INDENT_LEVEL } from \"../model/MailUtils.js\"\nimport { MAIL_PREFIX } from \"../../misc/RouteChange.js\"\nimport { MailFolderRow } from \"./MailFolderRow.js\"\nimport { last, noOp } from \"@tutao/tutanota-utils\"\nimport { MailFolder } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { attachDropdown, DropdownButtonAttrs } from \"../../gui/base/Dropdown.js\"\nimport { Icons } from \"../../gui/base/icons/Icons.js\"\nimport { ButtonColor } from \"../../gui/base/Button.js\"\nimport { ButtonSize } from \"../../gui/base/ButtonSize.js\"\nimport { MailFolderType } from \"../../api/common/TutanotaConstants.js\"\nimport { isSpamOrTrashFolder } from \"../../api/common/mail/CommonMailUtils.js\"\nimport { Icon } from \"../../gui/base/Icon.js\"\nimport { theme } from \"../../gui/theme.js\"\nimport { lang } from \"../../misc/LanguageViewModel.js\"\nimport { px, size } from \"../../gui/size.js\"\n\nexport interface MailFolderViewAttrs {\n\tmailboxDetail: MailboxDetail\n\tmailListToSelectedMail: ReadonlyMap<Id, Id>\n\tonFolderClick: (folder: MailFolder) => unknown\n\tonFolderDrop: (mailId: string, folder: MailFolder) => unknown\n\texpandedFolders: ReadonlySet<Id>\n\tonFolderExpanded: (folder: MailFolder, state: boolean) => unknown\n\tonShowFolderAddEditDialog: (mailGroupId: Id, folder: MailFolder | null, parentFolder: MailFolder | null) => unknown\n\tonDeleteCustomMailFolder: (folder: MailFolder) => unknown\n\tinEditMode: boolean\n\tonEditMailbox: () => unknown\n\tonEditingDone: () => unknown\n}\n\ntype Counters = Record<string, number>\n\n/** Displays a tree of all folders. */\nexport class MailFoldersView implements Component<MailFolderViewAttrs> {\n\tview({ attrs }: Vnode<MailFolderViewAttrs>): Children {\n\t\tconst { mailboxDetail } = attrs\n\t\tconst groupCounters = locator.mailModel.mailboxCounters()[mailboxDetail.mailGroup._id] || {}\n\t\t// Important: this array is keyed so each item must have a key and `null` cannot be in the array\n\t\t// So instead we push or not push into array\n\t\tconst customSystems = mailboxDetail.folders.customSubtrees\n\t\tconst systemSystems = mailboxDetail.folders.systemSubtrees\n\t\tconst children: Children = []\n\t\tconst selectedFolder = mailboxDetail.folders\n\t\t\t.getIndentedList()\n\t\t\t.map((f) => f.folder)\n\t\t\t.find((f) => isSelectedPrefix(MAIL_PREFIX + \"/\" + f.mails))\n\t\tconst path = selectedFolder ? mailboxDetail.folders.getPathToFolder(selectedFolder._id) : []\n\t\tconst systemChildren = this.renderFolderTree(systemSystems, groupCounters, attrs, path)\n\t\tif (systemChildren) {\n\t\t\tchildren.push(...systemChildren.children)\n\t\t}\n\t\tif (locator.logins.isInternalUserLoggedIn()) {\n\t\t\tchildren.push(\n\t\t\t\tm(\n\t\t\t\t\tSidebarSection,\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"yourFolders_action\",\n\t\t\t\t\t\tbutton: attrs.inEditMode ? this.renderCreateFolderAddButton(null, attrs) : this.renderEditFoldersButton(attrs),\n\t\t\t\t\t\tkey: \"yourFolders\", // we need to set a key because folder rows also have a key.\n\t\t\t\t\t},\n\t\t\t\t\tthis.renderFolderTree(customSystems, groupCounters, attrs, path).children,\n\t\t\t\t),\n\t\t\t)\n\t\t\tchildren.push(this.renderAddFolderButtonRow(attrs))\n\t\t}\n\t\treturn children\n\t}\n\n\tprivate renderFolderTree(\n\t\tsubSystems: readonly FolderSubtree[],\n\t\tgroupCounters: Counters,\n\t\tattrs: MailFolderViewAttrs,\n\t\tpath: MailFolder[],\n\t\tindentationLevel: number = 0,\n\t): { children: Children[]; numRows: number } {\n\t\t// we need to keep track of how many rows we've drawn so far for this subtree so that we can draw hierarchy lines correctly\n\t\tconst result: { children: Children[]; numRows: number } = { children: [], numRows: 0 }\n\t\tfor (let system of subSystems) {\n\t\t\tconst id = getElementId(system.folder)\n\t\t\tconst button: NavButtonAttrs = {\n\t\t\t\tlabel: () => getFolderName(system.folder),\n\t\t\t\thref: () => {\n\t\t\t\t\tif (attrs.inEditMode) {\n\t\t\t\t\t\treturn m.route.get()\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst mailId = attrs.mailListToSelectedMail.get(system.folder.mails)\n\t\t\t\t\t\tif (mailId) {\n\t\t\t\t\t\t\treturn `/mail/${system.folder.mails}/${mailId}`\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn `/mail/${system.folder.mails}`\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tisSelectedPrefix: attrs.inEditMode ? false : MAIL_PREFIX + \"/\" + system.folder.mails,\n\t\t\t\tcolors: NavButtonColor.Nav,\n\t\t\t\tclick: () => attrs.onFolderClick(system.folder),\n\t\t\t\tdropHandler: (droppedMailId) => attrs.onFolderDrop(droppedMailId, system.folder),\n\t\t\t\tdisableHoverBackground: true,\n\t\t\t\tdisabled: attrs.inEditMode,\n\t\t\t}\n\t\t\tconst currentExpansionState = attrs.inEditMode ? true : attrs.expandedFolders.has(getElementId(system.folder)) ?? false //default is false\n\t\t\tconst hasChildren = system.children.length > 0\n\t\t\tconst summedCount = !currentExpansionState && hasChildren ? this.getTotalFolderCounter(groupCounters, system) : groupCounters[system.folder.mails]\n\t\t\tconst childResult =\n\t\t\t\thasChildren && currentExpansionState\n\t\t\t\t\t? this.renderFolderTree(system.children, groupCounters, attrs, path, indentationLevel + 1)\n\t\t\t\t\t: { children: null, numRows: 0 }\n\t\t\tconst render = m.fragment(\n\t\t\t\t{\n\t\t\t\t\tkey: id,\n\t\t\t\t},\n\t\t\t\t[\n\t\t\t\t\tm(MailFolderRow, {\n\t\t\t\t\t\tcount: attrs.inEditMode ? 0 : summedCount,\n\t\t\t\t\t\tbutton,\n\t\t\t\t\t\ticon: getFolderIcon(system.folder),\n\t\t\t\t\t\trightButton: !(system.folder.folderType === MailFolderType.TRASH || system.folder.folderType === MailFolderType.SPAM)\n\t\t\t\t\t\t\t? this.createFolderMoreButton(system.folder, attrs)\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\texpanded: hasChildren ? currentExpansionState : null,\n\t\t\t\t\t\tindentationLevel: Math.min(indentationLevel, MAX_FOLDER_INDENT_LEVEL),\n\t\t\t\t\t\tonExpanderClick: hasChildren ? () => attrs.onFolderExpanded(system.folder, currentExpansionState) : noOp,\n\t\t\t\t\t\thasChildren,\n\t\t\t\t\t\tonSelectedPath: path.includes(system.folder),\n\t\t\t\t\t\tnumberOfPreviousRows: result.numRows,\n\t\t\t\t\t\tisLastSibling: last(subSystems) === system,\n\t\t\t\t\t\teditMode: attrs.inEditMode,\n\t\t\t\t\t}),\n\t\t\t\t\tchildResult.children,\n\t\t\t\t],\n\t\t\t)\n\t\t\tresult.numRows += childResult.numRows + 1\n\t\t\tresult.children.push(render)\n\t\t}\n\t\treturn result\n\t}\n\n\tprivate renderAddFolderButtonRow(attrs: MailFolderViewAttrs): Child {\n\t\t// This button needs to fill the whole role, but is not a navigation button (so IconButton or NavButton weren't appropriate)\n\t\treturn m(\n\t\t\t\"button.folder-row.flex.flex-row.mlr-button.border-radius-small.state-bg.button-height\",\n\t\t\t{\n\t\t\t\tkey: \"addFolder\",\n\t\t\t\tstyle: {\n\t\t\t\t\tcolor: theme.navigation_button,\n\t\t\t\t\twidth: `calc(100% - ${px(size.hpad_button * 2)})`,\n\t\t\t\t},\n\t\t\t\tonclick: () => {\n\t\t\t\t\tattrs.onShowFolderAddEditDialog(attrs.mailboxDetail.mailGroup._id, null, null)\n\t\t\t\t},\n\t\t\t},\n\t\t\tm(Icon, {\n\t\t\t\ticon: Icons.Add,\n\t\t\t\tlarge: true,\n\t\t\t\tstyle: {\n\t\t\t\t\tfill: theme.navigation_button,\n\t\t\t\t},\n\t\t\t\tclass: \"plr-button\",\n\t\t\t}),\n\t\t\tm(\"span.label.plr-button\", lang.get(\"addFolder_action\")),\n\t\t)\n\t}\n\n\tprivate getTotalFolderCounter(counters: Counters, system: FolderSubtree): number {\n\t\treturn (counters[system.folder.mails] ?? 0) + system.children.reduce((acc, child) => acc + this.getTotalFolderCounter(counters, child), 0)\n\t}\n\n\tprivate createFolderMoreButton(folder: MailFolder, attrs: MailFolderViewAttrs): IconButtonAttrs {\n\t\treturn attachDropdown({\n\t\t\tmainButtonAttrs: {\n\t\t\t\ttitle: \"more_label\",\n\t\t\t\ticon: Icons.More,\n\t\t\t\tcolors: ButtonColor.Nav,\n\t\t\t\tsize: ButtonSize.Compact,\n\t\t\t},\n\t\t\tchildAttrs: () => {\n\t\t\t\treturn folder.folderType === MailFolderType.CUSTOM\n\t\t\t\t\t? // cannot add new folder to custom folder in spam or trash folder\n\t\t\t\t\t  isSpamOrTrashFolder(attrs.mailboxDetail.folders, folder)\n\t\t\t\t\t\t? [this.editButtonAttrs(attrs, folder), this.deleteButtonAttrs(attrs, folder)]\n\t\t\t\t\t\t: [this.editButtonAttrs(attrs, folder), this.addButtonAttrs(attrs, folder), this.deleteButtonAttrs(attrs, folder)]\n\t\t\t\t\t: [this.addButtonAttrs(attrs, folder)]\n\t\t\t},\n\t\t})\n\t}\n\n\tprivate deleteButtonAttrs(attrs: MailFolderViewAttrs, folder: MailFolder): DropdownButtonAttrs {\n\t\treturn {\n\t\t\tlabel: \"delete_action\",\n\t\t\ticon: Icons.Trash,\n\t\t\tclick: () => {\n\t\t\t\tattrs.onDeleteCustomMailFolder(folder)\n\t\t\t},\n\t\t}\n\t}\n\n\tprivate addButtonAttrs(attrs: MailFolderViewAttrs, folder: MailFolder): DropdownButtonAttrs {\n\t\treturn {\n\t\t\tlabel: \"addFolder_action\",\n\t\t\ticon: Icons.Add,\n\t\t\tclick: () => {\n\t\t\t\tattrs.onShowFolderAddEditDialog(attrs.mailboxDetail.mailGroup._id, null, folder)\n\t\t\t},\n\t\t}\n\t}\n\n\tprivate editButtonAttrs(attrs: MailFolderViewAttrs, folder: MailFolder): DropdownButtonAttrs {\n\t\treturn {\n\t\t\tlabel: \"edit_action\",\n\t\t\ticon: Icons.Edit,\n\t\t\tclick: () => {\n\t\t\t\tattrs.onShowFolderAddEditDialog(\n\t\t\t\t\tattrs.mailboxDetail.mailGroup._id,\n\t\t\t\t\tfolder,\n\t\t\t\t\tfolder.parentFolder ? attrs.mailboxDetail.folders.getFolderById(folder.parentFolder) : null,\n\t\t\t\t)\n\t\t\t},\n\t\t}\n\t}\n\n\tprivate renderCreateFolderAddButton(parentFolder: MailFolder | null, attrs: MailFolderViewAttrs): Child {\n\t\treturn m(IconButton, {\n\t\t\ttitle: \"addFolder_action\",\n\t\t\tclick: () => {\n\t\t\t\treturn attrs.onShowFolderAddEditDialog(attrs.mailboxDetail.mailGroup._id, null, parentFolder)\n\t\t\t},\n\t\t\ticon: Icons.Add,\n\t\t\tsize: ButtonSize.Compact,\n\t\t})\n\t}\n\n\tprivate renderEditFoldersButton(attrs: MailFolderViewAttrs): Child {\n\t\treturn m(IconButton, {\n\t\t\ttitle: \"edit_action\",\n\t\t\tclick: () => attrs.onEditMailbox(),\n\t\t\ticon: Icons.Edit,\n\t\t\tsize: ButtonSize.Compact,\n\t\t})\n\t}\n}\n","import { modal, ModalComponent } from \"../../gui/base/Modal.js\"\nimport { assertNotNull, noOp, Thunk } from \"@tutao/tutanota-utils\"\nimport { focusNext, focusPrevious, keyManager, Shortcut } from \"../../misc/KeyManager.js\"\nimport { Keys } from \"../../api/common/TutanotaConstants.js\"\nimport m, { Children } from \"mithril\"\nimport { alpha, AlphaEnum, AnimationPromise, animations, DefaultAnimationTime, opacity } from \"../../gui/animation/Animations.js\"\nimport { getElevatedBackground, theme } from \"../../gui/theme.js\"\nimport { Button, ButtonType } from \"../../gui/base/Button.js\"\nimport { INPUT } from \"../../gui/base/Dialog.js\"\nimport { ease } from \"../../gui/animation/Easing.js\"\nimport { px, size } from \"../../gui/size.js\"\nimport { styles } from \"../../gui/styles.js\"\n\nexport class EditFoldersDialog implements ModalComponent {\n\tprivate visible: boolean\n\tprivate readonly _shortcuts: Shortcut[]\n\tprivate _domDialog: HTMLElement | null = null\n\t/** The element that was focused before we've shown the component so that we can return it back upon closing. */\n\tprivate focusedBeforeShown: HTMLElement | null = null\n\n\tprivate _closeHandler: Thunk | null = null\n\n\tprivate usedBottomNavBefore: boolean = styles.isUsingBottomNavigation()\n\n\tconstructor(private readonly folderList: () => Children) {\n\t\tthis.visible = false\n\n\t\tthis._shortcuts = [\n\t\t\t{\n\t\t\t\tkey: Keys.RETURN,\n\t\t\t\tshift: false,\n\t\t\t\texec: () => this.close(),\n\t\t\t\thelp: \"close_alt\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.ESC,\n\t\t\t\tshift: false,\n\t\t\t\texec: () => this.close(),\n\t\t\t\thelp: \"close_alt\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.TAB,\n\t\t\t\tshift: true,\n\t\t\t\texec: () => (this._domDialog ? focusPrevious(this._domDialog) : false),\n\t\t\t\thelp: \"selectPrevious_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.TAB,\n\t\t\t\tshift: false,\n\t\t\t\texec: () => (this._domDialog ? focusNext(this._domDialog) : false),\n\t\t\t\thelp: \"selectNext_action\",\n\t\t\t},\n\t\t]\n\n\t\tthis.view = this.view.bind(this)\n\t}\n\n\tview() {\n\t\tif (this.usedBottomNavBefore !== styles.isUsingBottomNavigation()) {\n\t\t\tthis.close()\n\t\t}\n\t\tthis.usedBottomNavBefore = styles.isUsingBottomNavigation()\n\t\tconst marginTop = this.usedBottomNavBefore ? \"env(safe-area-inset-top)\" : px(size.navbar_height)\n\t\treturn m(\n\t\t\t\".flex.col\",\n\t\t\t{\n\t\t\t\tstyle: {\n\t\t\t\t\twidth: px(size.first_col_max_width - size.button_height),\n\t\t\t\t\theight: `calc(100% - ${marginTop})`,\n\t\t\t\t\t// for the header\n\t\t\t\t\tmarginTop,\n\t\t\t\t\tmarginLeft: px(size.button_height),\n\t\t\t\t},\n\t\t\t\tonclick: (e: MouseEvent) => e.stopPropagation(),\n\t\t\t\t// do not propagate clicks on the dialog as the Modal expects all propagated clicks to be clicks on the background\n\t\t\t\toncreate: (vnode) => {\n\t\t\t\t\tthis._domDialog = vnode.dom as HTMLElement\n\t\t\t\t\tlet animation: AnimationPromise | null = null\n\n\t\t\t\t\tconst bgcolor = theme.navigation_bg\n\t\t\t\t\tconst children = Array.from(this._domDialog.children) as Array<HTMLElement>\n\t\t\t\t\tfor (let child of children) {\n\t\t\t\t\t\tchild.style.opacity = \"0\"\n\t\t\t\t\t}\n\t\t\t\t\tthis._domDialog.style.backgroundColor = `rgba(0, 0, 0, 0)`\n\t\t\t\t\tanimation = Promise.all([\n\t\t\t\t\t\tanimations.add(this._domDialog, alpha(AlphaEnum.BackgroundColor, bgcolor, 0, 1)),\n\t\t\t\t\t\tanimations.add(children, opacity(0, 1, true), {\n\t\t\t\t\t\t\tdelay: DefaultAnimationTime / 2,\n\t\t\t\t\t\t}),\n\t\t\t\t\t])\n\n\t\t\t\t\t// select first input field. blur first to avoid that users can enter text in the previously focused element while the animation is running\n\t\t\t\t\twindow.requestAnimationFrame(() => {\n\t\t\t\t\t\tconst activeElement = document.activeElement as HTMLElement | null\n\t\t\t\t\t\tif (activeElement && typeof activeElement.blur === \"function\") {\n\t\t\t\t\t\t\tactiveElement.blur()\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\tanimation.then(() => {\n\t\t\t\t\t\tthis.defaultFocusOnLoad()\n\t\t\t\t\t})\n\t\t\t\t},\n\t\t\t},\n\t\t\t[\n\t\t\t\tm(\n\t\t\t\t\t\".plr-button.mt.mb\",\n\t\t\t\t\tm(Button, {\n\t\t\t\t\t\ttype: ButtonType.Login,\n\t\t\t\t\t\tlabel: \"done_action\",\n\t\t\t\t\t\tclick: () => this.close(),\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t\tm(\n\t\t\t\t\t\".scroll.overflow-x-hidden.flex.col.flex-grow\",\n\t\t\t\t\t{\n\t\t\t\t\t\tonscroll: (e: Event) => {\n\t\t\t\t\t\t\tconst target = e.target as HTMLElement\n\t\t\t\t\t\t\ttarget.style.borderTop = `1px solid ${theme.content_border}`\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tthis.folderList(),\n\t\t\t\t),\n\t\t\t],\n\t\t)\n\t}\n\n\tprivate defaultFocusOnLoad() {\n\t\tconst dom = assertNotNull(this._domDialog)\n\t\tlet inputs = Array.from(dom.querySelectorAll(INPUT)) as Array<HTMLElement>\n\n\t\tif (inputs.length > 0) {\n\t\t\tinputs[0].focus()\n\t\t} else {\n\t\t\tlet button = dom.querySelector(\"button\")\n\n\t\t\tif (button) {\n\t\t\t\tbutton.focus()\n\t\t\t}\n\t\t}\n\t}\n\n\thideAnimation(): Promise<void> {\n\t\tlet bgcolor = getElevatedBackground()\n\n\t\tif (this._domDialog) {\n\t\t\treturn Promise.all([\n\t\t\t\tanimations.add(this._domDialog.children, opacity(1, 0, true)),\n\t\t\t\tanimations.add(this._domDialog, alpha(AlphaEnum.BackgroundColor, bgcolor, 1, 0), {\n\t\t\t\t\tdelay: DefaultAnimationTime / 2,\n\t\t\t\t\teasing: ease.linear,\n\t\t\t\t}),\n\t\t\t]).then(noOp)\n\t\t} else {\n\t\t\treturn Promise.resolve()\n\t\t}\n\t}\n\n\tshow(): EditFoldersDialog {\n\t\tthis.focusedBeforeShown = document.activeElement as HTMLElement\n\t\tmodal.display(this)\n\t\tthis.visible = true\n\t\treturn this\n\t}\n\n\tclose(): void {\n\t\tthis.visible = false\n\t\tmodal.remove(this)\n\t\tthis.focusedBeforeShown && this.focusedBeforeShown.focus()\n\t}\n\n\t/**\n\t * Should be called to close a dialog. Notifies the closeHandler about the close attempt.\n\t */\n\tonClose(): void {\n\t\tif (this._closeHandler) {\n\t\t\tthis._closeHandler()\n\t\t} else {\n\t\t\tthis.close()\n\t\t}\n\t}\n\n\tshortcuts(): Shortcut[] {\n\t\treturn this._shortcuts\n\t}\n\n\tbackgroundClick(e: MouseEvent) {}\n\n\tpopState(e: Event): boolean {\n\t\tthis.onClose()\n\t\treturn false\n\t}\n\n\taddShortcut(shortcut: Shortcut): EditFoldersDialog {\n\t\tthis._shortcuts.push(shortcut)\n\n\t\tif (this.visible) {\n\t\t\tkeyManager.registerModalShortcuts([shortcut])\n\t\t}\n\n\t\treturn this\n\t}\n\n\tstatic showEdit(folders: () => Children) {\n\t\tnew EditFoldersDialog(folders).show()\n\t}\n}\n","import m, { Children, Component, Vnode } from \"mithril\"\nimport { ButtonType } from \"../../gui/base/Button.js\"\nimport { CalendarAttendeeStatus, CalendarMethod } from \"../../api/common/TutanotaConstants\"\nimport { lang } from \"../../misc/LanguageViewModel\"\nimport type { CalendarEvent, Mail } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { Dialog } from \"../../gui/base/Dialog\"\nimport { showProgressDialog } from \"../../gui/dialogs/ProgressDialog\"\nimport { isRepliedTo } from \"../model/MailUtils\"\nimport { findAttendeeInAddresses } from \"../../api/common/utils/CommonCalendarUtils.js\"\nimport { BannerType, InfoBanner, InfoBannerAttrs } from \"../../gui/base/InfoBanner.js\"\nimport { Icons } from \"../../gui/base/icons/Icons.js\"\nimport { LazyLoaded } from \"@tutao/tutanota-utils\"\n\nexport type EventBannerAttrs = {\n\tevent: CalendarEvent\n\tmail: Mail\n\trecipient: string\n\tmethod: CalendarMethod\n}\n\n/**\n * displayed above a mail that contains a calendar invite.\n * Its main function is to make it possible to inspect the event with the CalendarEventPopup, to quick respond\n * your attendance with Accept/Decline/Tentative while adding the event to your personal calendar\n */\nexport class EventBanner implements Component<EventBannerAttrs> {\n\t/** ReplyButtons are used from mail-view and calendar-view.\n\t * they can't import each other and only have gui-base as a\n\t * common ancestor, where these don't belong. */\n\tprivate readonly ReplyButtons = new LazyLoaded(async () => (await import(\"../../calendar/view/eventpopup/EventPreviewView.js\")).ReplyButtons)\n\n\tview({ attrs }: Vnode<EventBannerAttrs>): Children {\n\t\tconst { event, mail } = attrs\n\t\treturn m(InfoBanner, {\n\t\t\tmessage: () => this.getMessage(attrs),\n\t\t\ttype: BannerType.Info,\n\t\t\ticon: Icons.People,\n\t\t\tbuttons: [\n\t\t\t\t{\n\t\t\t\t\tlabel: \"viewEvent_action\",\n\t\t\t\t\ttype: ButtonType.Secondary,\n\t\t\t\t\tclick: (e, dom) =>\n\t\t\t\t\t\timport(\"../../calendar/date/CalendarInvites\").then(({ showEventDetails }) =>\n\t\t\t\t\t\t\tshowEventDetails(event, dom.getBoundingClientRect(), mail),\n\t\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t],\n\t\t} satisfies InfoBannerAttrs)\n\t}\n\n\tprivate getMessage({ event, mail, recipient, method }: EventBannerAttrs): Children {\n\t\tconst ownAttendee = findAttendeeInAddresses(event.attendees, [recipient])\n\t\tif (method === CalendarMethod.REQUEST && ownAttendee != null) {\n\t\t\tif (isRepliedTo(mail) || ownAttendee.status !== CalendarAttendeeStatus.NEEDS_ACTION) {\n\t\t\t\treturn m(\".align-self-start.start.small\", lang.get(\"alreadyReplied_msg\"))\n\t\t\t} else if (this.ReplyButtons.isLoaded()) {\n\t\t\t\treturn m(this.ReplyButtons.getLoaded(), {\n\t\t\t\t\townAttendee,\n\t\t\t\t\tsetParticipation: (status: CalendarAttendeeStatus) => sendResponse(event, recipient, status, mail),\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tthis.ReplyButtons.reload().then(m.redraw)\n\t\t\t\treturn null\n\t\t\t}\n\t\t} else if (method === CalendarMethod.REPLY) {\n\t\t\tm(\".pt.align-self-start.start.small\", lang.get(\"eventNotificationUpdated_msg\"))\n\t\t} else {\n\t\t\treturn null\n\t\t}\n\t}\n}\n\n/** show a progress dialog while sending a response to the event's organizer and update the ui. will always send a reply, even if the status did not change. */\nexport function sendResponse(event: CalendarEvent, recipient: string, status: CalendarAttendeeStatus, previousMail: Mail) {\n\tshowProgressDialog(\n\t\t\"pleaseWait_msg\",\n\t\timport(\"../../calendar/date/CalendarInvites\").then(({ getLatestEvent, replyToEventInvitation }) => {\n\t\t\treturn getLatestEvent(event).then((latestEvent) => {\n\t\t\t\tconst ownAttendee = findAttendeeInAddresses(latestEvent.attendees, [recipient])\n\n\t\t\t\tif (ownAttendee == null) {\n\t\t\t\t\tDialog.message(\"attendeeNotFound_msg\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\treplyToEventInvitation(latestEvent, ownAttendee, status, previousMail)\n\t\t\t\t\t.then(() => (ownAttendee.status = status))\n\t\t\t\t\t.then(m.redraw)\n\t\t\t})\n\t\t}),\n\t)\n}\n","import m, { Children, Component, Vnode } from \"mithril\"\nimport { InfoLink, lang } from \"../../misc/LanguageViewModel.js\"\nimport { getFolderIconByType, getMailAddressDisplayText, getSenderAddressDisplay, isTutanotaTeamMail } from \"../model/MailUtils.js\"\nimport { theme } from \"../../gui/theme.js\"\nimport { styles } from \"../../gui/styles.js\"\nimport { ExpanderPanel } from \"../../gui/base/Expander.js\"\nimport { File as TutanotaFile } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { BannerType, InfoBanner } from \"../../gui/base/InfoBanner.js\"\nimport { Icons } from \"../../gui/base/icons/Icons.js\"\nimport { EventBanner } from \"./EventBanner.js\"\nimport { RecipientButton } from \"../../gui/base/RecipientButton.js\"\nimport { createAsyncDropdown, createDropdown, DropdownButtonAttrs } from \"../../gui/base/Dropdown.js\"\nimport { InboxRuleType, Keys, MailAuthenticationStatus, TabIndex } from \"../../api/common/TutanotaConstants.js\"\nimport { Icon, progressIcon } from \"../../gui/base/Icon.js\"\nimport { formatDateWithWeekday, formatDateWithWeekdayAndYear, formatStorageSize, formatTime } from \"../../misc/Formatter.js\"\nimport { isAndroidApp, isDesktop, isIOSApp } from \"../../api/common/Env.js\"\nimport { Button, ButtonAttrs, ButtonType } from \"../../gui/base/Button.js\"\nimport Badge from \"../../gui/base/Badge.js\"\nimport { ContentBlockingStatus, MailViewerViewModel } from \"./MailViewerViewModel.js\"\nimport { canSeeTutanotaLinks, createMoreSecondaryButtonAttrs } from \"../../gui/base/GuiUtils.js\"\nimport { isNotNull, noOp } from \"@tutao/tutanota-utils\"\nimport { IconButton } from \"../../gui/base/IconButton.js\"\nimport { promptAndDeleteMails, showMoveMailsDropdown } from \"./MailGuiUtils.js\"\nimport { BootIcons } from \"../../gui/base/icons/BootIcons.js\"\nimport { editDraft, mailViewerMoreActions } from \"./MailViewerUtils.js\"\nimport { liveDataAttrs } from \"../../gui/AriaUtils.js\"\nimport { isKeyPressed } from \"../../misc/KeyManager.js\"\nimport { AttachmentBubble } from \"../../gui/AttachmentBubble.js\"\nimport { responsiveCardHMargin, responsiveCardHPadding } from \"../../gui/cards.js\"\n\nexport interface MailAddressAndName {\n\tname: string\n\taddress: string\n}\n\nexport type MailAddressDropdownCreator = (args: {\n\tmailAddress: MailAddressAndName\n\tdefaultInboxRuleField: InboxRuleType | null\n\tcreateContact?: boolean\n}) => Promise<Array<DropdownButtonAttrs>>\n\nexport interface MailViewerHeaderAttrs {\n\t// Passing the whole viewModel because there are a lot of separate bits we might need.\n\t// If we want to reuse this view we should probably pass everything on its own.\n\tviewModel: MailViewerViewModel\n\tcreateMailAddressContextButtons: MailAddressDropdownCreator\n\tisPrimary: boolean\n}\n\n/** The upper part of the mail viewer, everything but the mail body itself. */\nexport class MailViewerHeader implements Component<MailViewerHeaderAttrs> {\n\tprivate detailsExpanded = false\n\tprivate filesExpanded = false\n\n\tview({ attrs }: Vnode<MailViewerHeaderAttrs>): Children {\n\t\tconst { viewModel } = attrs\n\t\tconst dateTime = formatDateWithWeekday(viewModel.mail.receivedDate) + \" • \" + formatTime(viewModel.mail.receivedDate)\n\t\tconst dateTimeFull = formatDateWithWeekdayAndYear(viewModel.mail.receivedDate) + \" • \" + formatTime(viewModel.mail.receivedDate)\n\n\t\treturn m(\".header\", [\n\t\t\tthis.renderSubjectActionsLine(attrs),\n\t\t\tthis.renderFolderText(viewModel),\n\t\t\tthis.renderAddressesAndDate(viewModel, attrs, dateTime, dateTimeFull),\n\t\t\tm(\n\t\t\t\tExpanderPanel,\n\t\t\t\t{\n\t\t\t\t\texpanded: this.detailsExpanded,\n\t\t\t\t},\n\t\t\t\tthis.renderDetails(attrs, { bubbleMenuWidth: 300 }),\n\t\t\t),\n\t\t\tthis.renderAttachments(viewModel),\n\t\t\tthis.renderConnectionLostBanner(viewModel),\n\t\t\tthis.renderEventBanner(viewModel),\n\t\t\tthis.renderBanners(attrs),\n\t\t])\n\t}\n\n\tprivate renderFolderText(viewModel: MailViewerViewModel) {\n\t\tconst folderInfo = viewModel.getFolderInfo()\n\t\tif (!folderInfo) return null\n\t\tconst icon = getFolderIconByType(folderInfo.folderType)\n\n\t\treturn viewModel.getFolderMailboxText()\n\t\t\t? m(\".flex.small.plr-l.mt-xs.mb-xs.ml-between-s\", [\n\t\t\t\t\tm(\".b\", m(\"\", lang.get(\"location_label\"))),\n\t\t\t\t\tm(Icon, {\n\t\t\t\t\t\ticon,\n\t\t\t\t\t\tcontainer: \"div\",\n\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\tfill: theme.content_button,\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t\tm(\".span.mr-s\", folderInfo.name),\n\t\t\t  ])\n\t\t\t: null\n\t}\n\n\tprivate renderAddressesAndDate(viewModel: MailViewerViewModel, attrs: MailViewerHeaderAttrs, dateTime: string, dateTimeFull: string) {\n\t\tconst folderInfo = viewModel.getFolderInfo()\n\t\tif (!folderInfo) return null\n\n\t\treturn m(\n\t\t\t\".flex.mt-xs.click.col\",\n\t\t\t{\n\t\t\t\tclass: responsiveCardHMargin(),\n\t\t\t\trole: \"button\",\n\t\t\t\t\"aria-pressed\": String(this.detailsExpanded),\n\t\t\t\ttabindex: TabIndex.Default,\n\t\t\t\tonclick: () => {\n\t\t\t\t\tthis.detailsExpanded = !this.detailsExpanded\n\t\t\t\t},\n\t\t\t\tonkeydown: (e: KeyboardEvent) => {\n\t\t\t\t\tif (isKeyPressed(e.keyCode, Keys.SPACE, Keys.RETURN)) {\n\t\t\t\t\t\tthis.detailsExpanded = !this.detailsExpanded\n\t\t\t\t\t\te.preventDefault()\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t[\n\t\t\t\tm(\".small.flex.flex-wrap.items-start\", [m(\"span.text-break\", getSenderAddressDisplay(viewModel.mail))]),\n\t\t\t\tm(\".flex\", [\n\t\t\t\t\tthis.getRecipientEmailAddress(attrs),\n\t\t\t\t\tm(\".flex-grow\"),\n\t\t\t\t\tm(\".flex.items-center.white-space-pre.ml-s.ml-between-s\", {\n\t\t\t\t\t\t// Orca refuses to read ut unless it's not focusable\n\t\t\t\t\t\ttabindex: TabIndex.Default,\n\t\t\t\t\t\t\"aria-label\": lang.get(viewModel.isConfidential() ? \"confidential_action\" : \"nonConfidential_action\") + \", \" + dateTime,\n\t\t\t\t\t}),\n\t\t\t\t\tm(\".flex.ml-between-s.items-center\", [\n\t\t\t\t\t\tviewModel.isConfidential()\n\t\t\t\t\t\t\t? m(Icon, {\n\t\t\t\t\t\t\t\t\ticon: Icons.Lock,\n\t\t\t\t\t\t\t\t\tcontainer: \"div\",\n\t\t\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\t\t\tfill: theme.content_button,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t  })\n\t\t\t\t\t\t\t: null,\n\n\t\t\t\t\t\tm(Icon, {\n\t\t\t\t\t\t\ticon: getFolderIconByType(folderInfo.folderType),\n\t\t\t\t\t\t\tcontainer: \"div\",\n\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\tfill: theme.content_button,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\thoverText: folderInfo.name,\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tm(\".small.font-weight-600.selectable.no-wrap\", { style: { color: theme.content_button } }, [\n\t\t\t\t\t\t\tm(\".noprint\", dateTime), // show the short date when viewing\n\t\t\t\t\t\t\tm(\".noscreen\", dateTimeFull), // show the date with year when printing\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\tprivate renderSubjectActionsLine(attrs: MailViewerHeaderAttrs) {\n\t\tconst { viewModel } = attrs\n\t\tconst classes = this.makeSubjectActionsLineClasses()\n\n\t\treturn m(\n\t\t\tclasses,\n\t\t\t{\n\t\t\t\trole: \"button\",\n\t\t\t\t\"mail-expander\": \"true\",\n\t\t\t\ttabindex: TabIndex.Default,\n\t\t\t\tonclick: (e: MouseEvent) => {\n\t\t\t\t\tviewModel.collapseMail()\n\t\t\t\t\te.stopPropagation()\n\t\t\t\t},\n\t\t\t\tonkeydown: (e: KeyboardEvent) => {\n\t\t\t\t\tif (isKeyPressed(e.keyCode, Keys.SPACE, Keys.RETURN) && (e.target as HTMLElement).hasAttribute(\"mail-expander\")) {\n\t\t\t\t\t\tviewModel.collapseMail()\n\t\t\t\t\t\te.preventDefault()\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t[\n\t\t\t\tm(\n\t\t\t\t\t\".flex.flex-grow.align-self-start.items-start\",\n\t\t\t\t\t{\n\t\t\t\t\t\tclass: styles.isSingleColumnLayout() ? \"mt-m\" : \"mt\",\n\t\t\t\t\t},\n\t\t\t\t\t[\n\t\t\t\t\t\tviewModel.isUnread() ? this.renderUnreadDot() : null,\n\t\t\t\t\t\tviewModel.isDraftMail()\n\t\t\t\t\t\t\t? m(\n\t\t\t\t\t\t\t\t\t\".mr-xs.align-self-center\",\n\t\t\t\t\t\t\t\t\tm(Icon, {\n\t\t\t\t\t\t\t\t\t\ticon: Icons.Edit,\n\t\t\t\t\t\t\t\t\t\tcontainer: \"div\",\n\t\t\t\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\t\t\t\tfill: theme.content_button,\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  )\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\tthis.tutaoBadge(viewModel),\n\t\t\t\t\t\tm(\"span.text-break\" + (viewModel.isUnread() ? \".font-weight-600\" : \"\"), viewModel.mail.sender.name),\n\t\t\t\t\t],\n\t\t\t\t),\n\t\t\t\tm(\n\t\t\t\t\t\".flex-end.items-start.ml-between-s\",\n\t\t\t\t\t{\n\t\t\t\t\t\tclass: styles.isSingleColumnLayout() ? \"\" : \"mt-xs\",\n\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t// align \"more\" button with the datetime text\n\t\t\t\t\t\t\tmarginRight: styles.isSingleColumnLayout() ? \"-3px\" : \"6px\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tthis.moreButton(attrs),\n\t\t\t\t),\n\t\t\t],\n\t\t)\n\t}\n\n\tprivate renderUnreadDot(): Children {\n\t\treturn m(\n\t\t\t\".flex.flex-no-grow.no-shrink.pr-s\",\n\t\t\t{\n\t\t\t\tstyle: {\n\t\t\t\t\tpaddingTop: \"2px\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tm(\".dot.bg-accent-fg\"),\n\t\t)\n\t}\n\n\tprivate makeSubjectActionsLineClasses() {\n\t\tlet classes = \".flex.click\"\n\t\tif (styles.isSingleColumnLayout()) {\n\t\t\tclasses += \".ml\"\n\t\t} else {\n\t\t\tclasses += \".pl-l\"\n\t\t}\n\n\t\treturn classes\n\t}\n\n\tprivate renderBanners(attrs: MailViewerHeaderAttrs): Children {\n\t\tconst { viewModel } = attrs\n\t\tif (viewModel.isCollapsed()) return null\n\t\t// we don't wrap it in a single element because our container might depend on us being separate children for margins\n\t\treturn [\n\t\t\tm(\n\t\t\t\t\".\" + responsiveCardHMargin(),\n\t\t\t\tthis.renderPhishingWarning(viewModel) ||\n\t\t\t\t\tthis.renderHardAuthenticationFailWarning(viewModel) ||\n\t\t\t\t\tthis.renderSoftAuthenticationFailWarning(viewModel),\n\t\t\t),\n\t\t\tm(\".\" + responsiveCardHMargin(), this.renderExternalContentBanner(attrs)),\n\t\t\tm(\"hr.hr.mt-xs.\" + responsiveCardHMargin()),\n\t\t].filter(Boolean)\n\t}\n\n\tprivate renderConnectionLostBanner(viewModel: MailViewerViewModel): Children {\n\t\t// If the mail body failed to load, then we show a message in the main column\n\t\t// If the mail body did load but not everything else, we show the message here\n\t\tif (viewModel.isConnectionLost()) {\n\t\t\treturn m(\n\t\t\t\t\".\" + responsiveCardHMargin(),\n\t\t\t\tm(InfoBanner, {\n\t\t\t\t\tmessage: \"mailPartsNotLoaded_msg\",\n\t\t\t\t\ticon: Icons.Warning,\n\t\t\t\t\tbuttons: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlabel: \"retry_action\",\n\t\t\t\t\t\t\tclick: () => viewModel.loadAll(Promise.resolve()),\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} else {\n\t\t\treturn null\n\t\t}\n\t}\n\n\tprivate renderEventBanner(viewModel: MailViewerViewModel): Children {\n\t\tconst event = viewModel.getCalendarEventAttachment()\n\t\treturn event\n\t\t\t? m(\n\t\t\t\t\t\".\" + responsiveCardHMargin(),\n\t\t\t\t\tm(EventBanner, {\n\t\t\t\t\t\tevent: event.event,\n\t\t\t\t\t\tmethod: event.method,\n\t\t\t\t\t\trecipient: event.recipient,\n\t\t\t\t\t\tmail: viewModel.mail,\n\t\t\t\t\t}),\n\t\t\t  )\n\t\t\t: null\n\t}\n\n\tprivate renderDetails(attrs: MailViewerHeaderAttrs, { bubbleMenuWidth }: { bubbleMenuWidth: number }): Children {\n\t\tconst { viewModel, createMailAddressContextButtons } = attrs\n\t\tconst envelopeSender = viewModel.getDifferentEnvelopeSender()\n\t\treturn m(\".\" + responsiveCardHPadding(), liveDataAttrs(), [\n\t\t\tm(\n\t\t\t\t\".mt-s\",\n\t\t\t\tm(\".small.b\", lang.get(\"from_label\")),\n\t\t\t\tm(RecipientButton, {\n\t\t\t\t\tlabel: getMailAddressDisplayText(viewModel.getSender().name, viewModel.getSender().address, false),\n\t\t\t\t\tclick: createAsyncDropdown({\n\t\t\t\t\t\tlazyButtons: () =>\n\t\t\t\t\t\t\tcreateMailAddressContextButtons({\n\t\t\t\t\t\t\t\tmailAddress: viewModel.getSender(),\n\t\t\t\t\t\t\t\tdefaultInboxRuleField: InboxRuleType.FROM_EQUALS,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\twidth: bubbleMenuWidth,\n\t\t\t\t\t}),\n\t\t\t\t}),\n\t\t\t\tenvelopeSender\n\t\t\t\t\t? [\n\t\t\t\t\t\t\tm(\".small.b\", lang.get(\"sender_label\")),\n\t\t\t\t\t\t\tm(RecipientButton, {\n\t\t\t\t\t\t\t\tlabel: getMailAddressDisplayText(\"\", envelopeSender, false),\n\t\t\t\t\t\t\t\tclick: createAsyncDropdown({\n\t\t\t\t\t\t\t\t\tlazyButtons: async () => {\n\t\t\t\t\t\t\t\t\t\tconst childElements = [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tinfo: lang.get(\"envelopeSenderInfo_msg\"),\n\t\t\t\t\t\t\t\t\t\t\t\tcenter: false,\n\t\t\t\t\t\t\t\t\t\t\t\tbold: false,\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tinfo: envelopeSender,\n\t\t\t\t\t\t\t\t\t\t\t\tcenter: true,\n\t\t\t\t\t\t\t\t\t\t\t\tbold: true,\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\tconst contextButtons = await createMailAddressContextButtons({\n\t\t\t\t\t\t\t\t\t\t\tmailAddress: {\n\t\t\t\t\t\t\t\t\t\t\t\taddress: envelopeSender,\n\t\t\t\t\t\t\t\t\t\t\t\tname: \"\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tdefaultInboxRuleField: InboxRuleType.FROM_EQUALS,\n\t\t\t\t\t\t\t\t\t\t\tcreateContact: false,\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\treturn [...childElements, ...contextButtons]\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\twidth: bubbleMenuWidth,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t  ]\n\t\t\t\t\t: null,\n\t\t\t),\n\t\t\tm(\n\t\t\t\t\".mt-s\",\n\t\t\t\tviewModel.getToRecipients().length\n\t\t\t\t\t? [\n\t\t\t\t\t\t\tm(\".small.b\", lang.get(\"to_label\")),\n\t\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\t\".flex.col.mt-between-s\",\n\t\t\t\t\t\t\t\tviewModel.getToRecipients().map((recipient) =>\n\t\t\t\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\t\t\t\".flex\",\n\t\t\t\t\t\t\t\t\t\tm(RecipientButton, {\n\t\t\t\t\t\t\t\t\t\t\tlabel: getMailAddressDisplayText(recipient.name, recipient.address, false),\n\t\t\t\t\t\t\t\t\t\t\tclick: createAsyncDropdown({\n\t\t\t\t\t\t\t\t\t\t\t\tlazyButtons: () =>\n\t\t\t\t\t\t\t\t\t\t\t\t\tcreateMailAddressContextButtons({\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tmailAddress: recipient,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdefaultInboxRuleField: InboxRuleType.RECIPIENT_TO_EQUALS,\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\twidth: bubbleMenuWidth,\n\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t\t\t// To wrap text inside flex container, we need to allow element to shrink and pick own width\n\t\t\t\t\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\t\t\t\t\tflex: \"0 1 auto\",\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),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t  ]\n\t\t\t\t\t: null,\n\t\t\t),\n\t\t\tm(\n\t\t\t\t\".mt-s\",\n\t\t\t\tviewModel.getCcRecipients().length\n\t\t\t\t\t? [\n\t\t\t\t\t\t\tm(\".small.b\", lang.get(\"cc_label\")),\n\t\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\t\".flex-start.flex-wrap\",\n\t\t\t\t\t\t\t\tviewModel.getCcRecipients().map((recipient) =>\n\t\t\t\t\t\t\t\t\tm(RecipientButton, {\n\t\t\t\t\t\t\t\t\t\tlabel: getMailAddressDisplayText(recipient.name, recipient.address, false),\n\t\t\t\t\t\t\t\t\t\tclick: createAsyncDropdown({\n\t\t\t\t\t\t\t\t\t\t\tlazyButtons: () =>\n\t\t\t\t\t\t\t\t\t\t\t\tcreateMailAddressContextButtons({\n\t\t\t\t\t\t\t\t\t\t\t\t\tmailAddress: recipient,\n\t\t\t\t\t\t\t\t\t\t\t\t\tdefaultInboxRuleField: InboxRuleType.RECIPIENT_CC_EQUALS,\n\t\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t\t\twidth: bubbleMenuWidth,\n\t\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\tflex: \"0 1 auto\",\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),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t  ]\n\t\t\t\t\t: null,\n\t\t\t),\n\t\t\tm(\n\t\t\t\t\".mt-s\",\n\t\t\t\tviewModel.getBccRecipients().length\n\t\t\t\t\t? [\n\t\t\t\t\t\t\tm(\".small.b\", lang.get(\"bcc_label\")),\n\t\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\t\".flex-start.flex-wrap\",\n\t\t\t\t\t\t\t\tviewModel.getBccRecipients().map((recipient) =>\n\t\t\t\t\t\t\t\t\tm(RecipientButton, {\n\t\t\t\t\t\t\t\t\t\tlabel: getMailAddressDisplayText(recipient.name, recipient.address, false),\n\t\t\t\t\t\t\t\t\t\tclick: createAsyncDropdown({\n\t\t\t\t\t\t\t\t\t\t\tlazyButtons: () =>\n\t\t\t\t\t\t\t\t\t\t\t\tcreateMailAddressContextButtons({\n\t\t\t\t\t\t\t\t\t\t\t\t\tmailAddress: recipient,\n\t\t\t\t\t\t\t\t\t\t\t\t\tdefaultInboxRuleField: InboxRuleType.RECIPIENT_BCC_EQUALS,\n\t\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t\t\twidth: bubbleMenuWidth,\n\t\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\tflex: \"0 1 auto\",\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),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t  ]\n\t\t\t\t\t: null,\n\t\t\t),\n\t\t\tm(\n\t\t\t\t\".mt-s\",\n\t\t\t\tviewModel.getReplyTos().length\n\t\t\t\t\t? [\n\t\t\t\t\t\t\tm(\".small.b\", lang.get(\"replyTo_label\")),\n\t\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\t\".flex-start.flex-wrap\",\n\t\t\t\t\t\t\t\tviewModel.getReplyTos().map((recipient) =>\n\t\t\t\t\t\t\t\t\tm(RecipientButton, {\n\t\t\t\t\t\t\t\t\t\tlabel: getMailAddressDisplayText(recipient.name, recipient.address, false),\n\t\t\t\t\t\t\t\t\t\tclick: createAsyncDropdown({\n\t\t\t\t\t\t\t\t\t\t\tlazyButtons: () =>\n\t\t\t\t\t\t\t\t\t\t\t\tcreateMailAddressContextButtons({\n\t\t\t\t\t\t\t\t\t\t\t\t\tmailAddress: recipient,\n\t\t\t\t\t\t\t\t\t\t\t\t\tdefaultInboxRuleField: null,\n\t\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t\t\twidth: bubbleMenuWidth,\n\t\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\tflex: \"0 1 auto\",\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),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t  ]\n\t\t\t\t\t: null,\n\t\t\t),\n\t\t])\n\t}\n\n\tprivate renderAttachments(viewModel: MailViewerViewModel): Children {\n\t\t// Show a loading symbol if we are loading attachments\n\t\tif (viewModel.isLoadingAttachments() && !viewModel.isConnectionLost()) {\n\t\t\treturn m(\".flex.\" + responsiveCardHMargin(), [\n\t\t\t\tm(\".flex-v-center.pl-button\", progressIcon()),\n\t\t\t\tm(\".small.flex-v-center.plr.button-height\", lang.get(\"loading_msg\")),\n\t\t\t])\n\t\t} else {\n\t\t\tconst attachments = viewModel.getNonInlineAttachments()\n\t\t\tconst attachmentCount = attachments.length\n\n\t\t\t// Do nothing if we have no attachments\n\t\t\tif (attachmentCount === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\t// Get the total size of the attachments\n\t\t\tlet totalAttachmentSize = 0\n\t\t\tattachments.forEach((attachment) => (totalAttachmentSize += Number(attachment.size)))\n\n\t\t\treturn [\n\t\t\t\tm(\".flex\" + \".\" + responsiveCardHMargin(), liveDataAttrs(), [\n\t\t\t\t\tattachmentCount === 1\n\t\t\t\t\t\t? // If we have exactly one attachment, just show the attachment\n\t\t\t\t\t\t  this.renderAttachmentContainer(viewModel, attachments)\n\t\t\t\t\t\t: // Otherwise, we show the number of attachments and its total size along with a show all button\n\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.center-vertically.click.flex-grow.ml-between-s.mt-xs\",\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\trole: \"button\",\n\t\t\t\t\t\t\t\t\t\t\"aria-pressed\": String(this.detailsExpanded),\n\t\t\t\t\t\t\t\t\t\ttabindex: TabIndex.Default,\n\t\t\t\t\t\t\t\t\t\tonclick: () => (this.filesExpanded = !this.filesExpanded),\n\t\t\t\t\t\t\t\t\t\tonkeydown: (e: KeyboardEvent) => {\n\t\t\t\t\t\t\t\t\t\t\tif (isKeyPressed(e.keyCode, Keys.SPACE, Keys.RETURN)) {\n\t\t\t\t\t\t\t\t\t\t\t\tthis.filesExpanded = !this.filesExpanded\n\t\t\t\t\t\t\t\t\t\t\t\te.preventDefault()\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},\n\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\tm(\n\t\t\t\t\t\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\t\t\t\t\tlang.get(\"attachmentAmount_label\", { \"{amount}\": attachmentCount + \"\" }) +\n\t\t\t\t\t\t\t\t\t\t\t\t` (${formatStorageSize(totalAttachmentSize)})`,\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\tm(Icon, {\n\t\t\t\t\t\t\t\t\t\t\ticon: BootIcons.Expand,\n\t\t\t\t\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\t\t\t\t\tfill: theme.content_fg,\n\t\t\t\t\t\t\t\t\t\t\t\ttransform: this.filesExpanded ? \"rotate(180deg)\" : \"\",\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],\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t  ],\n\t\t\t\t]),\n\n\t\t\t\t// if we have more than one attachment, list them here in this expander panel\n\t\t\t\tattachments.length > 1\n\t\t\t\t\t? m(\n\t\t\t\t\t\t\tExpanderPanel,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\texpanded: this.filesExpanded,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tm(\".flex.col.\" + responsiveCardHMargin(), [\n\t\t\t\t\t\t\t\tm(\".flex.flex-wrap.column-gap\", this.renderAttachmentContainer(viewModel, attachments)),\n\t\t\t\t\t\t\t\tisIOSApp()\n\t\t\t\t\t\t\t\t\t? null\n\t\t\t\t\t\t\t\t\t: m(\n\t\t\t\t\t\t\t\t\t\t\t\".flex\",\n\t\t\t\t\t\t\t\t\t\t\tm(Button, {\n\t\t\t\t\t\t\t\t\t\t\t\tlabel: \"saveAll_action\",\n\t\t\t\t\t\t\t\t\t\t\t\ttype: ButtonType.Secondary,\n\t\t\t\t\t\t\t\t\t\t\t\tclick: () => viewModel.downloadAll(),\n\t\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]),\n\t\t\t\t\t  )\n\t\t\t\t\t: null,\n\t\t\t]\n\t\t}\n\t}\n\n\tprivate renderAttachmentContainer(viewModel: MailViewerViewModel, attachments: TutanotaFile[]): Children {\n\t\treturn attachments.map((attachment) =>\n\t\t\tm(AttachmentBubble, {\n\t\t\t\tattachment,\n\t\t\t\tremove: null,\n\t\t\t\tdownload:\n\t\t\t\t\tisAndroidApp() || isDesktop()\n\t\t\t\t\t\t? () => viewModel.downloadAndOpenAttachment(attachment, false)\n\t\t\t\t\t\t: () => viewModel.downloadAndOpenAttachment(attachment, true),\n\t\t\t\topen: isAndroidApp() || isDesktop() ? () => viewModel.downloadAndOpenAttachment(attachment, true) : null,\n\t\t\t}),\n\t\t)\n\t}\n\n\tprivate tutaoBadge(viewModel: MailViewerViewModel): Children {\n\t\treturn isTutanotaTeamMail(viewModel.mail)\n\t\t\t? m(\n\t\t\t\t\tBadge,\n\t\t\t\t\t{\n\t\t\t\t\t\tclasses: \".mr-s\",\n\t\t\t\t\t},\n\t\t\t\t\t\"Tutanota Team\",\n\t\t\t  )\n\t\t\t: null\n\t}\n\n\tprivate renderPhishingWarning(viewModel: MailViewerViewModel): Children | null {\n\t\tif (viewModel.isMailSuspicious()) {\n\t\t\treturn m(InfoBanner, {\n\t\t\t\tmessage: \"phishingMessageBody_msg\",\n\t\t\t\ticon: Icons.Warning,\n\t\t\t\ttype: BannerType.Warning,\n\t\t\t\thelpLink: canSeeTutanotaLinks(viewModel.logins) ? InfoLink.Phishing : null,\n\t\t\t\tbuttons: [\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: \"markAsNotPhishing_action\",\n\t\t\t\t\t\tclick: () => viewModel.markAsNotPhishing().then(() => m.redraw()),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t})\n\t\t}\n\t}\n\n\tprivate renderHardAuthenticationFailWarning(viewModel: MailViewerViewModel): Children | null {\n\t\tif (!viewModel.isWarningDismissed() && viewModel.mail.authStatus === MailAuthenticationStatus.HARD_FAIL) {\n\t\t\treturn m(InfoBanner, {\n\t\t\t\tmessage: \"mailAuthFailed_msg\",\n\t\t\t\ticon: Icons.Warning,\n\t\t\t\thelpLink: canSeeTutanotaLinks(viewModel.logins) ? InfoLink.MailAuth : null,\n\t\t\t\ttype: BannerType.Warning,\n\t\t\t\tbuttons: [\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: \"close_alt\",\n\t\t\t\t\t\tclick: () => viewModel.setWarningDismissed(true),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t})\n\t\t}\n\t}\n\n\tprivate renderSoftAuthenticationFailWarning(viewModel: MailViewerViewModel): Children | null {\n\t\tif (!viewModel.isWarningDismissed() && viewModel.mail.authStatus === MailAuthenticationStatus.SOFT_FAIL) {\n\t\t\treturn m(InfoBanner, {\n\t\t\t\tmessage: () =>\n\t\t\t\t\tviewModel.mail.differentEnvelopeSender\n\t\t\t\t\t\t? lang.get(\"mailAuthMissingWithTechnicalSender_msg\", {\n\t\t\t\t\t\t\t\t\"{sender}\": viewModel.mail.differentEnvelopeSender,\n\t\t\t\t\t\t  })\n\t\t\t\t\t\t: lang.get(\"mailAuthMissing_label\"),\n\t\t\t\ticon: Icons.Warning,\n\t\t\t\thelpLink: canSeeTutanotaLinks(viewModel.logins) ? InfoLink.MailAuth : null,\n\t\t\t\tbuttons: [\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: \"close_alt\",\n\t\t\t\t\t\tclick: () => viewModel.setWarningDismissed(true),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t})\n\t\t} else {\n\t\t\treturn null\n\t\t}\n\t}\n\n\tprivate renderExternalContentBanner(attrs: MailViewerHeaderAttrs): Children | null {\n\t\t// only show banner when there are blocked images and the user hasn't made a decision about how to handle them\n\t\tif (attrs.viewModel.getContentBlockingStatus() !== ContentBlockingStatus.Block) {\n\t\t\treturn null\n\t\t}\n\n\t\tconst showButton: ButtonAttrs = {\n\t\t\tlabel: \"showBlockedContent_action\",\n\t\t\tclick: () => attrs.viewModel.setContentBlockingStatus(ContentBlockingStatus.Show),\n\t\t}\n\t\tconst alwaysOrNeverAllowButtons = attrs.viewModel.canPersistBlockingStatus()\n\t\t\t? [\n\t\t\t\t\tattrs.viewModel.isMailAuthenticated()\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tlabel: \"allowExternalContentSender_action\" as const,\n\t\t\t\t\t\t\t\tclick: () => attrs.viewModel.setContentBlockingStatus(ContentBlockingStatus.AlwaysShow),\n\t\t\t\t\t\t  }\n\t\t\t\t\t\t: null,\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: \"blockExternalContentSender_action\" as const,\n\t\t\t\t\t\tclick: () => attrs.viewModel.setContentBlockingStatus(ContentBlockingStatus.AlwaysBlock),\n\t\t\t\t\t},\n\t\t\t  ].filter(isNotNull)\n\t\t\t: []\n\t\t// on narrow screens the buttons will end up on 2 lines if there are too many, this looks bad.\n\t\tconst maybeDropdownButtons =\n\t\t\tstyles.isSingleColumnLayout() && alwaysOrNeverAllowButtons.length > 1\n\t\t\t\t? [createMoreSecondaryButtonAttrs(alwaysOrNeverAllowButtons, 216)]\n\t\t\t\t: alwaysOrNeverAllowButtons\n\t\treturn m(InfoBanner, {\n\t\t\tmessage: \"contentBlocked_msg\",\n\t\t\ticon: Icons.Picture,\n\t\t\thelpLink: canSeeTutanotaLinks(attrs.viewModel.logins) ? InfoLink.LoadImages : null,\n\t\t\tbuttons: [showButton, ...maybeDropdownButtons],\n\t\t})\n\t}\n\n\tprivate moreButton(attrs: MailViewerHeaderAttrs): Children {\n\t\treturn m(IconButton, {\n\t\t\ttitle: \"more_label\",\n\t\t\ticon: Icons.More,\n\t\t\tclick: this.prepareMoreActions(attrs),\n\t\t})\n\t}\n\n\tprivate prepareMoreActions({ viewModel }: MailViewerHeaderAttrs) {\n\t\treturn createDropdown({\n\t\t\tlazyButtons: () => {\n\t\t\t\tlet actionButtons: DropdownButtonAttrs[] = []\n\t\t\t\tif (viewModel.isDraftMail()) {\n\t\t\t\t\tactionButtons.push({\n\t\t\t\t\t\tlabel: \"edit_action\",\n\t\t\t\t\t\tclick: () => editDraft(viewModel),\n\t\t\t\t\t\ticon: Icons.Edit,\n\t\t\t\t\t})\n\t\t\t\t\tactionButtons.push({\n\t\t\t\t\t\tlabel: \"move_action\",\n\t\t\t\t\t\tclick: (_: MouseEvent, dom: HTMLElement) => showMoveMailsDropdown(viewModel.mailModel, dom.getBoundingClientRect(), [viewModel.mail]),\n\t\t\t\t\t\ticon: Icons.Folder,\n\t\t\t\t\t})\n\t\t\t\t\tactionButtons.push({\n\t\t\t\t\t\tlabel: \"delete_action\",\n\t\t\t\t\t\tclick: () => promptAndDeleteMails(viewModel.mailModel, [viewModel.mail], noOp),\n\t\t\t\t\t\ticon: Icons.Trash,\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tif (viewModel.canForwardOrMove()) {\n\t\t\t\t\t\tactionButtons.push({\n\t\t\t\t\t\t\tlabel: \"reply_action\",\n\t\t\t\t\t\t\tclick: () => viewModel.reply(false),\n\t\t\t\t\t\t\ticon: Icons.Reply,\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\tif (viewModel.canReplyAll()) {\n\t\t\t\t\t\t\tactionButtons.push({\n\t\t\t\t\t\t\t\tlabel: \"replyAll_action\",\n\t\t\t\t\t\t\t\tclick: () => viewModel.reply(true),\n\t\t\t\t\t\t\t\ticon: Icons.ReplyAll,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tactionButtons.push({\n\t\t\t\t\t\t\tlabel: \"forward_action\",\n\t\t\t\t\t\t\tclick: () => viewModel.forward(),\n\t\t\t\t\t\t\ticon: Icons.Forward,\n\t\t\t\t\t\t})\n\t\t\t\t\t\tactionButtons.push({\n\t\t\t\t\t\t\tlabel: \"move_action\",\n\t\t\t\t\t\t\tclick: (_: MouseEvent, dom: HTMLElement) =>\n\t\t\t\t\t\t\t\tshowMoveMailsDropdown(viewModel.mailModel, dom.getBoundingClientRect(), [viewModel.mail]),\n\t\t\t\t\t\t\ticon: Icons.Folder,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\n\t\t\t\t\tactionButtons.push({\n\t\t\t\t\t\tlabel: \"delete_action\",\n\t\t\t\t\t\tclick: () => promptAndDeleteMails(viewModel.mailModel, [viewModel.mail], noOp),\n\t\t\t\t\t\ticon: Icons.Trash,\n\t\t\t\t\t})\n\n\t\t\t\t\tactionButtons.push(...mailViewerMoreActions(viewModel))\n\t\t\t\t}\n\n\t\t\t\treturn actionButtons\n\t\t\t},\n\t\t\twidth: 300,\n\t\t})\n\t}\n\n\tgetRecipientEmailAddress({ viewModel }: MailViewerHeaderAttrs) {\n\t\tconst relevantRecipient = viewModel.getRelevantRecipient()\n\n\t\tif (relevantRecipient) {\n\t\t\tconst numberOfAllRecipients = viewModel.getNumberOfRecipients()\n\t\t\treturn m(\n\t\t\t\t\".flex.click.small.ml-between-s.items-center\",\n\t\t\t\t{\n\t\t\t\t\tstyle: {\n\t\t\t\t\t\t// use this to allow the container to shrink, otherwise it doesn't want to cut the recipient address\n\t\t\t\t\t\tminWidth: \"20px\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t[\n\t\t\t\t\tm(\"\", lang.get(\"mailViewerRecipients_label\")),\n\t\t\t\t\tm(\".text-ellipsis\", relevantRecipient.address),\n\t\t\t\t\tm(\".flex.no-wrap\", [\n\t\t\t\t\t\tnumberOfAllRecipients > 1 ? `+ ${numberOfAllRecipients - 1}` : null,\n\t\t\t\t\t\tm(Icon, {\n\t\t\t\t\t\t\ticon: BootIcons.Expand,\n\t\t\t\t\t\t\tcontainer: \"div\",\n\t\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t\tfill: theme.content_fg,\n\t\t\t\t\t\t\t\ttransform: this.detailsExpanded ? \"rotate(180deg)\" : \"\",\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} else {\n\t\t\treturn \"\"\n\t\t}\n\t}\n}\n","import { px, size } from \"../../gui/size\"\nimport m, { Children, Component, Vnode } from \"mithril\"\nimport stream from \"mithril/stream\"\nimport { windowFacade, windowSizeListener } from \"../../misc/WindowFacade\"\nimport { FeatureType, InboxRuleType, Keys, MailFolderType, SpamRuleFieldType, SpamRuleType } from \"../../api/common/TutanotaConstants\"\nimport type { Mail } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { lang } from \"../../misc/LanguageViewModel\"\nimport { assertMainOrNode } from \"../../api/common/Env\"\nimport { assertNonNull, assertNotNull, defer, DeferredObject, neverNull, noOp, ofClass } from \"@tutao/tutanota-utils\"\nimport { createNewContact, getExistingRuleForType, isTutanotaTeamMail } from \"../model/MailUtils\"\nimport { IconMessageBox } from \"../../gui/base/ColumnEmptyMessageBox\"\nimport type { Shortcut } from \"../../misc/KeyManager\"\nimport { keyManager } from \"../../misc/KeyManager\"\nimport { Icon, progressIcon } from \"../../gui/base/Icon\"\nimport { Icons } from \"../../gui/base/icons/Icons\"\nimport { theme } from \"../../gui/theme\"\nimport { client } from \"../../misc/ClientDetector\"\nimport { styles } from \"../../gui/styles\"\nimport { DropdownButtonAttrs, showDropdownAtPosition } from \"../../gui/base/Dropdown.js\"\nimport type { InlineImageReference } from \"./MailGuiUtils\"\nimport { replaceCidsWithInlineImages } from \"./MailGuiUtils\"\nimport { getCoordsOfMouseOrTouchEvent } from \"../../gui/base/GuiUtils\"\nimport { copyToClipboard } from \"../../misc/ClipboardUtils\"\nimport { ContentBlockingStatus, MailViewerViewModel } from \"./MailViewerViewModel\"\nimport { getListId } from \"../../api/common/utils/EntityUtils\"\nimport { createEmailSenderListElement } from \"../../api/entities/sys/TypeRefs.js\"\nimport { UserError } from \"../../api/main/UserError\"\nimport { showUserError } from \"../../misc/ErrorHandlerImpl\"\nimport { isNewMailActionAvailable } from \"../../gui/nav/NavFunctions\"\nimport { CancelledError } from \"../../api/common/error/CancelledError\"\nimport { MailViewerHeader } from \"./MailViewerHeader.js\"\nimport { editDraft, showHeaderDialog } from \"./MailViewerUtils.js\"\nimport { ToggleButton } from \"../../gui/base/buttons/ToggleButton.js\"\nimport { locator } from \"../../api/main/MainLocator.js\"\nimport { responsiveCardHMargin, responsiveCardHPadding } from \"../../gui/cards.js\"\n\nassertMainOrNode()\n// map of inline image cid to InlineImageReference\nexport type InlineImages = Map<string, InlineImageReference>\n\nconst DOUBLE_TAP_TIME_MS = 350\n\ntype MailAddressAndName = {\n\tname: string\n\taddress: string\n}\n\nexport type MailViewerAttrs = {\n\tviewModel: MailViewerViewModel\n\tisPrimary: boolean\n\t/**\n\t * Mail body might contain blockquotes that we want to collapse in some cases (e.g. the thread is visible in conversation anyway) or expand in other\n\t * cases (e.g. if it's a single/the first email in the conversation).\n\t *\n\t */\n\tdefaultQuoteBehavior: \"collapse\" | \"expand\"\n}\n\n/**\n * The MailViewer displays a mail. The mail body is loaded asynchronously.\n *\n * The viewer has a longer lifecycle than viewModel so we need to be careful about the state.\n */\nexport class MailViewer implements Component<MailViewerAttrs> {\n\t/** it is set after we measured mail body element */\n\tprivate bodyLineHeight: number | null = null\n\n\tprivate isScaling = true\n\n\tprivate lastBodyTouchEndTime = 0\n\tprivate lastTouchStart = {\n\t\tx: 0,\n\t\ty: 0,\n\t\ttime: Date.now(),\n\t}\n\n\t/**\n\t * Delay the display of the progress spinner in main body view for a short time to suppress it when we are switching between cached emails\n\t * and we are just sanitizing\n\t */\n\tprivate delayProgressSpinner = true\n\n\tprivate readonly resizeListener: windowSizeListener\n\n\tprivate viewModel!: MailViewerViewModel\n\n\tprivate readonly shortcuts: Array<Shortcut>\n\n\tprivate scrollDom: HTMLElement | null = null\n\n\tprivate domBodyDeferred: DeferredObject<HTMLElement> = defer()\n\tprivate domBody: HTMLElement | null = null\n\n\tprivate shadowDomRoot: ShadowRoot | null = null\n\tprivate currentlyRenderedMailBody: DocumentFragment | null = null\n\tprivate lastContentBlockingStatus: ContentBlockingStatus | null = null\n\n\tprivate loadAllListener = stream()\n\t/** for block quotes in mail bodies, whether to display quote before user interaction\n\t * is \"none\" until we render once */\n\tprivate currentQuoteBehavior: \"none\" | \"collapse\" | \"expand\" = \"none\"\n\t/** for block quotes in mail bodies, whether to display placeholder or original quote */\n\tprivate quoteState: \"noquotes\" | \"unset\" | \"collapsed\" | \"expanded\" = \"unset\"\n\n\tconstructor(vnode: Vnode<MailViewerAttrs>) {\n\t\tthis.setViewModel(vnode.attrs.viewModel, vnode.attrs.isPrimary)\n\n\t\tthis.resizeListener = () => this.domBodyDeferred.promise.then((dom) => this.updateLineHeight(dom))\n\n\t\tthis.shortcuts = this.setupShortcuts(vnode.attrs)\n\t}\n\n\toncreate({ attrs }: Vnode<MailViewerAttrs>) {\n\t\tif (attrs.isPrimary) {\n\t\t\tkeyManager.registerShortcuts(this.shortcuts)\n\t\t}\n\t\twindowFacade.addResizeListener(this.resizeListener)\n\t}\n\n\tonremove({ attrs }: Vnode<MailViewerAttrs>) {\n\t\twindowFacade.removeResizeListener(this.resizeListener)\n\t\tthis.clearDomBody()\n\t\tif (attrs.isPrimary) {\n\t\t\tkeyManager.unregisterShortcuts(this.shortcuts)\n\t\t}\n\t}\n\n\tprivate setViewModel(viewModel: MailViewerViewModel, isPrimary: boolean) {\n\t\t// Figuring out whether we have a new email assigned.\n\t\tconst oldViewModel = this.viewModel\n\t\tthis.viewModel = viewModel\n\t\tif (this.viewModel !== oldViewModel) {\n\t\t\tthis.loadAllListener.end(true)\n\t\t\tthis.loadAllListener = this.viewModel.loadCompleteNotification.map(async () => {\n\t\t\t\t// streams are pretty much synchronous, so we could be in the middle of a redraw here and mithril does not just schedule another redraw, it\n\t\t\t\t// will error out so before calling m.redraw.sync() we want to make sure that we are not inside a redraw by just scheduling a microtask with\n\t\t\t\t// this simple await.\n\t\t\t\tawait Promise.resolve()\n\t\t\t\t// Wait for mail body to be redrawn before replacing images\n\t\t\t\tm.redraw.sync()\n\t\t\t\tawait this.replaceInlineImages()\n\t\t\t\tm.redraw()\n\t\t\t})\n\n\t\t\t// Reset scaling status if it's a new email.\n\t\t\tthis.isScaling = true\n\t\t\tthis.lastContentBlockingStatus = null\n\t\t\tthis.delayProgressSpinner = true\n\t\t\tsetTimeout(() => {\n\t\t\t\tthis.delayProgressSpinner = false\n\t\t\t\tm.redraw()\n\t\t\t}, 50)\n\t\t}\n\t}\n\n\tview(vnode: Vnode<MailViewerAttrs>): Children {\n\t\tthis.handleContentBlockingOnRender()\n\t\treturn [\n\t\t\tm(\".mail-viewer.overflow-x-hidden\", [\n\t\t\t\tthis.renderMailHeader(vnode.attrs),\n\t\t\t\tthis.renderMailSubject(vnode.attrs),\n\t\t\t\tm(\n\t\t\t\t\t\".flex-grow.scroll-x.pt.pb.border-radius-big\" + (this.viewModel.isContrastFixNeeded() ? \".bg-white.content-black\" : \" \"),\n\t\t\t\t\t{\n\t\t\t\t\t\tclass: responsiveCardHPadding(),\n\t\t\t\t\t\toncreate: (vnode) => {\n\t\t\t\t\t\t\tthis.scrollDom = vnode.dom as HTMLElement\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tthis.renderMailBodySection(vnode.attrs),\n\t\t\t\t),\n\t\t\t\tthis.renderQuoteExpanderButton(),\n\t\t\t]),\n\t\t]\n\t}\n\n\tprivate renderMailSubject(attrs: MailViewerAttrs) {\n\t\treturn m(\"h4.font-weight-600.mt.mb.text-break.selectable.\" + responsiveCardHMargin(), attrs.viewModel.getSubject())\n\t}\n\n\t/**\n\t * important: must be called after rendering the mail body part so that {@link quoteState} is set correctly.\n\t * The logic here relies on the fact that lifecycle methods will be called after body section lifecycle methods.\n\t */\n\tprivate renderQuoteExpanderButton() {\n\t\tconst buttonHeight = 24\n\t\treturn m(\n\t\t\t\".abs.flex.justify-center.full-width\",\n\t\t\t{\n\t\t\t\tstyle: {\n\t\t\t\t\t// +1 for the border\n\t\t\t\t\tbottom: px(-(buttonHeight / 2 + 1)),\n\t\t\t\t\tdisplay: \"hidden\",\n\t\t\t\t},\n\t\t\t\toncreate: ({ dom }) => {\n\t\t\t\t\t;(dom as HTMLElement).style.display = this.quoteState === \"noquotes\" ? \"none\" : \"\"\n\t\t\t\t},\n\t\t\t\tonupdate: ({ dom }) => {\n\t\t\t\t\t;(dom as HTMLElement).style.display = this.quoteState === \"noquotes\" ? \"none\" : \"\"\n\t\t\t\t},\n\t\t\t},\n\t\t\tm(\n\t\t\t\t// needs flex for correct height\n\t\t\t\t\".flex\",\n\t\t\t\t{\n\t\t\t\t\tstyle: {\n\t\t\t\t\t\tborderRadius: \"25%\",\n\t\t\t\t\t\tborder: `1px solid ${theme.list_border}`,\n\t\t\t\t\t\tbackgroundColor: theme.content_bg,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tm(ToggleButton, {\n\t\t\t\t\ticon: Icons.More,\n\t\t\t\t\ttitle: \"showText_action\",\n\t\t\t\t\ttoggledTitle: \"hideText_action\",\n\t\t\t\t\ttoggled: this.shouldDisplayCollapsedQuotes(),\n\t\t\t\t\tonToggled: () => {\n\t\t\t\t\t\tthis.quoteState = this.shouldDisplayCollapsedQuotes() ? \"collapsed\" : \"expanded\"\n\t\t\t\t\t\tthis.shadowDomRoot && this.updateCollapsedQuotes(this.shadowDomRoot, this.shouldDisplayCollapsedQuotes())\n\t\t\t\t\t},\n\t\t\t\t\tstyle: {\n\t\t\t\t\t\theight: \"24px\",\n\t\t\t\t\t\twidth: px(size.button_height_compact),\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t),\n\t\t)\n\t}\n\n\tprivate handleContentBlockingOnRender() {\n\t\tif (this.lastContentBlockingStatus != null && this.viewModel.getContentBlockingStatus() != this.lastContentBlockingStatus) {\n\t\t\tPromise.resolve().then(async () => {\n\t\t\t\t// Wait for new mail body to be rendered before replacing images. Probably not necessary anymore as we already schedule it after the render\n\t\t\t\t// but better be safe.\n\t\t\t\tm.redraw.sync()\n\t\t\t\tawait this.replaceInlineImages()\n\t\t\t})\n\t\t}\n\t\tthis.lastContentBlockingStatus = this.viewModel.getContentBlockingStatus()\n\t}\n\n\tprivate renderMailHeader(attrs: MailViewerAttrs) {\n\t\treturn m(MailViewerHeader, {\n\t\t\tviewModel: this.viewModel,\n\t\t\tcreateMailAddressContextButtons: this.createMailAddressContextButtons.bind(this),\n\t\t\tisPrimary: attrs.isPrimary,\n\t\t})\n\t}\n\n\tonbeforeupdate(vnode: Vnode<MailViewerAttrs>): boolean | void {\n\t\t// Setting viewModel here to have viewModel that we will use for render already and be able to make a decision\n\t\t// about skipping rendering\n\t\tthis.setViewModel(vnode.attrs.viewModel, vnode.attrs.isPrimary)\n\t\t// We skip rendering progress indicator when switching between emails.\n\t\t// However if we already loaded the mail then we can just render it.\n\t\tconst shouldSkipRender = this.viewModel.isLoading() && this.delayProgressSpinner\n\t\treturn !shouldSkipRender\n\t}\n\n\tprivate renderMailBodySection(attrs: MailViewerAttrs): Children {\n\t\tif (this.viewModel.didErrorsOccur()) {\n\t\t\treturn m(IconMessageBox, {\n\t\t\t\tmessage: \"corrupted_msg\",\n\t\t\t\ticon: Icons.Warning,\n\t\t\t\tcolor: theme.content_message_bg,\n\t\t\t})\n\t\t}\n\n\t\tconst sanitizedMailBody = this.viewModel.getSanitizedMailBody()\n\n\t\t// Do not render progress spinner or mail body while we are animating.\n\t\tif (this.viewModel.shouldDelayRendering()) {\n\t\t\treturn null\n\t\t} else if (sanitizedMailBody != null) {\n\t\t\treturn this.renderMailBody(sanitizedMailBody, attrs)\n\t\t} else if (this.viewModel.isLoading()) {\n\t\t\treturn this.renderLoadingIcon()\n\t\t} else {\n\t\t\t// The body failed to load, just show blank body because there is a banner\n\t\t\treturn null\n\t\t}\n\t}\n\n\tprivate renderMailBody(sanitizedMailBody: DocumentFragment, attrs: MailViewerAttrs): Children {\n\t\treturn m(\"#mail-body\", {\n\t\t\t// key to avoid mithril reusing the dom element when it should switch the rendering the loading spinner\n\t\t\tkey: \"mailBody\",\n\t\t\toncreate: (vnode) => {\n\t\t\t\tconst dom = vnode.dom as HTMLElement\n\t\t\t\tthis.setDomBody(dom)\n\t\t\t\tthis.updateLineHeight(dom)\n\t\t\t\tthis.rescale(false)\n\t\t\t\tthis.renderShadowMailBody(sanitizedMailBody, attrs)\n\t\t\t},\n\t\t\tonupdate: (vnode) => {\n\t\t\t\tconst dom = vnode.dom as HTMLElement\n\t\t\t\tthis.setDomBody(dom)\n\n\t\t\t\t// Only measure and update line height once.\n\t\t\t\t// BUT we need to do in from onupdate too if we swap mailViewer but mithril does not realize\n\t\t\t\t// that it's a different vnode so oncreate might not be called.\n\t\t\t\tif (!this.bodyLineHeight) {\n\t\t\t\t\tthis.updateLineHeight(vnode.dom as HTMLElement)\n\t\t\t\t}\n\n\t\t\t\tthis.rescale(false)\n\n\t\t\t\tif (this.currentlyRenderedMailBody !== sanitizedMailBody) this.renderShadowMailBody(sanitizedMailBody, attrs)\n\t\t\t\t// If the quote behavior changes (e.g. after loading is finished) we should update the quotes.\n\t\t\t\t// If we already rendered it correctly it will already be set in renderShadowMailBody() so we will avoid doing it twice.\n\t\t\t\tif (this.currentQuoteBehavior !== attrs.defaultQuoteBehavior) {\n\t\t\t\t\tthis.updateCollapsedQuotes(assertNotNull(this.shadowDomRoot), attrs.defaultQuoteBehavior === \"expand\")\n\t\t\t\t}\n\t\t\t\tthis.currentQuoteBehavior = attrs.defaultQuoteBehavior\n\t\t\t},\n\t\t\tonbeforeremove: () => {\n\t\t\t\t// Clear dom body in case there will be a new one, we want promise to be up-to-date\n\t\t\t\tthis.clearDomBody()\n\t\t\t},\n\t\t\tonsubmit: (event: Event) => {\n\t\t\t\t// use the default confirm dialog here because the submit can not be done async\n\t\t\t\tif (!confirm(lang.get(\"reallySubmitContent_msg\"))) {\n\t\t\t\t\tevent.preventDefault()\n\t\t\t\t}\n\t\t\t},\n\t\t\tstyle: {\n\t\t\t\t\"line-height\": this.bodyLineHeight ? this.bodyLineHeight.toString() : size.line_height,\n\t\t\t\t\"transform-origin\": \"top left\",\n\t\t\t},\n\t\t})\n\t}\n\n\tprivate updateCollapsedQuotes(dom: ParentNode, showQuote: boolean) {\n\t\tconst quotes: NodeListOf<HTMLElement> = dom.querySelectorAll(\"[tuta-collapsed-quote]\")\n\t\tfor (const quoteWrap of Array.from(quotes)) {\n\t\t\tconst quote = quoteWrap.children[0] as HTMLElement\n\t\t\tquote.style.display = showQuote ? \"\" : \"none\"\n\t\t\tconst quoteIndicator = quoteWrap.children[1] as HTMLElement\n\t\t\tquoteIndicator.style.display = showQuote ? \"none\" : \"\"\n\t\t}\n\t}\n\n\tprivate shouldDisplayCollapsedQuotes(): boolean {\n\t\t// if the user didn't do anything yet take the behavior passed from the outside, otherwise whatever user has selected\n\t\treturn this.quoteState === \"unset\" ? this.currentQuoteBehavior === \"expand\" : this.quoteState === \"expanded\"\n\t}\n\n\t/**\n\t * manually wrap and style a mail body to display correctly inside a shadow root\n\t * @param sanitizedMailBody the mail body to display\n\t * @private\n\t */\n\tprivate renderShadowMailBody(sanitizedMailBody: DocumentFragment, attrs: MailViewerAttrs) {\n\t\tthis.currentQuoteBehavior = attrs.defaultQuoteBehavior\n\t\tassertNonNull(this.shadowDomRoot, \"shadow dom root is null!\")\n\t\twhile (this.shadowDomRoot.firstChild) {\n\t\t\tthis.shadowDomRoot.firstChild.remove()\n\t\t}\n\t\tconst wrapNode = document.createElement(\"div\")\n\t\twrapNode.className = \"selectable touch-callout break-word-links\" + (client.isMobileDevice() ? \" break-pre\" : \"\")\n\t\twrapNode.style.lineHeight = String(this.bodyLineHeight ? this.bodyLineHeight.toString() : size.line_height)\n\t\twrapNode.style.transformOrigin = \"top left\"\n\t\twrapNode.appendChild(sanitizedMailBody.cloneNode(true))\n\n\t\t// query all top level block quotes\n\t\tconst quoteElements = Array.from(wrapNode.querySelectorAll(\"blockquote:not(blockquote blockquote)\")) as HTMLElement[]\n\t\tif (quoteElements.length === 0) {\n\t\t\tthis.quoteState = \"noquotes\"\n\t\t}\n\t\tfor (const quote of quoteElements) {\n\t\t\tthis.createCollapsedBlockQuote(quote, this.shouldDisplayCollapsedQuotes())\n\t\t}\n\n\t\tif (client.isMobileDevice()) {\n\t\t\twrapNode.addEventListener(\"touchstart\", (event) => {\n\t\t\t\tconst touch = event.touches[0]\n\t\t\t\tthis.lastTouchStart.x = touch.clientX\n\t\t\t\tthis.lastTouchStart.y = touch.clientY\n\t\t\t\tthis.lastTouchStart.time = Date.now()\n\t\t\t})\n\t\t\twrapNode.addEventListener(\"touchend\", (event) => {\n\t\t\t\tconst href = (event.target as Element | null)?.closest(\"a\")?.getAttribute(\"href\") ?? null\n\t\t\t\tthis.handleDoubleTap(\n\t\t\t\t\tevent,\n\t\t\t\t\t(e) => this.handleAnchorClick(e, href, true),\n\t\t\t\t\t() => this.rescale(true),\n\t\t\t\t)\n\t\t\t})\n\t\t} else {\n\t\t\twrapNode.addEventListener(\"click\", (event) => {\n\t\t\t\tconst href = (event.target as Element | null)?.closest(\"a\")?.getAttribute(\"href\") ?? null\n\t\t\t\tthis.handleAnchorClick(event, href, false)\n\t\t\t})\n\t\t}\n\t\tthis.shadowDomRoot.appendChild(styles.getStyleSheetElement(\"main\"))\n\t\tthis.shadowDomRoot.appendChild(wrapNode)\n\t\tthis.currentlyRenderedMailBody = sanitizedMailBody\n\t}\n\n\tprivate createCollapsedBlockQuote(quote: HTMLElement, expanded: boolean) {\n\t\tconst quoteWrap = document.createElement(\"div\")\n\t\t// used to query quotes later\n\t\tquoteWrap.setAttribute(\"tuta-collapsed-quote\", \"true\")\n\n\t\tquote.replaceWith(quoteWrap)\n\t\tquote.style.display = expanded ? \"\" : \"none\"\n\n\t\tconst quoteIndicator = document.createElement(\"div\")\n\t\tquoteIndicator.classList.add(\"flex\")\n\t\tquoteIndicator.style.borderLeft = `2px solid ${theme.content_border}`\n\t\tquoteIndicator.style.display = expanded ? \"none\" : \"\"\n\n\t\tm.render(\n\t\t\tquoteIndicator,\n\t\t\tm(Icon, {\n\t\t\t\ticon: Icons.More,\n\t\t\t\tclass: \"icon-xl mlr\",\n\t\t\t\tcontainer: \"div\",\n\t\t\t\tstyle: {\n\t\t\t\t\tfill: theme.navigation_menu_icon,\n\t\t\t\t},\n\t\t\t}),\n\t\t)\n\n\t\tquoteWrap.appendChild(quote)\n\t\tquoteWrap.appendChild(quoteIndicator)\n\t}\n\n\tprivate clearDomBody() {\n\t\tthis.domBodyDeferred = defer()\n\t\tthis.domBody = null\n\t\tthis.shadowDomRoot = null\n\t}\n\n\tprivate setDomBody(dom: HTMLElement) {\n\t\tif (dom !== this.domBody || this.shadowDomRoot == null) {\n\t\t\t// If the dom element hasn't been created anew in onupdate\n\t\t\t// then trying to create a new shadow root on the same node will cause an error\n\t\t\tthis.shadowDomRoot = dom.attachShadow({ mode: \"open\" })\n\n\t\t\t// Allow forms inside of mail bodies to be filled out without resulting in keystrokes being interpreted as shortcuts\n\t\t\tthis.shadowDomRoot.getRootNode().addEventListener(\"keydown\", (event: Event) => {\n\t\t\t\tconst { target } = event\n\t\t\t\tif (this.eventTargetWithKeyboardInput(target)) {\n\t\t\t\t\tevent.stopPropagation()\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\n\t\tthis.domBodyDeferred.resolve(dom)\n\t\tthis.domBody = dom\n\t}\n\n\tprivate renderLoadingIcon(): Children {\n\t\treturn m(\n\t\t\t\".progress-panel.flex-v-center.items-center\",\n\t\t\t{\n\t\t\t\tkey: \"loadingIcon\",\n\t\t\t\tstyle: {\n\t\t\t\t\theight: \"200px\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t[progressIcon(), m(\"small\", lang.get(\"loading_msg\"))],\n\t\t)\n\t}\n\n\tasync replaceInlineImages() {\n\t\tconst loadedInlineImages = await this.viewModel.getLoadedInlineImages()\n\t\tconst domBody = await this.domBodyDeferred.promise\n\t\treplaceCidsWithInlineImages(domBody, loadedInlineImages, (cid, event) => {\n\t\t\tconst inlineAttachment = this.viewModel.getAttachments().find((attachment) => attachment.cid === cid)\n\n\t\t\tif (inlineAttachment) {\n\t\t\t\tconst coords = getCoordsOfMouseOrTouchEvent(event)\n\t\t\t\tshowDropdownAtPosition(\n\t\t\t\t\t[\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlabel: \"download_action\",\n\t\t\t\t\t\t\tclick: () => this.viewModel.downloadAndOpenAttachment(inlineAttachment, false),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlabel: \"open_action\",\n\t\t\t\t\t\t\tclick: () => this.viewModel.downloadAndOpenAttachment(inlineAttachment, true),\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tcoords.x,\n\t\t\t\t\tcoords.y,\n\t\t\t\t)\n\t\t\t}\n\t\t})\n\t}\n\n\tprivate rescale(animate: boolean) {\n\t\tconst child = this.domBody\n\t\tif (!client.isMobileDevice() || !child) {\n\t\t\treturn\n\t\t}\n\t\tconst containerWidth = child.offsetWidth\n\n\t\tif (!this.isScaling || containerWidth > child.scrollWidth) {\n\t\t\tchild.style.transform = \"\"\n\t\t\tchild.style.marginBottom = \"\"\n\t\t} else {\n\t\t\tconst width = child.scrollWidth\n\t\t\tconst scale = containerWidth / width\n\t\t\tconst heightDiff = child.scrollHeight - child.scrollHeight * scale\n\t\t\tchild.style.transform = `scale(${scale})`\n\t\t\tchild.style.marginBottom = `${-heightDiff}px`\n\t\t}\n\n\t\tchild.style.transition = animate ? \"transform 200ms ease-in-out\" : \"\"\n\t\t// ios 15 bug: transformOrigin magically disappears so we ensure that it's always set\n\t\tchild.style.transformOrigin = \"top left\"\n\t}\n\n\tprivate setupShortcuts(attrs: MailViewerAttrs): Array<Shortcut> {\n\t\tconst userController = locator.logins.getUserController()\n\t\tconst shortcuts: Shortcut[] = [\n\t\t\t{\n\t\t\t\tkey: Keys.E,\n\t\t\t\tenabled: () => this.viewModel.isDraftMail(),\n\t\t\t\texec: () => {\n\t\t\t\t\teditDraft(this.viewModel)\n\t\t\t\t},\n\t\t\t\thelp: \"editMail_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.H,\n\t\t\t\tenabled: () => !this.viewModel.isDraftMail(),\n\t\t\t\texec: () => {\n\t\t\t\t\tshowHeaderDialog(this.viewModel.getHeaders())\n\t\t\t\t},\n\t\t\t\thelp: \"showHeaders_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.R,\n\t\t\t\texec: () => {\n\t\t\t\t\tthis.viewModel.reply(false)\n\t\t\t\t},\n\t\t\t\tenabled: () => !this.viewModel.isDraftMail(),\n\t\t\t\thelp: \"reply_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.R,\n\t\t\t\tshift: true,\n\t\t\t\texec: () => {\n\t\t\t\t\tthis.viewModel.reply(true)\n\t\t\t\t},\n\t\t\t\tenabled: () => !this.viewModel.isDraftMail(),\n\t\t\t\thelp: \"replyAll_action\",\n\t\t\t},\n\t\t]\n\n\t\tif (userController.isInternalUser()) {\n\t\t\tshortcuts.push({\n\t\t\t\tkey: Keys.F,\n\t\t\t\tshift: true,\n\t\t\t\tenabled: () => !this.viewModel.isDraftMail(),\n\t\t\t\texec: () => {\n\t\t\t\t\tthis.viewModel.forward().catch(ofClass(UserError, showUserError))\n\t\t\t\t},\n\t\t\t\thelp: \"forward_action\",\n\t\t\t})\n\t\t}\n\n\t\treturn shortcuts\n\t}\n\n\tprivate updateLineHeight(dom: HTMLElement) {\n\t\tconst width = dom.offsetWidth\n\n\t\tif (width > 900) {\n\t\t\tthis.bodyLineHeight = size.line_height_l\n\t\t} else if (width > 600) {\n\t\t\tthis.bodyLineHeight = size.line_height_m\n\t\t} else {\n\t\t\tthis.bodyLineHeight = size.line_height\n\t\t}\n\n\t\tdom.style.lineHeight = String(this.bodyLineHeight)\n\t}\n\n\tprivate async createMailAddressContextButtons(args: {\n\t\tmailAddress: MailAddressAndName\n\t\tdefaultInboxRuleField: InboxRuleType | null\n\t\tcreateContact?: boolean\n\t}): Promise<Array<DropdownButtonAttrs>> {\n\t\tconst { mailAddress, defaultInboxRuleField, createContact = true } = args\n\n\t\tconst buttons = [] as Array<DropdownButtonAttrs>\n\n\t\tbuttons.push({\n\t\t\tlabel: \"copy_action\",\n\t\t\tclick: () => copyToClipboard(mailAddress.address),\n\t\t})\n\n\t\tif (locator.logins.getUserController().isInternalUser()) {\n\t\t\t//searching for contacts will never resolve if the user has not logged in online\n\t\t\tif (createContact && !locator.logins.isEnabled(FeatureType.DisableContacts) && locator.logins.isFullyLoggedIn()) {\n\t\t\t\tconst contact = await this.viewModel.contactModel.searchForContact(mailAddress.address)\n\t\t\t\tif (contact) {\n\t\t\t\t\tbuttons.push({\n\t\t\t\t\t\tlabel: \"showContact_action\",\n\t\t\t\t\t\tclick: () => {\n\t\t\t\t\t\t\tconst [listId, contactId] = assertNotNull(contact)._id\n\t\t\t\t\t\t\tm.route.set(\"/contact/:listId/:contactId\", { listId, contactId })\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tbuttons.push({\n\t\t\t\t\t\tlabel: \"createContact_action\",\n\t\t\t\t\t\tclick: () => {\n\t\t\t\t\t\t\tthis.viewModel.contactModel.contactListId().then((contactListId) => {\n\t\t\t\t\t\t\t\timport(\"../../contacts/ContactEditor\").then(({ ContactEditor }) => {\n\t\t\t\t\t\t\t\t\tconst contact = createNewContact(locator.logins.getUserController().user, mailAddress.address, mailAddress.name)\n\t\t\t\t\t\t\t\t\tnew ContactEditor(this.viewModel.entityClient, contact, contactListId ?? undefined).show()\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\n\t\t\tif (defaultInboxRuleField && !locator.logins.isEnabled(FeatureType.InternalCommunication)) {\n\t\t\t\tconst rule = getExistingRuleForType(locator.logins.getUserController().props, mailAddress.address.trim().toLowerCase(), defaultInboxRuleField)\n\t\t\t\tbuttons.push({\n\t\t\t\t\tlabel: rule ? \"editInboxRule_action\" : \"addInboxRule_action\",\n\t\t\t\t\tclick: async () => {\n\t\t\t\t\t\tconst mailboxDetails = await this.viewModel.mailModel.getMailboxDetailsForMail(this.viewModel.mail)\n\t\t\t\t\t\tif (mailboxDetails == null) {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst { show, createInboxRuleTemplate } = await import(\"../../settings/AddInboxRuleDialog\")\n\t\t\t\t\t\tconst newRule = rule ?? createInboxRuleTemplate(defaultInboxRuleField, mailAddress.address.trim().toLowerCase())\n\n\t\t\t\t\t\tshow(mailboxDetails, newRule)\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tif (this.viewModel.canCreateSpamRule()) {\n\t\t\t\tbuttons.push({\n\t\t\t\t\tlabel: \"addSpamRule_action\",\n\t\t\t\t\tclick: () => this.addSpamRule(defaultInboxRuleField, mailAddress.address),\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\treturn buttons\n\t}\n\n\tprivate handleDoubleTap(e: TouchEvent, singleClickAction: (e: TouchEvent) => void, doubleClickAction: (e: TouchEvent) => void) {\n\t\tconst lastClick = this.lastBodyTouchEndTime\n\t\tconst now = Date.now()\n\t\tconst touch = e.changedTouches[0]\n\n\t\t// If there are no touches or it's not cancellable event (e.g. scroll) or more than certain time has passed or finger moved too\n\t\t// much then do nothing\n\t\tif (\n\t\t\t!touch ||\n\t\t\t!e.cancelable ||\n\t\t\tDate.now() - this.lastTouchStart.time > DOUBLE_TAP_TIME_MS ||\n\t\t\ttouch.clientX - this.lastTouchStart.x > 40 ||\n\t\t\ttouch.clientY - this.lastTouchStart.y > 40\n\t\t) {\n\t\t\treturn\n\t\t}\n\n\t\te.preventDefault()\n\n\t\tif (now - lastClick < DOUBLE_TAP_TIME_MS) {\n\t\t\tthis.isScaling = !this.isScaling\n\t\t\tthis.lastBodyTouchEndTime = 0\n\t\t\tdoubleClickAction(e)\n\t\t} else {\n\t\t\tsetTimeout(() => {\n\t\t\t\tif (this.lastBodyTouchEndTime === now) {\n\t\t\t\t\tsingleClickAction(e)\n\t\t\t\t}\n\t\t\t}, DOUBLE_TAP_TIME_MS)\n\t\t}\n\n\t\tthis.lastBodyTouchEndTime = now\n\t}\n\n\tprivate addSpamRule(defaultInboxRuleField: InboxRuleType | null, address: string) {\n\t\tconst folder = this.viewModel.mailModel.getMailFolder(getListId(this.viewModel.mail))\n\n\t\tconst spamRuleType = folder && folder.folderType === MailFolderType.SPAM ? SpamRuleType.WHITELIST : SpamRuleType.BLACKLIST\n\n\t\tlet spamRuleField: SpamRuleFieldType\n\t\tswitch (defaultInboxRuleField) {\n\t\t\tcase InboxRuleType.RECIPIENT_TO_EQUALS:\n\t\t\t\tspamRuleField = SpamRuleFieldType.TO\n\t\t\t\tbreak\n\n\t\t\tcase InboxRuleType.RECIPIENT_CC_EQUALS:\n\t\t\t\tspamRuleField = SpamRuleFieldType.CC\n\t\t\t\tbreak\n\n\t\t\tcase InboxRuleType.RECIPIENT_BCC_EQUALS:\n\t\t\t\tspamRuleField = SpamRuleFieldType.BCC\n\t\t\t\tbreak\n\n\t\t\tdefault:\n\t\t\t\tspamRuleField = SpamRuleFieldType.FROM\n\t\t\t\tbreak\n\t\t}\n\n\t\timport(\"../../settings/AddSpamRuleDialog\").then(({ showAddSpamRuleDialog }) => {\n\t\t\tshowAddSpamRuleDialog(\n\t\t\t\tcreateEmailSenderListElement({\n\t\t\t\t\tvalue: address.trim().toLowerCase(),\n\t\t\t\t\ttype: spamRuleType,\n\t\t\t\t\tfield: spamRuleField,\n\t\t\t\t}),\n\t\t\t)\n\t\t})\n\t}\n\n\tprivate handleAnchorClick(event: Event, href: string | null, shouldDispatchSyntheticClick: boolean): void {\n\t\tif (href) {\n\t\t\tif (href.startsWith(\"mailto:\")) {\n\t\t\t\tevent.preventDefault()\n\n\t\t\t\tif (isNewMailActionAvailable()) {\n\t\t\t\t\t// disable new mails for external users.\n\t\t\t\t\timport(\"../editor/MailEditor\").then(({ newMailtoUrlMailEditor }) => {\n\t\t\t\t\t\tnewMailtoUrlMailEditor(href, !locator.logins.getUserController().props.defaultUnconfidential)\n\t\t\t\t\t\t\t.then((editor) => editor.show())\n\t\t\t\t\t\t\t.catch(ofClass(CancelledError, noOp))\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t} else if (isSettingsLink(href, this.viewModel.mail)) {\n\t\t\t\t// Navigate to the settings menu if they are linked within an email.\n\t\t\t\tconst newRoute = href.substring(href.indexOf(\"/settings/\"))\n\t\t\t\tm.route.set(newRoute)\n\t\t\t\tevent.preventDefault()\n\t\t\t} else if (shouldDispatchSyntheticClick) {\n\t\t\t\tconst syntheticTag = document.createElement(\"a\")\n\t\t\t\tsyntheticTag.setAttribute(\"href\", href)\n\t\t\t\tsyntheticTag.setAttribute(\"target\", \"_blank\")\n\t\t\t\tsyntheticTag.setAttribute(\"rel\", \"noopener noreferrer\")\n\t\t\t\tconst newClickEvent = new MouseEvent(\"click\")\n\t\t\t\tsyntheticTag.dispatchEvent(newClickEvent)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * returns true if the passed in target is an HTMLElement that can receive some sort of keyboard input\n\t */\n\tprivate eventTargetWithKeyboardInput(target: EventTarget | null): boolean {\n\t\tif (target && target instanceof HTMLElement) {\n\t\t\treturn target.matches(\n\t\t\t\t'input[type=\"text\"], input[type=\"date\"], input[type=\"datetime-local\"], input[type=\"email\"], input[type=\"month\"], input[type=\"number\"],' +\n\t\t\t\t\t'input[type=\"password\"], input[type=\"search\"], input[type=\"tel\"], input[type=\"time\"], input[type=\"url\"], input[type=\"week\"], input[type=\"datetime\"], textarea',\n\t\t\t)\n\t\t}\n\t\treturn false\n\t}\n}\n\nexport type CreateMailViewerOptions = {\n\tmail: Mail\n\tshowFolder: boolean\n\tdelayBodyRenderingUntil?: Promise<void>\n}\n\n/**\n * support and invoice mails can contain links to the settings page.\n * we don't want normal mails to be able to link places in the app, though.\n * */\nfunction isSettingsLink(href: string, mail: Mail): boolean {\n\treturn (href.startsWith(\"/settings/\") ?? false) && isTutanotaTeamMail(mail)\n}\n","import m, { Children, Component, Vnode } from \"mithril\"\nimport { getFolderIconByType, getMailAddressDisplayText } from \"../model/MailUtils.js\"\nimport { formatDateWithWeekday, formatTime } from \"../../misc/Formatter.js\"\nimport { MailViewerViewModel } from \"./MailViewerViewModel.js\"\nimport { theme } from \"../../gui/theme.js\"\nimport { AllIcons, Icon } from \"../../gui/base/Icon.js\"\nimport { Icons } from \"../../gui/base/icons/Icons.js\"\nimport { responsiveCardHPadding } from \"../../gui/cards.js\"\nimport { Keys, TabIndex } from \"../../api/common/TutanotaConstants.js\"\nimport { isKeyPressed } from \"../../misc/KeyManager.js\"\n\nexport interface CollapsedMailViewAttrs {\n\tviewModel: MailViewerViewModel\n}\n\nexport class CollapsedMailView implements Component<CollapsedMailViewAttrs> {\n\tview({ attrs }: Vnode<CollapsedMailViewAttrs>): Children {\n\t\tconst { viewModel } = attrs\n\t\tconst { mail } = viewModel\n\t\tconst dateTime = formatDateWithWeekday(mail.receivedDate) + \" • \" + formatTime(mail.receivedDate)\n\t\tconst folderInfo = viewModel.getFolderInfo()\n\t\tif (!folderInfo) return null\n\n\t\treturn m(\n\t\t\t\".flex.items-center.pt.pb.click.no-wrap\",\n\t\t\t{\n\t\t\t\tclass: responsiveCardHPadding(),\n\t\t\t\tstyle: {\n\t\t\t\t\tcolor: theme.content_button,\n\t\t\t\t},\n\t\t\t\tonclick: () => viewModel.expandMail(Promise.resolve()),\n\t\t\t\tonkeyup: (e: KeyboardEvent) => {\n\t\t\t\t\tif (isKeyPressed(e.keyCode, Keys.SPACE)) {\n\t\t\t\t\t\tviewModel.expandMail(Promise.resolve())\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\ttabindex: TabIndex.Default,\n\t\t\t},\n\t\t\t[\n\t\t\t\tviewModel.isUnread() ? this.renderUnreadDot() : null,\n\t\t\t\tviewModel.isDraftMail() ? m(\".mr-xs\", this.renderIcon(Icons.Edit)) : null,\n\t\t\t\tm(this.getMailAddressDisplayClasses(viewModel), getMailAddressDisplayText(mail.sender.name, mail.sender.address, true)),\n\t\t\t\tm(\".flex.ml-between-s.items-center\", [\n\t\t\t\t\tmail.attachments.length > 0 ? this.renderIcon(Icons.Attachment) : null,\n\t\t\t\t\tviewModel.isConfidential() ? this.renderIcon(Icons.Lock) : null,\n\t\t\t\t\tthis.renderIcon(getFolderIconByType(folderInfo.folderType), folderInfo.name),\n\t\t\t\t\tm(\".small.font-weight-600\", dateTime),\n\t\t\t\t]),\n\t\t\t],\n\t\t)\n\t}\n\n\tprivate getMailAddressDisplayClasses(viewModel: MailViewerViewModel): string {\n\t\tlet classes = \".flex-grow.text-ellipsis\"\n\t\tif (viewModel.isUnread()) {\n\t\t\tclasses += \".font-weight-600\"\n\t\t}\n\t\treturn classes\n\t}\n\n\tprivate renderUnreadDot(): Children {\n\t\treturn m(\n\t\t\t\".flex.flex-no-grow.no-shrink.pr-s\",\n\t\t\tm(\".dot.bg-accent-fg\", {\n\t\t\t\tstyle: {\n\t\t\t\t\tmarginTop: 0,\n\t\t\t\t},\n\t\t\t}),\n\t\t)\n\t}\n\n\tprivate renderIcon(icon: AllIcons, hoverText: string | null = null) {\n\t\treturn m(Icon, {\n\t\t\ticon,\n\t\t\tcontainer: \"div\",\n\t\t\tstyle: {\n\t\t\t\tfill: theme.content_button,\n\t\t\t},\n\t\t\thoverText: hoverText,\n\t\t})\n\t}\n}\n","import m, { Children, Component, Vnode } from \"mithril\"\nimport { ConversationItem, ConversationViewModel } from \"./ConversationViewModel.js\"\nimport { MailViewer } from \"./MailViewer.js\"\nimport { lang } from \"../../misc/LanguageViewModel.js\"\nimport { theme } from \"../../gui/theme.js\"\nimport { Button, ButtonType } from \"../../gui/base/Button.js\"\nimport { elementIdPart, isSameId } from \"../../api/common/utils/EntityUtils.js\"\nimport { CollapsedMailView } from \"./CollapsedMailView.js\"\nimport { MailViewerViewModel } from \"./MailViewerViewModel.js\"\nimport { px, size } from \"../../gui/size.js\"\nimport { Keys } from \"../../api/common/TutanotaConstants.js\"\nimport { keyManager, Shortcut } from \"../../misc/KeyManager.js\"\nimport { styles } from \"../../gui/styles.js\"\nimport { responsiveCardHMargin } from \"../../gui/cards.js\"\n\nexport interface ConversationViewerAttrs {\n\tviewModel: ConversationViewModel\n\tdelayBodyRendering: Promise<unknown>\n}\n\nconst SCROLL_FACTOR = 4 / 5\n\nexport const conversationCardMargin = size.hpad_large\n\n/**\n * Displays mails in a conversation\n */\nexport class ConversationViewer implements Component<ConversationViewerAttrs> {\n\tprivate containerDom: HTMLElement | null = null\n\tprivate didScroll = false\n\t/** items from the last render, we need them to calculate the right subject based on the scroll position without the full re-render. */\n\tprivate lastItems: readonly ConversationItem[] | null = null\n\n\tprivate readonly shortcuts: Shortcut[] = [\n\t\t{\n\t\t\tkey: Keys.PAGE_UP,\n\t\t\texec: () => this.scrollUp(),\n\t\t\thelp: \"scrollUp_action\",\n\t\t},\n\t\t{\n\t\t\tkey: Keys.PAGE_DOWN,\n\t\t\texec: () => this.scrollDown(),\n\t\t\thelp: \"scrollDown_action\",\n\t\t},\n\t\t{\n\t\t\tkey: Keys.HOME,\n\t\t\texec: () => this.scrollToTop(),\n\t\t\thelp: \"scrollToTop_action\",\n\t\t},\n\t\t{\n\t\t\tkey: Keys.END,\n\t\t\texec: () => this.scrollToBottom(),\n\t\t\thelp: \"scrollToBottom_action\",\n\t\t},\n\t]\n\n\toncreate() {\n\t\tkeyManager.registerShortcuts(this.shortcuts)\n\t}\n\n\tonremove() {\n\t\tkeyManager.unregisterShortcuts(this.shortcuts)\n\t}\n\n\tview(vnode: Vnode<ConversationViewerAttrs>): Children {\n\t\tconst { viewModel, delayBodyRendering } = vnode.attrs\n\n\t\tviewModel.init(delayBodyRendering)\n\n\t\tthis.lastItems = viewModel.conversationItems()\n\t\tthis.doScroll(viewModel, this.lastItems)\n\n\t\treturn m(\".fill-absolute.nav-bg.flex.col\", [\n\t\t\t// see comment for .scrollbar-gutter-stable-or-fallback\n\t\t\tm(\n\t\t\t\t\".flex-grow.overflow-y-scroll\",\n\t\t\t\t{\n\t\t\t\t\toncreate: (vnode) => {\n\t\t\t\t\t\tthis.containerDom = vnode.dom as HTMLElement\n\t\t\t\t\t},\n\t\t\t\t\tonremove: () => {\n\t\t\t\t\t\tconsole.log(\"remove container\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tthis.renderItems(viewModel, this.lastItems),\n\t\t\t\tthis.renderLoadingState(viewModel),\n\t\t\t\tthis.renderFooter(),\n\t\t\t),\n\t\t])\n\t}\n\n\tprivate renderFooter() {\n\t\t// Having more room at the bottom allows the last email so it is (almost) always in the same place on the screen.\n\t\t// We reduce space by 100 for the header of the viewer and a bit more\n\t\tconst height =\n\t\t\tdocument.body.offsetHeight - (styles.isUsingBottomNavigation() ? size.navbar_height_mobile + size.bottom_nav_bar : size.navbar_height) - 300\n\t\treturn m(\".mt-l\", {\n\t\t\tstyle: {\n\t\t\t\theight: px(height),\n\t\t\t},\n\t\t})\n\t}\n\n\tprivate renderItems(viewModel: ConversationViewModel, entries: readonly ConversationItem[]): Children {\n\t\treturn entries.map((entry, position) => {\n\t\t\tswitch (entry.type) {\n\t\t\t\tcase \"mail\": {\n\t\t\t\t\tconst mailViewModel = entry.viewModel\n\t\t\t\t\tconst isPrimary = mailViewModel === viewModel.primaryViewModel()\n\t\t\t\t\t// only pass in position if we do have an actual conversation position\n\t\t\t\t\treturn this.renderViewer(mailViewModel, isPrimary, viewModel.isFinished() ? position : null)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n\n\tprivate renderLoadingState(viewModel: ConversationViewModel): Children {\n\t\treturn viewModel.isConnectionLost()\n\t\t\t? m(\n\t\t\t\t\t\".center\",\n\t\t\t\t\tm(Button, {\n\t\t\t\t\t\ttype: ButtonType.Secondary,\n\t\t\t\t\t\tlabel: \"retry_action\",\n\t\t\t\t\t\tclick: () => viewModel.retry(),\n\t\t\t\t\t}),\n\t\t\t  )\n\t\t\t: !viewModel.isFinished()\n\t\t\t? m(\n\t\t\t\t\t\".font-weight-600.center.mt-l\" + \".\" + responsiveCardHMargin(),\n\t\t\t\t\t{\n\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\tcolor: theme.content_button,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tlang.get(\"loading_msg\"),\n\t\t\t  )\n\t\t\t: null\n\t}\n\n\tprivate renderViewer(mailViewModel: MailViewerViewModel, isPrimary: boolean, position: number | null): Children {\n\t\treturn m(\n\t\t\t\".mlr-safe-inset\",\n\t\t\tm(\n\t\t\t\t\".border-radius-big.rel\",\n\t\t\t\t{\n\t\t\t\t\tclass: responsiveCardHMargin(),\n\t\t\t\t\tkey: elementIdPart(mailViewModel.mail.conversationEntry),\n\t\t\t\t\tstyle: {\n\t\t\t\t\t\tbackgroundColor: theme.content_bg,\n\t\t\t\t\t\tmarginTop: px(position == null || position === 0 ? 0 : conversationCardMargin),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tmailViewModel.isCollapsed()\n\t\t\t\t\t? m(CollapsedMailView, {\n\t\t\t\t\t\t\tviewModel: mailViewModel,\n\t\t\t\t\t  })\n\t\t\t\t\t: m(MailViewer, {\n\t\t\t\t\t\t\tviewModel: mailViewModel,\n\t\t\t\t\t\t\tisPrimary: isPrimary,\n\t\t\t\t\t\t\t// we want to expand for the first email like when it's a forwarded email\n\t\t\t\t\t\t\tdefaultQuoteBehavior: position === 0 ? \"expand\" : \"collapse\",\n\t\t\t\t\t  }),\n\t\t\t),\n\t\t)\n\t}\n\n\tprivate doScroll(viewModel: ConversationViewModel, items: readonly ConversationItem[]) {\n\t\tconst containerDom = this.containerDom\n\t\tif (!this.didScroll && containerDom && viewModel.isFinished()) {\n\t\t\tconst conversationId = viewModel.primaryMail.conversationEntry\n\n\t\t\tthis.didScroll = true\n\t\t\t// We need to do this at the end of the frame when every change is already applied.\n\t\t\t// Promise.resolve() schedules a microtask exactly where we need it.\n\t\t\t// RAF is too long and would flash the wrong frame\n\t\t\tPromise.resolve().then(() => {\n\t\t\t\t// There's a chance that item are not in sync with dom but it's very unlikely, this is the same frame after the last render we used the items\n\t\t\t\t// and viewModel is finished.\n\t\t\t\tconst itemIndex = items.findIndex((e) => e.type === \"mail\" && isSameId(e.entryId, conversationId))\n\t\t\t\t// Don't scroll if it's already the first (or if we didn't find it but that would be weird)\n\t\t\t\tif (itemIndex > 0) {\n\t\t\t\t\tconst childDom = containerDom.childNodes[itemIndex] as HTMLElement\n\t\t\t\t\tconst parentTop = containerDom.getBoundingClientRect().top\n\t\t\t\t\tconst childTop = childDom.getBoundingClientRect().top\n\t\t\t\t\tconst relativeTop = childTop - parentTop\n\t\t\t\t\tconst top = relativeTop - conversationCardMargin * 2 - 10\n\t\t\t\t\tcontainerDom.scrollTo({ top: top })\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t}\n\n\tprivate scrollUp(): void {\n\t\tif (this.containerDom) {\n\t\t\tthis.containerDom.scrollBy({ top: -this.containerDom.clientHeight * SCROLL_FACTOR, behavior: \"smooth\" })\n\t\t}\n\t}\n\n\tprivate scrollDown(): void {\n\t\tif (this.containerDom) {\n\t\t\tthis.containerDom.scrollBy({ top: this.containerDom.clientHeight * SCROLL_FACTOR, behavior: \"smooth\" })\n\t\t}\n\t}\n\n\tprivate scrollToTop(): void {\n\t\tif (this.containerDom) {\n\t\t\tthis.containerDom.scrollTo({ top: 0, behavior: \"smooth\" })\n\t\t}\n\t}\n\n\tprivate scrollToBottom(): void {\n\t\tif (this.containerDom) {\n\t\t\tthis.containerDom.scrollTo({ top: this.containerDom.scrollHeight - this.containerDom.offsetHeight, behavior: \"smooth\" })\n\t\t}\n\t}\n}\n","import m, { Children, Component, Vnode } from \"mithril\"\nimport { MailModel } from \"../model/MailModel.js\"\nimport { Mail } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { IconButton } from \"../../gui/base/IconButton.js\"\nimport { promptAndDeleteMails, showMoveMailsDropdown } from \"./MailGuiUtils.js\"\nimport { noOp, ofClass } from \"@tutao/tutanota-utils\"\nimport { Icons } from \"../../gui/base/icons/Icons.js\"\nimport { MailViewerViewModel } from \"./MailViewerViewModel.js\"\nimport { UserError } from \"../../api/main/UserError.js\"\nimport { showUserError } from \"../../misc/ErrorHandlerImpl.js\"\nimport { createAsyncDropdown, createDropdown, DropdownButtonAttrs } from \"../../gui/base/Dropdown.js\"\nimport { editDraft, mailViewerMoreActions, makeAssignMailsButtons } from \"./MailViewerUtils.js\"\nimport { ButtonColor } from \"../../gui/base/Button.js\"\nimport { isApp } from \"../../api/common/Env.js\"\nimport { locator } from \"../../api/main/MainLocator.js\"\nimport { showProgressDialog } from \"../../gui/dialogs/ProgressDialog.js\"\nimport { exportMails } from \"../export/Exporter.js\"\n\n/*\n\tnote that mailViewerViewModel has a mailModel, so you do not need to pass both if you pass a mailViewerViewModel\n */\nexport interface MailViewerToolbarAttrs {\n\tmailModel: MailModel\n\tmailViewerViewModel?: MailViewerViewModel\n\tmails: Mail[]\n\tselectNone?: () => void\n}\n\nexport class MailViewerActions implements Component<MailViewerToolbarAttrs> {\n\tview(vnode: Vnode<MailViewerToolbarAttrs>) {\n\t\treturn m(\".flex.ml-between-s.items-center\", [\n\t\t\tthis.renderSingleMailActions(vnode.attrs),\n\t\t\tvnode.attrs.mailViewerViewModel ? m(\".nav-bar-spacer\") : null,\n\t\t\tthis.renderActions(vnode.attrs),\n\t\t\tthis.renderMoreButton(vnode.attrs.mailViewerViewModel),\n\t\t])\n\t}\n\n\tprivate renderActions(attrs: MailViewerToolbarAttrs): Children {\n\t\tconst mailModel = attrs.mailViewerViewModel ? attrs.mailViewerViewModel.mailModel : attrs.mailModel\n\n\t\tif (!mailModel || !attrs.mails) {\n\t\t\treturn null\n\t\t} else if (attrs.mailViewerViewModel) {\n\t\t\treturn [\n\t\t\t\tthis.renderDeleteButton(mailModel, attrs.mails, attrs.selectNone ?? noOp),\n\t\t\t\tattrs.mailViewerViewModel.canForwardOrMove() ? this.renderMoveButton(mailModel, attrs.mails) : null,\n\t\t\t\tattrs.mailViewerViewModel.isDraftMail() ? null : this.renderReadButton(attrs),\n\t\t\t]\n\t\t} else if (attrs.mails.length > 0) {\n\t\t\treturn [\n\t\t\t\tthis.renderDeleteButton(mailModel, attrs.mails, attrs.selectNone ?? noOp),\n\t\t\t\tattrs.mailModel.isMovingMailsAllowed() ? this.renderMoveButton(mailModel, attrs.mails) : null,\n\t\t\t\tthis.renderReadButton(attrs),\n\t\t\t\tthis.renderExportButton(attrs),\n\t\t\t]\n\t\t}\n\t}\n\n\t/*\n\t * Actions that can only be taken on a single mail (reply, forward, edit, assign)\n\t * Will only return actions if there is a mailViewerViewModel\n\t * */\n\tprivate renderSingleMailActions(attrs: MailViewerToolbarAttrs): Children {\n\t\t// mailViewerViewModel means we are viewing one mail; if there is only the mailModel, it is coming from a MultiViewer\n\t\tif (attrs.mailViewerViewModel) {\n\t\t\tif (attrs.mailViewerViewModel.isAnnouncement()) {\n\t\t\t\treturn []\n\t\t\t} else if (attrs.mailViewerViewModel.isDraftMail()) {\n\t\t\t\treturn [this.renderEditButton(attrs.mailViewerViewModel)]\n\t\t\t} else if (attrs.mailViewerViewModel.canForwardOrMove()) {\n\t\t\t\treturn [this.renderReplyButton(attrs.mailViewerViewModel), this.renderForwardButton(attrs.mailViewerViewModel)]\n\t\t\t} else if (attrs.mailViewerViewModel.canAssignMails()) {\n\t\t\t\treturn [this.renderReplyButton(attrs.mailViewerViewModel), this.renderAssignButton(attrs.mailViewerViewModel)]\n\t\t\t} else {\n\t\t\t\treturn [this.renderReplyButton(attrs.mailViewerViewModel)]\n\t\t\t}\n\t\t} else {\n\t\t\treturn []\n\t\t}\n\t}\n\n\tprivate renderDeleteButton(mailModel: MailModel, mails: Mail[], selectNone: () => void): Children {\n\t\treturn m(IconButton, {\n\t\t\ttitle: \"delete_action\",\n\t\t\tclick: () => {\n\t\t\t\tpromptAndDeleteMails(mailModel, mails, selectNone)\n\t\t\t},\n\t\t\ticon: Icons.Trash,\n\t\t})\n\t}\n\n\tprivate renderMoveButton(mailModel: MailModel, mails: Mail[]): Children {\n\t\treturn m(IconButton, {\n\t\t\ttitle: \"move_action\",\n\t\t\ticon: Icons.Folder,\n\t\t\tclick: (e, dom) => showMoveMailsDropdown(mailModel, dom.getBoundingClientRect(), mails),\n\t\t})\n\t}\n\n\tprivate renderReadButton({ mailModel, mailViewerViewModel, mails }: MailViewerToolbarAttrs): Children {\n\t\tconst markAction: (unread: boolean) => unknown = mailViewerViewModel\n\t\t\t? (unread) => mailViewerViewModel.setUnread(unread)\n\t\t\t: (unread) => mailModel.markMails(mails, unread)\n\n\t\tconst markReadButton = m(IconButton, {\n\t\t\ttitle: \"markRead_action\",\n\t\t\tclick: () => markAction(false),\n\t\t\ticon: Icons.Eye,\n\t\t})\n\t\tconst markUnreadButton = m(IconButton, {\n\t\t\ttitle: \"markUnread_action\",\n\t\t\tclick: () => markAction(true),\n\t\t\ticon: Icons.NoEye,\n\t\t})\n\n\t\t// mailViewerViewModel means we are viewing one mail; if there is only the mailModel, it is coming from a MultiViewer\n\t\tif (mailViewerViewModel) {\n\t\t\tif (mailViewerViewModel.isUnread()) {\n\t\t\t\treturn markReadButton\n\t\t\t} else {\n\t\t\t\treturn markUnreadButton\n\t\t\t}\n\t\t}\n\n\t\treturn [markReadButton, markUnreadButton]\n\t}\n\n\tprivate renderExportButton(attrs: MailViewerToolbarAttrs) {\n\t\tif (!isApp() && attrs.mailModel.isExportingMailsAllowed()) {\n\t\t\treturn m(IconButton, {\n\t\t\t\ttitle: \"export_action\",\n\t\t\t\tclick: () => showProgressDialog(\"pleaseWait_msg\", exportMails(attrs.mails, locator.entityClient, locator.fileController)),\n\t\t\t\ticon: Icons.Export,\n\t\t\t})\n\t\t}\n\t}\n\n\tprivate renderReplyButton(viewModel: MailViewerViewModel) {\n\t\tconst actions: Children = []\n\t\tactions.push(\n\t\t\tm(IconButton, {\n\t\t\t\ttitle: \"reply_action\",\n\t\t\t\tclick: () => viewModel.reply(false),\n\t\t\t\ticon: Icons.Reply,\n\t\t\t}),\n\t\t)\n\n\t\tif (viewModel.canReplyAll()) {\n\t\t\tactions.push(\n\t\t\t\tm(IconButton, {\n\t\t\t\t\ttitle: \"replyAll_action\",\n\t\t\t\t\tclick: () => viewModel.reply(true),\n\t\t\t\t\ticon: Icons.ReplyAll,\n\t\t\t\t}),\n\t\t\t)\n\t\t}\n\t\treturn actions\n\t}\n\n\tprivate renderForwardButton(viewModel: MailViewerViewModel) {\n\t\treturn m(IconButton, {\n\t\t\ttitle: \"forward_action\",\n\t\t\tclick: () => viewModel.forward().catch(ofClass(UserError, showUserError)),\n\t\t\ticon: Icons.Forward,\n\t\t})\n\t}\n\n\tprivate renderMoreButton(viewModel: MailViewerViewModel | undefined): Children {\n\t\tlet actions: DropdownButtonAttrs[] = []\n\n\t\tif (viewModel) {\n\t\t\tactions = mailViewerMoreActions(viewModel, false)\n\t\t}\n\n\t\treturn actions.length > 0\n\t\t\t? m(IconButton, {\n\t\t\t\t\ttitle: \"more_label\",\n\t\t\t\t\ticon: Icons.More,\n\t\t\t\t\tclick: createDropdown({\n\t\t\t\t\t\tlazyButtons: () => actions,\n\t\t\t\t\t\twidth: 300,\n\t\t\t\t\t}),\n\t\t\t  })\n\t\t\t: null\n\t}\n\n\tprivate renderAssignButton(viewModel: MailViewerViewModel) {\n\t\treturn m(IconButton, {\n\t\t\ttitle: \"forward_action\",\n\t\t\ticon: Icons.Forward,\n\t\t\tcolors: ButtonColor.Content,\n\t\t\tclick: createAsyncDropdown({\n\t\t\t\twidth: 250,\n\t\t\t\tlazyButtons: () => makeAssignMailsButtons(viewModel),\n\t\t\t}),\n\t\t})\n\t}\n\n\tprivate renderEditButton(viewModel: MailViewerViewModel) {\n\t\treturn m(IconButton, {\n\t\t\ttitle: \"edit_action\",\n\t\t\tclick: () => editDraft(viewModel),\n\t\t\ticon: Icons.Edit,\n\t\t})\n\t}\n}\n","import { Mail } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport m, { Children, Vnode } from \"mithril\"\nimport { IconButton } from \"../../gui/base/IconButton.js\"\nimport { Icons } from \"../../gui/base/icons/Icons.js\"\nimport { promptAndDeleteMails, showMoveMailsDropdown } from \"./MailGuiUtils.js\"\nimport { DROPDOWN_MARGIN } from \"../../gui/base/Dropdown.js\"\nimport { MobileBottomActionBar } from \"../../gui/MobileBottomActionBar.js\"\nimport { MailModel } from \"../model/MailModel.js\"\n\nexport interface MobileMailMultiselectionActionBarAttrs {\n\tmails: readonly Mail[]\n\tmailModel: MailModel\n\tselectNone: () => unknown\n}\n\nexport class MobileMailMultiselectionActionBar {\n\tprivate dom: HTMLElement | null = null\n\n\tview({ attrs }: Vnode<MobileMailMultiselectionActionBarAttrs>): Children {\n\t\tconst { mails, selectNone, mailModel } = attrs\n\t\treturn m(\n\t\t\tMobileBottomActionBar,\n\t\t\t{\n\t\t\t\toncreate: ({ dom }) => (this.dom = dom as HTMLElement),\n\t\t\t},\n\t\t\t[\n\t\t\t\tm(IconButton, {\n\t\t\t\t\ticon: Icons.Trash,\n\t\t\t\t\ttitle: \"delete_action\",\n\t\t\t\t\tclick: () => promptAndDeleteMails(mailModel, mails, selectNone),\n\t\t\t\t}),\n\t\t\t\tmailModel.isMovingMailsAllowed()\n\t\t\t\t\t? m(IconButton, {\n\t\t\t\t\t\t\ticon: Icons.Folder,\n\t\t\t\t\t\t\ttitle: \"move_action\",\n\t\t\t\t\t\t\tclick: (e, dom) => {\n\t\t\t\t\t\t\t\tconst referenceDom = this.dom ?? dom\n\t\t\t\t\t\t\t\tshowMoveMailsDropdown(mailModel, referenceDom.getBoundingClientRect(), mails, {\n\t\t\t\t\t\t\t\t\tonSelected: () => selectNone,\n\t\t\t\t\t\t\t\t\twidth: referenceDom.offsetWidth - DROPDOWN_MARGIN * 2,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t},\n\t\t\t\t\t  })\n\t\t\t\t\t: null,\n\t\t\t\tm(IconButton, {\n\t\t\t\t\ticon: Icons.Eye,\n\t\t\t\t\ttitle: \"markRead_action\",\n\t\t\t\t\tclick: () => {\n\t\t\t\t\t\tmailModel.markMails(mails, false)\n\t\t\t\t\t\t// I think these should be left out and it stops a bug, but we'll see\n\t\t\t\t\t\t//selectNone()\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t\tm(IconButton, {\n\t\t\t\t\ticon: Icons.NoEye,\n\t\t\t\t\ttitle: \"markUnread_action\",\n\t\t\t\t\tclick: () => {\n\t\t\t\t\t\tmailModel.markMails(mails, true)\n\t\t\t\t\t\t//selectNone()\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t],\n\t\t)\n\t}\n}\n","import m, { Children, ClassComponent, Vnode } from \"mithril\"\nimport { MailFilterType } from \"../model/MailUtils.js\"\nimport { Icons } from \"../../gui/base/icons/Icons.js\"\nimport { createDropdown } from \"../../gui/base/Dropdown.js\"\nimport { ToggleButton } from \"../../gui/base/buttons/ToggleButton.js\"\n\nexport interface MailFilterButtonAttrs {\n\tfilter: MailFilterType | null\n\tsetFilter: (filter: MailFilterType | null) => unknown\n}\n\nexport class MailFilterButton implements ClassComponent<MailFilterButtonAttrs> {\n\tview({ attrs }: Vnode<MailFilterButtonAttrs>): Children {\n\t\treturn m(ToggleButton, {\n\t\t\ticon: Icons.Filter,\n\t\t\ttitle: \"filter_label\",\n\t\t\ttoggled: attrs.filter != null,\n\t\t\tonToggled: (_, event) => this.showDropdown(attrs, event),\n\t\t})\n\t}\n\n\tprivate showDropdown({ filter, setFilter }: MailFilterButtonAttrs, event: MouseEvent) {\n\t\tcreateDropdown({\n\t\t\tlazyButtons: () => [\n\t\t\t\t{\n\t\t\t\t\tselected: filter === MailFilterType.Unread,\n\t\t\t\t\tlabel: \"filterUnread_label\",\n\t\t\t\t\tclick: () => {\n\t\t\t\t\t\tsetFilter(MailFilterType.Unread)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tselected: filter === MailFilterType.Read,\n\t\t\t\t\tlabel: \"filterRead_label\",\n\t\t\t\t\tclick: () => {\n\t\t\t\t\t\tsetFilter(MailFilterType.Read)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tselected: filter === MailFilterType.WithAttachments,\n\t\t\t\t\tlabel: \"filterWithAttachments_label\",\n\t\t\t\t\tclick: () => {\n\t\t\t\t\t\tsetFilter(MailFilterType.WithAttachments)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"filterAllMails_label\",\n\t\t\t\t\tclick: () => {\n\t\t\t\t\t\tsetFilter(null)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t})(event, event.target as HTMLElement)\n\t}\n}\n","import m, { Children, Vnode } from \"mithril\"\nimport { ViewSlider } from \"../../gui/nav/ViewSlider.js\"\nimport { ColumnType, ViewColumn } from \"../../gui/base/ViewColumn\"\nimport { lang } from \"../../misc/LanguageViewModel\"\nimport { ButtonType } from \"../../gui/base/Button.js\"\nimport { Dialog } from \"../../gui/base/Dialog\"\nimport { FeatureType, Keys, MailFolderType } from \"../../api/common/TutanotaConstants\"\nimport { AppHeaderAttrs, Header } from \"../../gui/Header.js\"\nimport type { Mail, MailFolder } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { noOp, ofClass } from \"@tutao/tutanota-utils\"\nimport { MailListView } from \"./MailListView\"\nimport { assertMainOrNode, isApp } from \"../../api/common/Env\"\nimport type { Shortcut } from \"../../misc/KeyManager\"\nimport { keyManager } from \"../../misc/KeyManager\"\nimport { getMailSelectionMessage, MultiItemViewer } from \"./MultiItemViewer.js\"\nimport { Icons } from \"../../gui/base/icons/Icons\"\nimport { showProgressDialog } from \"../../gui/dialogs/ProgressDialog\"\nimport { canDoDragAndDropExport, getFolderName, getMailboxName } from \"../model/MailUtils\"\nimport type { MailboxDetail } from \"../model/MailModel\"\nimport { locator } from \"../../api/main/MainLocator\"\nimport { PermissionError } from \"../../api/common/error/PermissionError\"\nimport { styles } from \"../../gui/styles\"\nimport { px, size } from \"../../gui/size\"\nimport { UserError } from \"../../api/main/UserError\"\nimport { showUserError } from \"../../misc/ErrorHandlerImpl\"\nimport { archiveMails, getConversationTitle, getMoveMailBounds, moveMails, moveToInbox, promptAndDeleteMails, showMoveMailsDropdown } from \"./MailGuiUtils\"\nimport { getElementId, isSameId } from \"../../api/common/utils/EntityUtils\"\nimport { isNewMailActionAvailable } from \"../../gui/nav/NavFunctions\"\nimport { CancelledError } from \"../../api/common/error/CancelledError\"\nimport Stream from \"mithril/stream\"\nimport { readLocalFiles } from \"../../file/FileController.js\"\nimport { BottomNav } from \"../../gui/nav/BottomNav.js\"\nimport { MobileMailActionBar } from \"./MobileMailActionBar.js\"\nimport { deviceConfig } from \"../../misc/DeviceConfig.js\"\nimport { DrawerMenuAttrs } from \"../../gui/nav/DrawerMenu.js\"\nimport { BaseTopLevelView } from \"../../gui/BaseTopLevelView.js\"\nimport { showEditFolderDialog } from \"./EditFolderDialog.js\"\nimport { MailFoldersView } from \"./MailFoldersView.js\"\nimport { isSpamOrTrashFolder } from \"../../api/common/mail/CommonMailUtils.js\"\nimport { FolderColumnView } from \"../../gui/FolderColumnView.js\"\nimport { SidebarSection } from \"../../gui/SidebarSection.js\"\nimport { EditFoldersDialog } from \"./EditFoldersDialog.js\"\nimport { TopLevelAttrs, TopLevelView } from \"../../TopLevelView.js\"\nimport { ConversationViewModel } from \"./ConversationViewModel.js\"\nimport { conversationCardMargin, ConversationViewer } from \"./ConversationViewer.js\"\nimport type { DesktopSystemFacade } from \"../../native/common/generatedipc/DesktopSystemFacade.js\"\nimport { IconButton } from \"../../gui/base/IconButton.js\"\nimport { BackgroundColumnLayout } from \"../../gui/BackgroundColumnLayout.js\"\nimport { MailViewerActions } from \"./MailViewerToolbar.js\"\nimport { theme } from \"../../gui/theme.js\"\nimport { MobileMailMultiselectionActionBar } from \"./MobileMailMultiselectionActionBar.js\"\nimport { SelectAllCheckbox } from \"../../gui/SelectAllCheckbox.js\"\nimport { DesktopListToolbar, DesktopViewerToolbar } from \"../../gui/DesktopToolbars.js\"\nimport { MobileHeader } from \"../../gui/MobileHeader.js\"\nimport { LazySearchBar } from \"../../misc/LazySearchBar.js\"\nimport { MultiselectMobileHeader } from \"../../gui/MultiselectMobileHeader.js\"\nimport { MailViewModel } from \"./MailViewModel.js\"\nimport { selectionAttrsForList } from \"../../misc/ListModel.js\"\nimport { ListLoadingState, MultiselectMode } from \"../../gui/base/List.js\"\nimport { EnterMultiselectIconButton } from \"../../gui/EnterMultiselectIconButton.js\"\nimport { MailFilterButton } from \"./MailFilterButton.js\"\nimport { listSelectionKeyboardShortcuts } from \"../../gui/base/ListUtils.js\"\n\nassertMainOrNode()\n\n/** State persisted between re-creations. */\nexport interface MailViewCache {\n\t/** The preference for if conversation view was used, so we can reset if it was changed */\n\tconversationViewPreference: boolean | null\n}\n\nexport interface MailViewAttrs extends TopLevelAttrs {\n\tdrawerAttrs: DrawerMenuAttrs\n\tcache: MailViewCache\n\theader: AppHeaderAttrs\n\tdesktopSystemFacade: DesktopSystemFacade | null\n\tmailViewModel: MailViewModel\n}\n\n/**\n * Top-level view for displaying mailboxes.\n */\nexport class MailView extends BaseTopLevelView implements TopLevelView<MailViewAttrs> {\n\tprivate readonly listColumn: ViewColumn\n\tprivate readonly folderColumn: ViewColumn\n\tprivate readonly mailColumn: ViewColumn\n\tprivate readonly viewSlider: ViewSlider\n\tprivate readonly desktopSystemFacade: DesktopSystemFacade | null\n\tcache: MailViewCache\n\treadonly oncreate: TopLevelView[\"oncreate\"]\n\treadonly onremove: TopLevelView[\"onremove\"]\n\n\tprivate countersStream: Stream<unknown> | null = null\n\n\tprivate expandedState: Set<Id>\n\tprivate mailboxSubscription: Stream<void> | null = null\n\tprivate mailViewModel: MailViewModel\n\n\tget conversationViewModel(): ConversationViewModel | null {\n\t\treturn this.mailViewModel.getConversationViewModel()\n\t}\n\n\tconstructor(vnode: Vnode<MailViewAttrs>) {\n\t\tsuper()\n\t\tthis.desktopSystemFacade = vnode.attrs.desktopSystemFacade\n\t\tthis.expandedState = new Set(deviceConfig.getExpandedFolders(locator.logins.getUserController().userId))\n\t\tthis.cache = vnode.attrs.cache\n\t\tthis.folderColumn = this.createFolderColumn(null, vnode.attrs.drawerAttrs)\n\t\tthis.mailViewModel = vnode.attrs.mailViewModel\n\t\tthis.listColumn = new ViewColumn(\n\t\t\t{\n\t\t\t\tview: () => {\n\t\t\t\t\tconst listId = this.mailViewModel.getListId()\n\t\t\t\t\treturn m(BackgroundColumnLayout, {\n\t\t\t\t\t\tbackgroundColor: theme.navigation_bg,\n\t\t\t\t\t\tdesktopToolbar: () =>\n\t\t\t\t\t\t\tm(DesktopListToolbar, m(SelectAllCheckbox, selectionAttrsForList(this.mailViewModel.listModel)), this.renderFilterButton()),\n\t\t\t\t\t\tcolumnLayout: listId\n\t\t\t\t\t\t\t? m(\n\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\t\tstyle: {\n\t\t\t\t\t\t\t\t\t\t\tmarginBottom: px(conversationCardMargin),\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(MailListView, {\n\t\t\t\t\t\t\t\t\t\tkey: listId,\n\t\t\t\t\t\t\t\t\t\tmailViewModel: this.mailViewModel,\n\t\t\t\t\t\t\t\t\t\tlistId: listId,\n\t\t\t\t\t\t\t\t\t\tonSingleSelection: (mail) => {\n\t\t\t\t\t\t\t\t\t\t\tif (!this.mailViewModel.listModel?.state.inMultiselect) {\n\t\t\t\t\t\t\t\t\t\t\t\tthis.viewSlider.focus(this.mailColumn)\n\n\t\t\t\t\t\t\t\t\t\t\t\t// Make sure that we mark mail as read if you select the mail again, even if it was selected before.\n\t\t\t\t\t\t\t\t\t\t\t\t// Do it in the next even loop to not rely on what is called first, listModel or us. ListModel changes are\n\t\t\t\t\t\t\t\t\t\t\t\t// sync so this should be enough.\n\t\t\t\t\t\t\t\t\t\t\t\tPromise.resolve().then(() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst conversationViewModel = this.mailViewModel.getConversationViewModel()\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (conversationViewModel && isSameId(mail._id, conversationViewModel.primaryMail._id)) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tconversationViewModel?.primaryViewModel().setUnread(false)\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\t}\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tonClearFolder: async () => {\n\t\t\t\t\t\t\t\t\t\t\tconst folder = this.mailViewModel.getSelectedFolder()\n\t\t\t\t\t\t\t\t\t\t\tif (folder == null) {\n\t\t\t\t\t\t\t\t\t\t\t\tconsole.warn(\"Cannot delete folder, no folder is selected\")\n\t\t\t\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tconst confirmed = await Dialog.confirm(() =>\n\t\t\t\t\t\t\t\t\t\t\t\tlang.get(\"confirmDeleteFinallySystemFolder_msg\", { \"{1}\": getFolderName(folder) }),\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\tif (confirmed) {\n\t\t\t\t\t\t\t\t\t\t\t\tshowProgressDialog(\"progressDeleting_msg\", this.mailViewModel.finallyDeleteAllMailsInSelectedFolder(folder))\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}),\n\t\t\t\t\t\t\t  )\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\tmobileHeader: () =>\n\t\t\t\t\t\t\tthis.mailViewModel.listModel?.state.inMultiselect\n\t\t\t\t\t\t\t\t? m(MultiselectMobileHeader, {\n\t\t\t\t\t\t\t\t\t\t...selectionAttrsForList(this.mailViewModel.listModel),\n\t\t\t\t\t\t\t\t\t\tmessage: getMailSelectionMessage(this.mailViewModel.listModel.getSelectedAsArray()),\n\t\t\t\t\t\t\t\t  })\n\t\t\t\t\t\t\t\t: m(MobileHeader, {\n\t\t\t\t\t\t\t\t\t\t...vnode.attrs.header,\n\t\t\t\t\t\t\t\t\t\ttitle: this.listColumn.getTitle(),\n\t\t\t\t\t\t\t\t\t\tcolumnType: \"first\",\n\t\t\t\t\t\t\t\t\t\tactions: [\n\t\t\t\t\t\t\t\t\t\t\tthis.renderFilterButton(),\n\t\t\t\t\t\t\t\t\t\t\tm(EnterMultiselectIconButton, {\n\t\t\t\t\t\t\t\t\t\t\t\tclickAction: () => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tthis.mailViewModel.listModel?.enterMultiselect()\n\t\t\t\t\t\t\t\t\t\t\t\t},\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\tprimaryAction: () => this.renderHeaderRightView(),\n\t\t\t\t\t\t\t\t\t\tbackAction: () => this.viewSlider.focusPreviousColumn(),\n\t\t\t\t\t\t\t\t  }),\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,\n\t\t\tsize.second_col_max_width,\n\t\t\t() => {\n\t\t\t\tconst selectedFolder = this.mailViewModel.getSelectedFolder()\n\t\t\t\treturn selectedFolder ? getFolderName(selectedFolder) : \"\"\n\t\t\t},\n\t\t)\n\n\t\tthis.mailColumn = new ViewColumn(\n\t\t\t{\n\t\t\t\tview: () => {\n\t\t\t\t\tconst viewModel = this.conversationViewModel\n\t\t\t\t\tif (viewModel) {\n\t\t\t\t\t\treturn this.renderSingleMailViewer(vnode.attrs.header, viewModel)\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn this.renderMultiMailViewer(vnode.attrs.header)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\tColumnType.Background,\n\t\t\tsize.third_col_min_width,\n\t\t\tsize.third_col_max_width,\n\t\t\tundefined,\n\t\t\t() => lang.get(\"email_label\"),\n\t\t)\n\t\tthis.viewSlider = new ViewSlider([this.folderColumn, this.listColumn, this.mailColumn], \"MailView\")\n\t\tthis.viewSlider.focusedColumn = this.viewSlider.columns[0]\n\n\t\tconst shortcuts = this.getShortcuts()\n\n\t\tvnode.attrs.mailViewModel.init()\n\n\t\tthis.oncreate = (vnode) => {\n\t\t\tthis.countersStream = locator.mailModel.mailboxCounters.map(m.redraw)\n\t\t\tkeyManager.registerShortcuts(shortcuts)\n\t\t\tthis.cache.conversationViewPreference = deviceConfig.getConversationViewShowOnlySelectedMail()\n\t\t}\n\n\t\tthis.onremove = () => {\n\t\t\t// cancel the loading if we are destroyed\n\t\t\tthis.mailViewModel.listModel?.cancelLoadAll()\n\n\t\t\tthis.countersStream?.end(true)\n\t\t\tthis.countersStream = null\n\n\t\t\tkeyManager.unregisterShortcuts(shortcuts)\n\t\t\t// this is safe to pause because if we are recreated we will subscribe and get a new value right away\n\t\t\tthis.mailboxSubscription?.end(true)\n\t\t}\n\t}\n\n\tprivate renderFilterButton() {\n\t\treturn m(MailFilterButton, { filter: this.mailViewModel.filterType, setFilter: (filter) => this.mailViewModel.setFilter(filter) })\n\t}\n\n\tprivate mailViewerSingleActions(viewModel: ConversationViewModel) {\n\t\treturn m(MailViewerActions, {\n\t\t\tmailModel: viewModel.primaryViewModel().mailModel,\n\t\t\tmailViewerViewModel: viewModel.primaryViewModel(),\n\t\t\tmails: [viewModel.primaryMail],\n\t\t})\n\t}\n\n\tprivate renderSingleMailViewer(header: AppHeaderAttrs, viewModel: ConversationViewModel) {\n\t\treturn m(BackgroundColumnLayout, {\n\t\t\tbackgroundColor: theme.navigation_bg,\n\t\t\tdesktopToolbar: () => m(DesktopViewerToolbar, this.mailViewerSingleActions(viewModel)),\n\t\t\tmobileHeader: () =>\n\t\t\t\tm(MobileHeader, {\n\t\t\t\t\t...header,\n\t\t\t\t\tbackAction: () => {\n\t\t\t\t\t\tthis.viewSlider.focusPreviousColumn()\n\t\t\t\t\t},\n\t\t\t\t\tcolumnType: \"other\",\n\t\t\t\t\tactions: null,\n\t\t\t\t\tmulticolumnActions: () => this.mailViewerSingleActions(viewModel),\n\t\t\t\t\tprimaryAction: () => this.renderHeaderRightView(),\n\t\t\t\t\ttitle: getConversationTitle(viewModel),\n\t\t\t\t}),\n\t\t\tcolumnLayout: m(ConversationViewer, {\n\t\t\t\t// Re-create the whole viewer and its vnode tree if email has changed\n\t\t\t\tkey: getElementId(viewModel.primaryMail),\n\t\t\t\tviewModel: viewModel,\n\t\t\t\t// this assumes that the viewSlider focus animation is already started\n\t\t\t\tdelayBodyRendering: this.viewSlider.waitForAnimation(),\n\t\t\t}),\n\t\t})\n\t}\n\n\tprivate mailViewerMultiActions() {\n\t\treturn m(MailViewerActions, {\n\t\t\tmailModel: locator.mailModel,\n\t\t\tmails: this.mailViewModel.listModel?.getSelectedAsArray() ?? [],\n\t\t\tselectNone: () => this.mailViewModel.listModel?.selectNone(),\n\t\t})\n\t}\n\n\tprivate renderMultiMailViewer(header: AppHeaderAttrs) {\n\t\treturn m(BackgroundColumnLayout, {\n\t\t\tbackgroundColor: theme.navigation_bg,\n\t\t\tdesktopToolbar: () => m(DesktopViewerToolbar, this.mailViewerMultiActions()),\n\t\t\tmobileHeader: () =>\n\t\t\t\tm(MobileHeader, {\n\t\t\t\t\tactions: this.mailViewerMultiActions(),\n\t\t\t\t\tprimaryAction: () => this.renderHeaderRightView(),\n\t\t\t\t\tbackAction: () => this.viewSlider.focusPreviousColumn(),\n\t\t\t\t\t...header,\n\t\t\t\t\tcolumnType: \"other\",\n\t\t\t\t}),\n\t\t\tcolumnLayout: m(MultiItemViewer, {\n\t\t\t\tselectedEntities: this.mailViewModel.listModel?.getSelectedAsArray() ?? [],\n\t\t\t\tselectNone: () => {\n\t\t\t\t\tthis.mailViewModel.listModel?.selectNone()\n\t\t\t\t},\n\t\t\t\tloadAll: () => this.mailViewModel.listModel?.loadAll(),\n\t\t\t\tstopLoadAll: () => this.mailViewModel.listModel?.cancelLoadAll(),\n\t\t\t\tloadingAll: this.mailViewModel.listModel?.state.loadingAll\n\t\t\t\t\t? \"loading\"\n\t\t\t\t\t: this.mailViewModel.listModel?.state.loadingStatus === ListLoadingState.Done\n\t\t\t\t\t? \"loaded\"\n\t\t\t\t\t: \"can_load\",\n\t\t\t\tgetSelectionMessage: (selected: ReadonlyArray<Mail>) => getMailSelectionMessage(selected),\n\t\t\t}),\n\t\t})\n\t}\n\n\tview({ attrs }: Vnode<MailViewAttrs>): Children {\n\t\treturn m(\n\t\t\t\"#mail.main-view\",\n\t\t\t{\n\t\t\t\tondragover: (ev: DragEvent) => {\n\t\t\t\t\t// do not check the datatransfer here because it is not always filled, e.g. in Safari\n\t\t\t\t\tev.stopPropagation()\n\t\t\t\t\tev.preventDefault()\n\t\t\t\t},\n\t\t\t\tondrop: (ev: DragEvent) => {\n\t\t\t\t\tif (isNewMailActionAvailable() && ev.dataTransfer?.files && ev.dataTransfer.files.length > 0) {\n\t\t\t\t\t\tPromise.all([\n\t\t\t\t\t\t\tthis.mailViewModel.getMailboxDetails(),\n\t\t\t\t\t\t\treadLocalFiles(ev.dataTransfer.files),\n\t\t\t\t\t\t\timport(\"../signature/Signature\"),\n\t\t\t\t\t\t\timport(\"../editor/MailEditor\"),\n\t\t\t\t\t\t])\n\t\t\t\t\t\t\t.then(([mailbox, dataFiles, { appendEmailSignature }, { newMailEditorFromTemplate }]) => {\n\t\t\t\t\t\t\t\tmailbox &&\n\t\t\t\t\t\t\t\t\tnewMailEditorFromTemplate(\n\t\t\t\t\t\t\t\t\t\tmailbox,\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\tappendEmailSignature(\"\", locator.logins.getUserController().props),\n\t\t\t\t\t\t\t\t\t\tdataFiles,\n\t\t\t\t\t\t\t\t\t).then((dialog) => dialog.show())\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.catch(ofClass(PermissionError, noOp))\n\t\t\t\t\t\t\t.catch(ofClass(UserError, showUserError))\n\t\t\t\t\t}\n\n\t\t\t\t\t// prevent in any case because firefox tries to open\n\t\t\t\t\t// dataTransfer as a URL otherwise.\n\t\t\t\t\tev.stopPropagation()\n\t\t\t\t\tev.preventDefault()\n\t\t\t\t},\n\t\t\t},\n\t\t\tm(this.viewSlider, {\n\t\t\t\theader: m(Header, {\n\t\t\t\t\trightView: this.renderHeaderRightView(),\n\t\t\t\t\tsearchBar: () =>\n\t\t\t\t\t\t// not showing search for external users\n\t\t\t\t\t\tlocator.logins.isInternalUserLoggedIn()\n\t\t\t\t\t\t\t? m(LazySearchBar, {\n\t\t\t\t\t\t\t\t\tplaceholder: lang.get(\"searchEmails_placeholder\"),\n\t\t\t\t\t\t\t  })\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\t...attrs.header,\n\t\t\t\t}),\n\t\t\t\tbottomNav:\n\t\t\t\t\tstyles.isSingleColumnLayout() && this.viewSlider.focusedColumn === this.mailColumn && this.conversationViewModel\n\t\t\t\t\t\t? m(MobileMailActionBar, { viewModel: this.conversationViewModel.primaryViewModel() })\n\t\t\t\t\t\t: styles.isSingleColumnLayout() && this.mailViewModel.listModel?.state.inMultiselect\n\t\t\t\t\t\t? m(MobileMailMultiselectionActionBar, {\n\t\t\t\t\t\t\t\tmails: this.mailViewModel.listModel.getSelectedAsArray(),\n\t\t\t\t\t\t\t\tselectNone: () => this.mailViewModel.listModel?.selectNone(),\n\t\t\t\t\t\t\t\tmailModel: locator.mailModel,\n\t\t\t\t\t\t  })\n\t\t\t\t\t\t: m(BottomNav),\n\t\t\t}),\n\t\t)\n\t}\n\n\tgetViewSlider(): ViewSlider | null {\n\t\treturn this.viewSlider\n\t}\n\n\tprivate renderHeaderRightView(): Children {\n\t\treturn isNewMailActionAvailable()\n\t\t\t? [\n\t\t\t\t\tm(IconButton, {\n\t\t\t\t\t\ttitle: \"newMail_action\",\n\t\t\t\t\t\tclick: () => this.showNewMailDialog().catch(ofClass(PermissionError, noOp)),\n\t\t\t\t\t\ticon: Icons.PencilSquare,\n\t\t\t\t\t}),\n\t\t\t  ]\n\t\t\t: null\n\t}\n\n\tprivate getShortcuts(): Array<Shortcut> {\n\t\treturn [\n\t\t\t...listSelectionKeyboardShortcuts(MultiselectMode.Enabled, () => this.mailViewModel.listModel ?? null),\n\t\t\t{\n\t\t\t\tkey: Keys.N,\n\t\t\t\texec: () => {\n\t\t\t\t\tthis.showNewMailDialog().catch(ofClass(PermissionError, noOp))\n\t\t\t\t},\n\t\t\t\tenabled: () => !!this.mailViewModel.getSelectedFolder() && isNewMailActionAvailable(),\n\t\t\t\thelp: \"newMail_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.DELETE,\n\t\t\t\texec: () => {\n\t\t\t\t\tthis.mailViewModel.listModel && this.deleteMails(this.mailViewModel.listModel.getSelectedAsArray())\n\t\t\t\t},\n\t\t\t\thelp: \"deleteEmails_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.A,\n\t\t\t\texec: () => {\n\t\t\t\t\tthis.mailViewModel.listModel && archiveMails(this.mailViewModel.listModel.getSelectedAsArray())\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\thelp: \"archive_action\",\n\t\t\t\tenabled: () => locator.logins.isInternalUserLoggedIn(),\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.I,\n\t\t\t\texec: () => {\n\t\t\t\t\tthis.mailViewModel.listModel && moveToInbox(this.mailViewModel.listModel.getSelectedAsArray())\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\thelp: \"moveToInbox_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.V,\n\t\t\t\texec: () => {\n\t\t\t\t\tthis.moveMails()\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\thelp: \"move_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.U,\n\t\t\t\texec: () => {\n\t\t\t\t\tthis.mailViewModel.listModel && this.toggleUnreadMails(this.mailViewModel.listModel.getSelectedAsArray())\n\t\t\t\t},\n\t\t\t\thelp: \"toggleUnread_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.ONE,\n\t\t\t\texec: () => {\n\t\t\t\t\tthis.mailViewModel.switchToFolder(MailFolderType.INBOX)\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\thelp: \"switchInbox_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.TWO,\n\t\t\t\texec: () => {\n\t\t\t\t\tthis.mailViewModel.switchToFolder(MailFolderType.DRAFT)\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\thelp: \"switchDrafts_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.THREE,\n\t\t\t\texec: () => {\n\t\t\t\t\tthis.mailViewModel.switchToFolder(MailFolderType.SENT)\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\thelp: \"switchSentFolder_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.FOUR,\n\t\t\t\texec: () => {\n\t\t\t\t\tthis.mailViewModel.switchToFolder(MailFolderType.TRASH)\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\thelp: \"switchTrash_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.FIVE,\n\t\t\t\texec: () => {\n\t\t\t\t\tthis.mailViewModel.switchToFolder(MailFolderType.ARCHIVE)\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\tenabled: () => locator.logins.isInternalUserLoggedIn(),\n\t\t\t\thelp: \"switchArchive_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.SIX,\n\t\t\t\texec: () => {\n\t\t\t\t\tthis.mailViewModel.switchToFolder(MailFolderType.SPAM)\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\tenabled: () => locator.logins.isInternalUserLoggedIn() && !locator.logins.isEnabled(FeatureType.InternalCommunication),\n\t\t\t\thelp: \"switchSpam_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.CTRL,\n\t\t\t\texec: () => false,\n\t\t\t\tenabled: canDoDragAndDropExport,\n\t\t\t\thelp: \"dragAndDrop_action\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: Keys.P,\n\t\t\t\texec: () => {\n\t\t\t\t\tnew Promise(async (resolve) => {\n\t\t\t\t\t\tconst { openPressReleaseEditor } = await import(\"../press/PressReleaseEditor\")\n\t\t\t\t\t\tconst mailboxDetails = await this.mailViewModel.getMailboxDetails()\n\t\t\t\t\t\tmailboxDetails && openPressReleaseEditor(mailboxDetails)\n\t\t\t\t\t\tresolve(null)\n\t\t\t\t\t})\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t\thelp: \"emptyString_msg\",\n\t\t\t\tenabled: () => locator.logins.isEnabled(FeatureType.Newsletter),\n\t\t\t},\n\t\t]\n\t}\n\n\tprivate moveMails() {\n\t\tconst mailList = this.mailViewModel.listModel\n\t\tif (mailList == null) {\n\t\t\treturn\n\t\t}\n\n\t\tconst selectedMails = mailList.getSelectedAsArray()\n\n\t\tshowMoveMailsDropdown(locator.mailModel, getMoveMailBounds(), selectedMails)\n\t}\n\n\tprivate createFolderColumn(editingFolderForMailGroup: Id | null = null, drawerAttrs: DrawerMenuAttrs) {\n\t\treturn new ViewColumn(\n\t\t\t{\n\t\t\t\tview: () => {\n\t\t\t\t\treturn m(FolderColumnView, {\n\t\t\t\t\t\tdrawer: drawerAttrs,\n\t\t\t\t\t\tbutton: editingFolderForMailGroup\n\t\t\t\t\t\t\t? null\n\t\t\t\t\t\t\t: !styles.isUsingBottomNavigation() && isNewMailActionAvailable()\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: \"newMail_action\",\n\t\t\t\t\t\t\t\t\tclick: () => this.showNewMailDialog().catch(ofClass(PermissionError, noOp)),\n\t\t\t\t\t\t\t  }\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\tcontent: this.renderFolders(editingFolderForMailGroup),\n\t\t\t\t\t\tariaLabel: \"folderTitle_label\",\n\t\t\t\t\t})\n\t\t\t\t},\n\t\t\t},\n\t\t\teditingFolderForMailGroup ? ColumnType.Background : ColumnType.Foreground,\n\t\t\tsize.first_col_min_width,\n\t\t\tsize.first_col_max_width,\n\t\t\t() => lang.get(\"folderTitle_label\"),\n\t\t)\n\t}\n\n\tprivate renderFolders(editingFolderForMailGroup: Id | null) {\n\t\tconst details = locator.mailModel.mailboxDetails() ?? []\n\t\treturn [\n\t\t\t...details.map((mailboxDetail) => {\n\t\t\t\t// Only show folders for mailbox in which edit was selected\n\t\t\t\tif (editingFolderForMailGroup && editingFolderForMailGroup != mailboxDetail.mailGroup._id) {\n\t\t\t\t\treturn null\n\t\t\t\t} else {\n\t\t\t\t\treturn m(\n\t\t\t\t\t\tSidebarSection,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tname: () => getMailboxName(locator.logins, mailboxDetail),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tthis.createMailboxFolderItems(mailboxDetail, editingFolderForMailGroup),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}),\n\t\t]\n\t}\n\n\tprivate createMailboxFolderItems(mailboxDetail: MailboxDetail, editingFolderForMailGroup: Id | null = null): Children {\n\t\treturn m(MailFoldersView, {\n\t\t\tmailboxDetail,\n\t\t\texpandedFolders: this.expandedState,\n\t\t\tmailListToSelectedMail: this.mailViewModel.getMailListToSelectedMail(),\n\t\t\tonFolderClick: () => {\n\t\t\t\tif (!editingFolderForMailGroup) {\n\t\t\t\t\tthis.viewSlider.focus(this.listColumn)\n\t\t\t\t}\n\t\t\t},\n\t\t\tonFolderExpanded: (folder, state) => this.setExpandedState(folder, state),\n\t\t\tonShowFolderAddEditDialog: (...args) => this.showFolderAddEditDialog(...args),\n\t\t\tonDeleteCustomMailFolder: (folder) => this.deleteCustomMailFolder(mailboxDetail, folder),\n\t\t\tonFolderDrop: (mailId, folder) => this.handleFolderDrop(mailId, folder),\n\t\t\tinEditMode: editingFolderForMailGroup === mailboxDetail.mailGroup._id,\n\t\t\tonEditMailbox: () => {\n\t\t\t\tEditFoldersDialog.showEdit(() => this.renderFolders(mailboxDetail.mailGroup._id))\n\t\t\t},\n\t\t\tonEditingDone: () => {\n\t\t\t\teditingFolderForMailGroup = null\n\t\t\t},\n\t\t})\n\t}\n\n\tprivate setExpandedState(folder: MailFolder, currentExpansionState: boolean) {\n\t\tcurrentExpansionState ? this.expandedState.delete(getElementId(folder)) : this.expandedState.add(getElementId(folder))\n\t\tdeviceConfig.setExpandedFolders(locator.logins.getUserController().userId, [...this.expandedState])\n\t}\n\n\tprotected onNewUrl(args: Record<string, any>, requestedPath: string) {\n\t\tif (requestedPath.startsWith(\"/mailto\")) {\n\t\t\tif (location.hash.length > 5) {\n\t\t\t\tlet url = location.hash.substring(5)\n\t\t\t\tlet decodedUrl = decodeURIComponent(url)\n\t\t\t\tPromise.all([locator.mailModel.getUserMailboxDetails(), import(\"../editor/MailEditor\")]).then(\n\t\t\t\t\t([mailboxDetails, { newMailtoUrlMailEditor }]) => {\n\t\t\t\t\t\tnewMailtoUrlMailEditor(decodedUrl, false, mailboxDetails)\n\t\t\t\t\t\t\t.then((editor) => editor.show())\n\t\t\t\t\t\t\t.catch(ofClass(CancelledError, noOp))\n\t\t\t\t\t\thistory.pushState(\"\", document.title, window.location.pathname) // remove # from url\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t}\n\t\t} else if (args.action === \"supportMail\" && locator.logins.isGlobalAdminUserLoggedIn()) {\n\t\t\timport(\"../editor/MailEditor\").then(({ writeSupportMail }) => writeSupportMail())\n\t\t}\n\n\t\tif (isApp()) {\n\t\t\tlet userGroupInfo = locator.logins.getUserController().userGroupInfo\n\t\t\tlocator.pushService.closePushNotification(\n\t\t\t\tuserGroupInfo.mailAddressAliases.map((alias) => alias.mailAddress).concat(userGroupInfo.mailAddress || []),\n\t\t\t)\n\t\t}\n\n\t\tthis.mailViewModel.showMail(args.listId, args.mailId)\n\t}\n\n\tprivate async handleFolderDrop(droppedMailId: string, folder: MailFolder) {\n\t\tif (!this.mailViewModel.listModel) {\n\t\t\treturn\n\t\t}\n\t\tlet mailsToMove: Mail[] = []\n\n\t\t// the dropped mail is among the selected mails, move all selected mails\n\t\tif (this.mailViewModel.listModel.isItemSelected(droppedMailId)) {\n\t\t\tmailsToMove = this.mailViewModel.listModel.getSelectedAsArray()\n\t\t} else {\n\t\t\tconst entity = this.mailViewModel.listModel.state.items.find((item) => getElementId(item) === droppedMailId)\n\n\t\t\tif (entity) {\n\t\t\t\tmailsToMove.push(entity)\n\t\t\t}\n\t\t}\n\n\t\tmoveMails({ mailModel: locator.mailModel, mails: mailsToMove, targetMailFolder: folder })\n\t}\n\n\tprivate async showNewMailDialog(): Promise<void> {\n\t\tconst mailboxDetails = await this.mailViewModel.getMailboxDetails()\n\t\tif (mailboxDetails == null) {\n\t\t\treturn\n\t\t}\n\t\tconst { newMailEditor } = await import(\"../editor/MailEditor\")\n\t\tconst dialog = await newMailEditor(mailboxDetails)\n\t\tdialog.show()\n\t}\n\n\tprivate async deleteCustomMailFolder(mailboxDetail: MailboxDetail, folder: MailFolder): Promise<void> {\n\t\tif (folder.folderType !== MailFolderType.CUSTOM) {\n\t\t\tthrow new Error(\"Cannot delete non-custom folder: \" + String(folder._id))\n\t\t}\n\n\t\t// remove any selection to avoid that the next mail is loaded and selected for each deleted mail event\n\t\tthis.mailViewModel?.listModel?.selectNone()\n\n\t\tif (isSpamOrTrashFolder(mailboxDetail.folders, folder)) {\n\t\t\tconst confirmed = await Dialog.confirm(() =>\n\t\t\t\tlang.get(\"confirmDeleteFinallyCustomFolder_msg\", {\n\t\t\t\t\t\"{1}\": getFolderName(folder),\n\t\t\t\t}),\n\t\t\t)\n\t\t\tif (!confirmed) return\n\t\t\tawait locator.mailModel.finallyDeleteCustomMailFolder(folder)\n\t\t} else {\n\t\t\tconst confirmed = await Dialog.confirm(() =>\n\t\t\t\tlang.get(\"confirmDeleteCustomFolder_msg\", {\n\t\t\t\t\t\"{1}\": getFolderName(folder),\n\t\t\t\t}),\n\t\t\t)\n\t\t\tif (!confirmed) return\n\t\t\tawait locator.mailModel.trashFolderAndSubfolders(folder)\n\t\t}\n\t}\n\n\tlogout() {\n\t\tm.route.set(\"/\")\n\t}\n\n\tprivate async toggleUnreadMails(mails: Mail[]): Promise<void> {\n\t\tif (mails.length == 0) {\n\t\t\treturn\n\t\t}\n\t\t// set all selected emails to the opposite of the first email's unread state\n\t\tawait locator.mailModel.markMails(mails, !mails[0].unread)\n\t}\n\n\tprivate deleteMails(mails: Mail[]): Promise<boolean> {\n\t\treturn promptAndDeleteMails(locator.mailModel, mails, noOp)\n\t}\n\n\tprivate async showFolderAddEditDialog(mailGroupId: Id, folder: MailFolder | null, parentFolder: MailFolder | null) {\n\t\tconst mailboxDetail = await locator.mailModel.getMailboxDetailsForMailGroup(mailGroupId)\n\t\tawait showEditFolderDialog(mailboxDetail, folder, parentFolder)\n\t}\n}\n","import { MailFolder, MailTypeRef } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { DropDownSelector, SelectorItemList } from \"../../gui/base/DropDownSelector.js\"\nimport { getFolderName, getIndentedFolderNameForDropdown, getPathToFolderString } from \"../model/MailUtils.js\"\nimport m from \"mithril\"\nimport { TextField } from \"../../gui/base/TextField.js\"\nimport { Dialog } from \"../../gui/base/Dialog.js\"\nimport { locator } from \"../../api/main/MainLocator.js\"\nimport { isOfflineError } from \"../../api/common/utils/ErrorCheckUtils.js\"\nimport { LockedError } from \"../../api/common/error/RestError.js\"\nimport { lang, TranslationKey } from \"../../misc/LanguageViewModel.js\"\nimport { MailboxDetail } from \"../model/MailModel.js\"\nimport { MailFolderType, MailReportType } from \"../../api/common/TutanotaConstants.js\"\nimport { isSameId } from \"../../api/common/utils/EntityUtils.js\"\nimport { reportMailsAutomatically } from \"../view/MailReportDialog.js\"\nimport { isSpamOrTrashFolder } from \"../../api/common/mail/CommonMailUtils.js\"\n\n/**\n * Dialog for Edit and Add folder are the same.\n * @param editFolder if this is null, a folder is being added, otherwise a folder is being edited\n */\nexport async function showEditFolderDialog(mailBoxDetail: MailboxDetail, editFolder: MailFolder | null = null, parentFolder: MailFolder | null = null) {\n\tconst noParentFolderOption = lang.get(\"comboBoxSelectionNone_msg\")\n\tconst mailGroupId = mailBoxDetail.mailGroup._id\n\tlet folderNameValue = editFolder?.name ?? \"\"\n\tlet targetFolders: SelectorItemList<MailFolder | null> = mailBoxDetail.folders\n\t\t.getIndentedList(editFolder)\n\t\t// filter: SPAM and TRASH and descendants are only shown if editing (folders can only be moved there, not created there)\n\t\t.filter((folderInfo) => !(editFolder === null && isSpamOrTrashFolder(mailBoxDetail.folders, folderInfo.folder)))\n\t\t.map((folderInfo) => {\n\t\t\treturn {\n\t\t\t\tname: getIndentedFolderNameForDropdown(folderInfo),\n\t\t\t\tvalue: folderInfo.folder,\n\t\t\t}\n\t\t})\n\ttargetFolders = [{ name: noParentFolderOption, value: null }, ...targetFolders]\n\tlet selectedParentFolder = parentFolder\n\tlet form = () => [\n\t\tm(TextField, {\n\t\t\tlabel: editFolder ? \"rename_action\" : \"folderName_label\",\n\t\t\tvalue: folderNameValue,\n\t\t\toninput: (newInput) => {\n\t\t\t\tfolderNameValue = newInput\n\t\t\t},\n\t\t}),\n\t\tm(DropDownSelector, {\n\t\t\tlabel: \"parentFolder_label\",\n\t\t\titems: targetFolders,\n\t\t\tselectedValue: selectedParentFolder,\n\t\t\tselectedValueDisplay: selectedParentFolder ? getFolderName(selectedParentFolder) : noParentFolderOption,\n\t\t\tselectionChangedHandler: (newFolder: MailFolder | null) => (selectedParentFolder = newFolder),\n\t\t\thelpLabel: () => (selectedParentFolder ? getPathToFolderString(mailBoxDetail.folders, selectedParentFolder) : \"\"),\n\t\t}),\n\t]\n\tconst okAction = async (dialog: Dialog) => {\n\t\t// closing right away to prevent duplicate actions\n\t\tdialog.close()\n\t\ttry {\n\t\t\t// if folder is null, create new folder\n\t\t\tif (editFolder === null) {\n\t\t\t\tawait locator.mailFacade.createMailFolder(folderNameValue, selectedParentFolder?._id ?? null, mailGroupId)\n\t\t\t} else {\n\t\t\t\t// if it is being moved to trash (and not already in trash), ask about trashing\n\t\t\t\tif (selectedParentFolder?.folderType === MailFolderType.TRASH && !isSameId(selectedParentFolder._id, editFolder.parentFolder)) {\n\t\t\t\t\tconst confirmed = await Dialog.confirm(() =>\n\t\t\t\t\t\tlang.get(\"confirmDeleteCustomFolder_msg\", {\n\t\t\t\t\t\t\t\"{1}\": getFolderName(editFolder),\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\tif (!confirmed) return\n\n\t\t\t\t\tawait locator.mailFacade.updateMailFolderName(editFolder, folderNameValue)\n\t\t\t\t\tawait locator.mailModel.trashFolderAndSubfolders(editFolder)\n\t\t\t\t} else if (selectedParentFolder?.folderType === MailFolderType.SPAM && !isSameId(selectedParentFolder._id, editFolder.parentFolder)) {\n\t\t\t\t\t// if it is being moved to spam (and not already in spam), ask about reporting containing emails\n\t\t\t\t\tconst confirmed = await Dialog.confirm(() =>\n\t\t\t\t\t\tlang.get(\"confirmSpamCustomFolder_msg\", {\n\t\t\t\t\t\t\t\"{1}\": getFolderName(editFolder),\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\tif (!confirmed) return\n\n\t\t\t\t\t// get mails to report before moving to mail model\n\t\t\t\t\tconst descendants = mailBoxDetail.folders.getDescendantFoldersOfParent(editFolder._id).sort((l, r) => r.level - l.level)\n\t\t\t\t\tlet reportableMails = await locator.entityClient.loadAll(MailTypeRef, editFolder.mails)\n\t\t\t\t\tfor (const descendant of descendants) {\n\t\t\t\t\t\treportableMails.push(...(await locator.entityClient.loadAll(MailTypeRef, descendant.folder.mails)))\n\t\t\t\t\t}\n\t\t\t\t\tawait reportMailsAutomatically(MailReportType.SPAM, locator.mailModel, mailBoxDetail, reportableMails)\n\n\t\t\t\t\tawait locator.mailFacade.updateMailFolderName(editFolder, folderNameValue)\n\t\t\t\t\tawait locator.mailModel.sendFolderToSpam(editFolder)\n\t\t\t\t} else {\n\t\t\t\t\tawait locator.mailFacade.updateMailFolderName(editFolder, folderNameValue)\n\t\t\t\t\tawait locator.mailFacade.updateMailFolderParent(editFolder, selectedParentFolder?._id || null)\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (isOfflineError(error) || !(error instanceof LockedError)) {\n\t\t\t\tthrow error\n\t\t\t}\n\t\t}\n\t}\n\n\tDialog.showActionDialog({\n\t\ttitle: editFolder ? lang.get(\"editFolder_action\") : lang.get(\"addFolder_action\"),\n\t\tchild: form,\n\t\tvalidator: () => checkFolderName(mailBoxDetail, folderNameValue, mailGroupId, selectedParentFolder?._id ?? null),\n\t\tallowOkWithReturn: true,\n\t\tokAction: okAction,\n\t})\n}\n\nfunction checkFolderName(mailboxDetail: MailboxDetail, name: string, mailGroupId: Id, parentFolderId: IdTuple | null): TranslationKey | null {\n\tif (name.trim() === \"\") {\n\t\treturn \"folderNameNeutral_msg\"\n\t} else if (mailboxDetail.folders.getCustomFoldersOfParent(parentFolderId).some((f) => f.name === name)) {\n\t\treturn \"folderNameInvalidExisting_msg\"\n\t} else {\n\t\treturn null\n\t}\n}\n","import {\n\tCalendarEvent,\n\tConversationEntryTypeRef,\n\tcreateEncryptedMailAddress,\n\tcreateMailAddress,\n\tEncryptedMailAddress,\n\tFile as TutanotaFile,\n\tFileTypeRef,\n\tMail,\n\tMailAddress,\n\tMailTypeRef,\n} from \"../../api/entities/tutanota/TypeRefs.js\"\nimport {\n\tCalendarMethod,\n\tConversationType,\n\tExternalImageRule,\n\tFeatureType,\n\tMailAuthenticationStatus,\n\tMailFolderType,\n\tMailMethod,\n\tmailMethodToCalendarMethod,\n\tMailPhishingStatus,\n\tMailReportType,\n\tMailState,\n\tOperationType,\n} from \"../../api/common/TutanotaConstants\"\nimport { EntityClient } from \"../../api/common/EntityClient\"\nimport { MailboxDetail, MailModel } from \"../model/MailModel\"\nimport { ContactModel } from \"../../contacts/model/ContactModel\"\nimport { ConfigurationDatabase } from \"../../api/worker/facades/lazy/ConfigurationDatabase.js\"\nimport { InlineImages } from \"./MailViewer\"\nimport stream from \"mithril/stream\"\nimport { addAll, assertNonNull, contains, downcast, filterInt, first, neverNull, noOp, ofClass, startsWith } from \"@tutao/tutanota-utils\"\nimport { lang } from \"../../misc/LanguageViewModel\"\nimport {\n\tareParticipantsRestricted,\n\tgetDefaultSender,\n\tgetEnabledMailAddressesWithUser,\n\tgetFolderName,\n\tgetMailboxName,\n\tgetPathToFolderString,\n\tisExcludedMailAddress,\n\tisTutanotaTeamMail,\n\tloadMailDetails,\n\tloadMailHeaders,\n} from \"../model/MailUtils\"\nimport { LoginController } from \"../../api/main/LoginController\"\nimport m from \"mithril\"\nimport { LockedError, NotAuthorizedError, NotFoundError } from \"../../api/common/error/RestError\"\nimport { elementIdPart, getListId, haveSameId, isSameId, listIdPart } from \"../../api/common/utils/EntityUtils\"\nimport { getReferencedAttachments, loadInlineImages, moveMails, revokeInlineImages } from \"./MailGuiUtils\"\nimport { SanitizedFragment } from \"../../misc/HtmlSanitizer\"\nimport { CALENDAR_MIME_TYPE, FileController } from \"../../file/FileController\"\nimport { exportMails } from \"../export/Exporter.js\"\nimport { FileFacade } from \"../../api/worker/facades/lazy/FileFacade.js\"\nimport { IndexingNotSupportedError } from \"../../api/common/error/IndexingNotSupportedError\"\nimport { FileOpenError } from \"../../api/common/error/FileOpenError\"\nimport { Dialog } from \"../../gui/base/Dialog\"\nimport { checkApprovalStatus } from \"../../misc/LoginUtils\"\nimport { formatDateTime, urlEncodeHtmlTags } from \"../../misc/Formatter\"\nimport { UserError } from \"../../api/main/UserError\"\nimport { showUserError } from \"../../misc/ErrorHandlerImpl\"\nimport { CustomerTypeRef, GroupInfo } from \"../../api/entities/sys/TypeRefs.js\"\nimport { LoadingStateTracker } from \"../../offline/LoadingState\"\nimport { ProgrammingError } from \"../../api/common/error/ProgrammingError\"\nimport { InitAsResponseArgs, SendMailModel } from \"../editor/SendMailModel\"\nimport { isOfflineError } from \"../../api/common/utils/ErrorCheckUtils.js\"\nimport { isLegacyMail, MailWrapper } from \"../../api/common/MailWrapper.js\"\nimport { EntityUpdateData, EventController, isUpdateForTypeRef } from \"../../api/main/EventController.js\"\nimport { WorkerFacade } from \"../../api/worker/facades/WorkerFacade.js\"\nimport { SearchModel } from \"../../search/model/SearchModel.js\"\nimport { assertSystemFolderOfType } from \"../../api/common/mail/CommonMailUtils.js\"\n\nexport const enum ContentBlockingStatus {\n\tBlock = \"0\",\n\tShow = \"1\",\n\tAlwaysShow = \"2\",\n\tNoExternalContent = \"3\",\n\tAlwaysBlock = \"4\",\n}\n\nexport class MailViewerViewModel {\n\tprivate mailWrapper: MailWrapper | null = null\n\tprivate contrastFixNeeded: boolean = false\n\n\t// always sanitized in this.sanitizeMailBody\n\tprivate sanitizeResult: SanitizedFragment | null = null\n\tprivate loadingAttachments: boolean = false\n\n\tprivate attachments: TutanotaFile[] = []\n\n\tprivate contentBlockingStatus: ContentBlockingStatus | null = null\n\tprivate errorOccurred: boolean = false\n\tprivate loadedInlineImages: InlineImages | null = null\n\n\t/** only loaded when showFolder is set to true */\n\tprivate folderMailboxText: string | null\n\t/** @see getRelevantRecipient */\n\tprivate relevantRecipient: MailAddress | null = null\n\n\tprivate warningDismissed: boolean = false\n\n\tprivate calendarEventAttachment: {\n\t\tevent: CalendarEvent\n\t\tmethod: CalendarMethod\n\t\trecipient: string\n\t} | null = null\n\n\tprivate readonly loadingState = new LoadingStateTracker()\n\n\tprivate renderIsDelayed: boolean = true\n\n\treadonly loadCompleteNotification = stream<null>()\n\tprivate renderedMail: Mail | null = null\n\n\tprivate loading: Promise<void> | null = null\n\n\tprivate collapsed: boolean = true\n\n\tget mail(): Mail {\n\t\treturn this._mail\n\t}\n\n\tconstructor(\n\t\tprivate _mail: Mail,\n\t\tshowFolder: boolean,\n\t\treadonly entityClient: EntityClient,\n\t\tpublic readonly mailModel: MailModel,\n\t\treadonly contactModel: ContactModel,\n\t\tprivate readonly configFacade: ConfigurationDatabase,\n\t\tprivate readonly fileFacade: FileFacade,\n\t\tprivate readonly fileController: FileController,\n\t\treadonly logins: LoginController,\n\t\tprivate sendMailModelFactory: (mailboxDetails: MailboxDetail) => Promise<SendMailModel>,\n\t\tprivate readonly eventController: EventController,\n\t\tprivate readonly workerFacade: WorkerFacade,\n\t\tprivate readonly searchModel: SearchModel,\n\t) {\n\t\tthis.folderMailboxText = null\n\t\tif (showFolder) {\n\t\t\tthis.showFolder()\n\t\t}\n\t\tthis.eventController.addEntityListener(this.entityListener)\n\t}\n\n\tprivate readonly entityListener = async (events: EntityUpdateData[]) => {\n\t\tfor (const update of events) {\n\t\t\tif (isUpdateForTypeRef(MailTypeRef, update)) {\n\t\t\t\tconst { instanceListId, instanceId, operation } = update\n\t\t\t\tif (operation === OperationType.UPDATE && isSameId(this.mail._id, [instanceListId, instanceId])) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst updatedMail = await this.entityClient.load(MailTypeRef, this.mail._id)\n\t\t\t\t\t\tthis.updateMail({ mail: updatedMail })\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tif (e instanceof NotFoundError) {\n\t\t\t\t\t\t\tconsole.log(`Could not find updated mail ${JSON.stringify([instanceListId, instanceId])}`)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthrow e\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\tprivate async determineRelevantRecipient() {\n\t\t// The idea is that if there are multiple recipients then we should display the one which belongs to one of our mailboxes and then fall back to any\n\t\t// other one\n\t\tconst mailboxDetails = await this.mailModel.getMailboxDetailsForMail(this.mail)\n\t\tif (mailboxDetails == null) {\n\t\t\treturn\n\t\t}\n\t\tconst enabledMailAddresses = new Set(getEnabledMailAddressesWithUser(mailboxDetails, this.logins.getUserController().userGroupInfo))\n\t\tconst mailWrapper = this.mailWrapper\n\t\tif (mailWrapper == null) {\n\t\t\t// we could not load the mail body for some reason\n\t\t\treturn\n\t\t}\n\t\tthis.relevantRecipient =\n\t\t\tmailWrapper.getToRecipients().find((r) => enabledMailAddresses.has(r.address)) ??\n\t\t\tmailWrapper.getCcRecipients().find((r) => enabledMailAddresses.has(r.address)) ??\n\t\t\tmailWrapper.getBccRecipients().find((r) => enabledMailAddresses.has(r.address)) ??\n\t\t\tfirst(mailWrapper.getToRecipients()) ??\n\t\t\tfirst(mailWrapper.getCcRecipients()) ??\n\t\t\tfirst(mailWrapper.getBccRecipients())\n\t\tm.redraw()\n\t}\n\n\tprivate showFolder() {\n\t\tthis.folderMailboxText = null\n\t\tconst folder = this.mailModel.getMailFolder(this.mail._id[0])\n\n\t\tif (folder) {\n\t\t\tthis.mailModel.getMailboxDetailsForMail(this.mail).then((mailboxDetails) => {\n\t\t\t\tif (mailboxDetails == null) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst name = getPathToFolderString(mailboxDetails.folders, folder)\n\t\t\t\tthis.folderMailboxText = `${getMailboxName(this.logins, mailboxDetails)} / ${name}`\n\t\t\t\tm.redraw()\n\t\t\t})\n\t\t}\n\t}\n\n\tdispose() {\n\t\t// currently, the conversation view disposes us twice if our mail is deleted because it's getting disposed itself\n\t\t// (from the list selecting a different element) and because it disposes the mailViewerViewModel that got updated\n\t\t// this silences the warning about leaking entity event listeners when the listener is removed twice.\n\t\tthis.dispose = () => console.log(\"disposed MailViewerViewModel a second time, ignoring\")\n\t\tthis.eventController.removeEntityListener(this.entityListener)\n\t\tconst inlineImages = this.getLoadedInlineImages()\n\t\trevokeInlineImages(inlineImages)\n\t}\n\n\tasync loadAll(\n\t\tdelay: Promise<unknown>,\n\t\t{\n\t\t\tnotify,\n\t\t}: {\n\t\t\tnotify: boolean\n\t\t} = { notify: true },\n\t) {\n\t\tthis.renderIsDelayed = true\n\t\ttry {\n\t\t\tawait this.loading\n\t\t\ttry {\n\t\t\t\tthis.loading = this.loadAndProcessAdditionalMailInfo(this.mail, delay)\n\t\t\t\t\t.then((inlineImageCids) => {\n\t\t\t\t\t\tthis.determineRelevantRecipient()\n\t\t\t\t\t\treturn inlineImageCids\n\t\t\t\t\t})\n\t\t\t\t\t.then((inlineImageCids) => this.loadAttachments(this.mail, inlineImageCids))\n\t\t\t\tawait this.loadingState.trackPromise(this.loading)\n\n\t\t\t\tif (notify) this.loadCompleteNotification(null)\n\t\t\t} catch (e) {\n\t\t\t\tthis.loading = null\n\n\t\t\t\tif (!isOfflineError(e)) {\n\t\t\t\t\tthrow e\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tm.redraw()\n\n\t\t\t// We need the conversation entry in order to reply to the message.\n\t\t\t// We don't want the user to have to wait for it to load when they click reply,\n\t\t\t// So we load it here pre-emptively to make sure it is in the cache.\n\t\t\tthis.entityClient.load(ConversationEntryTypeRef, this.mail.conversationEntry).catch((e) => {\n\t\t\t\tif (e instanceof NotFoundError) {\n\t\t\t\t\tconsole.log(\"could load conversation entry as it has been moved/deleted already\", e)\n\t\t\t\t} else if (isOfflineError(e)) {\n\t\t\t\t\tconsole.log(\"failed to load conversation entry, because of a lost connection\", e)\n\t\t\t\t} else {\n\t\t\t\t\tthrow e\n\t\t\t\t}\n\t\t\t})\n\t\t} finally {\n\t\t\tthis.renderIsDelayed = false\n\t\t}\n\t}\n\n\tisLoading(): boolean {\n\t\treturn this.loadingState.isLoading()\n\t}\n\n\tisConnectionLost(): boolean {\n\t\treturn this.loadingState.isConnectionLost()\n\t}\n\n\tgetAttachments(): Array<TutanotaFile> {\n\t\treturn this.attachments\n\t}\n\n\tgetInlineCids(): Array<string> {\n\t\treturn this.sanitizeResult?.inlineImageCids ?? []\n\t}\n\n\tgetLoadedInlineImages(): InlineImages {\n\t\treturn this.loadedInlineImages ?? new Map()\n\t}\n\n\tisContrastFixNeeded(): boolean {\n\t\treturn this.contrastFixNeeded\n\t}\n\n\tisDraftMail() {\n\t\treturn this.mail.state === MailState.DRAFT\n\t}\n\n\tisReceivedMail() {\n\t\treturn this.mail.state === MailState.RECEIVED\n\t}\n\n\tisLoadingAttachments(): boolean {\n\t\treturn this.loadingAttachments\n\t}\n\n\tgetFolderMailboxText(): string | null {\n\t\treturn this.folderMailboxText\n\t}\n\n\tgetFolderInfo(): { folderType: MailFolderType; name: string } | null {\n\t\tconst folder = this.mailModel.getMailFolder(getListId(this.mail))\n\t\tif (!folder) return null\n\t\treturn { folderType: folder.folderType as MailFolderType, name: getFolderName(folder) }\n\t}\n\n\tgetSubject(): string {\n\t\treturn this.mail.subject\n\t}\n\n\tisConfidential(): boolean {\n\t\treturn this.mail.confidential\n\t}\n\n\tisMailSuspicious(): boolean {\n\t\treturn this.mail.phishingStatus === MailPhishingStatus.SUSPICIOUS\n\t}\n\n\tgetMailId(): IdTuple {\n\t\treturn this.mail._id\n\t}\n\n\tgetSanitizedMailBody(): DocumentFragment | null {\n\t\treturn this.sanitizeResult?.fragment ?? null\n\t}\n\n\tgetMailBody(): string {\n\t\tif (this.mailWrapper) {\n\t\t\treturn this.mailWrapper.getMailBodyText()\n\t\t} else {\n\t\t\treturn \"\"\n\t\t}\n\t}\n\n\tgetDate(): Date {\n\t\treturn this.mail.receivedDate\n\t}\n\n\tgetToRecipients(): Array<MailAddress> {\n\t\tif (this.mailWrapper === null) {\n\t\t\treturn []\n\t\t}\n\t\treturn this.mailWrapper.getToRecipients()\n\t}\n\n\tgetCcRecipients(): Array<MailAddress> {\n\t\tif (this.mailWrapper === null) {\n\t\t\treturn []\n\t\t}\n\t\treturn this.mailWrapper.getCcRecipients()\n\t}\n\n\tgetBccRecipients(): Array<MailAddress> {\n\t\tif (this.mailWrapper === null) {\n\t\t\treturn []\n\t\t}\n\t\treturn this.mailWrapper.getBccRecipients()\n\t}\n\n\t/** Get the recipient which is relevant the most for the current mailboxes. */\n\tgetRelevantRecipient(): MailAddress | null {\n\t\treturn this.relevantRecipient\n\t}\n\n\tgetNumberOfRecipients(): number {\n\t\tif (isLegacyMail(this.mail)) {\n\t\t\treturn this.mail.toRecipients.length + this.mail.ccRecipients.length + this.mail.bccRecipients.length\n\t\t} else {\n\t\t\treturn filterInt(this.mail.recipientCount)\n\t\t}\n\t}\n\n\tgetReplyTos(): Array<EncryptedMailAddress> {\n\t\tif (this.mailWrapper === null) {\n\t\t\treturn []\n\t\t}\n\t\treturn this.mailWrapper.getReplyTos()\n\t}\n\n\tgetSender(): MailAddress {\n\t\treturn this.mail.sender\n\t}\n\n\tgetPhishingStatus(): MailPhishingStatus {\n\t\treturn this.mail.phishingStatus as MailPhishingStatus\n\t}\n\n\tsetPhishingStatus(status: MailPhishingStatus) {\n\t\tthis.mail.phishingStatus = status\n\t}\n\n\tisMailAuthenticated() {\n\t\treturn this.mail.authStatus === MailAuthenticationStatus.AUTHENTICATED\n\t}\n\n\tsetAuthenticationStatus(status: MailAuthenticationStatus) {\n\t\tthis.mail.authStatus = status\n\t}\n\n\tcanCreateSpamRule(): boolean {\n\t\treturn this.logins.isGlobalAdminUserLoggedIn() && !this.logins.isEnabled(FeatureType.InternalCommunication)\n\t}\n\n\tdidErrorsOccur(): boolean {\n\t\tlet bodyErrors = false\n\t\tif (this.mailWrapper) {\n\t\t\tconst mailWrapper = this.mailWrapper\n\t\t\tif (mailWrapper.isLegacy()) {\n\t\t\t\tbodyErrors = typeof mailWrapper.getBody()._errors !== \"undefined\"\n\t\t\t} else {\n\t\t\t\tbodyErrors = typeof downcast(mailWrapper.getDetails().body)._errors !== \"undefined\"\n\t\t\t}\n\t\t}\n\t\treturn this.errorOccurred || typeof this.mail._errors !== \"undefined\" || bodyErrors\n\t}\n\n\tisTutanotaTeamMail(): boolean {\n\t\treturn isTutanotaTeamMail(this.mail)\n\t}\n\n\tisShowingExternalContent(): boolean {\n\t\treturn this.contentBlockingStatus === ContentBlockingStatus.Show || this.contentBlockingStatus === ContentBlockingStatus.AlwaysShow\n\t}\n\n\tisBlockingExternalImages(): boolean {\n\t\treturn this.contentBlockingStatus === ContentBlockingStatus.Block || this.contentBlockingStatus === ContentBlockingStatus.AlwaysBlock\n\t}\n\n\tgetDifferentEnvelopeSender(): string | null {\n\t\treturn this.mail.differentEnvelopeSender\n\t}\n\n\tgetCalendarEventAttachment(): MailViewerViewModel[\"calendarEventAttachment\"] {\n\t\treturn this.calendarEventAttachment\n\t}\n\n\tgetContentBlockingStatus(): ContentBlockingStatus | null {\n\t\treturn this.contentBlockingStatus\n\t}\n\n\tisWarningDismissed() {\n\t\treturn this.warningDismissed\n\t}\n\n\tsetWarningDismissed(dismissed: boolean) {\n\t\tthis.warningDismissed = dismissed\n\t}\n\n\tasync setContentBlockingStatus(status: ContentBlockingStatus): Promise<void> {\n\t\t// We can only be set to NoExternalContent when initially loading the mailbody (_loadMailBody)\n\t\t// so we ignore it here, and don't do anything if we were already set to NoExternalContent\n\t\tif (\n\t\t\tstatus === ContentBlockingStatus.NoExternalContent ||\n\t\t\tthis.contentBlockingStatus === ContentBlockingStatus.NoExternalContent ||\n\t\t\tthis.contentBlockingStatus === status\n\t\t) {\n\t\t\treturn\n\t\t}\n\n\t\tif (status === ContentBlockingStatus.AlwaysShow) {\n\t\t\tthis.configFacade.addExternalImageRule(this.getSender().address, ExternalImageRule.Allow).catch(ofClass(IndexingNotSupportedError, noOp))\n\t\t} else if (status === ContentBlockingStatus.AlwaysBlock) {\n\t\t\tthis.configFacade.addExternalImageRule(this.getSender().address, ExternalImageRule.Block).catch(ofClass(IndexingNotSupportedError, noOp))\n\t\t} else {\n\t\t\t// we are going from allow or block to something else it means we're resetting to the default rule for the given sender\n\t\t\tthis.configFacade.addExternalImageRule(this.getSender().address, ExternalImageRule.None).catch(ofClass(IndexingNotSupportedError, noOp))\n\t\t}\n\n\t\t// We don't check mail authentication status here because the user has manually called this\n\t\tthis.sanitizeResult = await this.sanitizeMailBody(this.mail, status === ContentBlockingStatus.Block || status === ContentBlockingStatus.AlwaysBlock)\n\t\t//follow-up actions resulting from a changed blocking status must start after sanitization finished\n\t\tthis.contentBlockingStatus = status\n\t}\n\n\tasync markAsNotPhishing(): Promise<void> {\n\t\tconst oldStatus = this.getPhishingStatus()\n\n\t\tif (oldStatus === MailPhishingStatus.WHITELISTED) {\n\t\t\treturn\n\t\t}\n\n\t\tthis.setPhishingStatus(MailPhishingStatus.WHITELISTED)\n\n\t\tawait this.entityClient.update(this.mail).catch(() => this.setPhishingStatus(oldStatus))\n\t}\n\n\tasync reportMail(reportType: MailReportType): Promise<void> {\n\t\ttry {\n\t\t\tawait this.mailModel.reportMails(reportType, [this.mail])\n\t\t\tif (reportType === MailReportType.PHISHING) {\n\t\t\t\tthis.setPhishingStatus(MailPhishingStatus.SUSPICIOUS)\n\t\t\t\tawait this.entityClient.update(this.mail)\n\t\t\t}\n\t\t\tconst mailboxDetail = await this.mailModel.getMailboxDetailsForMail(this.mail)\n\t\t\tif (mailboxDetail == null) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst spamFolder = assertSystemFolderOfType(mailboxDetail.folders, MailFolderType.SPAM)\n\t\t\t// do not report moved mails again\n\t\t\tawait moveMails({ mailModel: this.mailModel, mails: [this.mail], targetMailFolder: spamFolder, isReportable: false })\n\t\t} catch (e) {\n\t\t\tif (e instanceof NotFoundError) {\n\t\t\t\tconsole.log(\"mail already moved\")\n\t\t\t} else {\n\t\t\t\tthrow e\n\t\t\t}\n\t\t}\n\t}\n\n\tcanExport(): boolean {\n\t\treturn !this.isAnnouncement() && !this.logins.isEnabled(FeatureType.DisableMailExport)\n\t}\n\n\tcanPrint(): boolean {\n\t\treturn !this.logins.isEnabled(FeatureType.DisableMailExport)\n\t}\n\n\tcanReport(): boolean {\n\t\treturn this.getPhishingStatus() === MailPhishingStatus.UNKNOWN && !this.isTutanotaTeamMail() && this.logins.isInternalUserLoggedIn()\n\t}\n\n\tcanShowHeaders(): boolean {\n\t\treturn this.logins.isInternalUserLoggedIn()\n\t}\n\n\tcanPersistBlockingStatus(): boolean {\n\t\treturn this.searchModel.indexingSupported\n\t}\n\n\tasync exportMail(): Promise<void> {\n\t\tawait exportMails([this.mail], this.entityClient, this.fileController)\n\t}\n\n\tasync getHeaders(): Promise<string | null> {\n\t\t// make sure that the wrapper is loaded\n\t\tconst wrapper = await this.loadMailWrapper()\n\t\treturn loadMailHeaders(this.entityClient, wrapper)\n\t}\n\n\tprivate loadMailWrapper() {\n\t\treturn loadMailDetails(this.entityClient, this.mail)\n\t}\n\n\tisUnread(): boolean {\n\t\treturn this.mail.unread\n\t}\n\n\tsetUnread(unread: boolean) {\n\t\tif (this.mail.unread !== unread) {\n\t\t\tthis.mail.unread = unread\n\n\t\t\tthis.entityClient\n\t\t\t\t.update(this.mail)\n\t\t\t\t.catch(ofClass(LockedError, () => console.log(\"could not update mail read state: \", lang.get(\"operationStillActive_msg\"))))\n\t\t\t\t.catch(ofClass(NotFoundError, noOp))\n\t\t}\n\t}\n\n\tisListUnsubscribe(): boolean {\n\t\treturn this.mail.listUnsubscribe\n\t}\n\n\tisAnnouncement(): boolean {\n\t\treturn isExcludedMailAddress(this.getSender().address)\n\t}\n\n\tasync unsubscribe(): Promise<boolean> {\n\t\tif (!this.isListUnsubscribe()) {\n\t\t\treturn false\n\t\t}\n\n\t\tconst mailHeaders = await this.getHeaders()\n\t\tif (!mailHeaders) {\n\t\t\treturn false\n\t\t}\n\t\tconst unsubHeaders = mailHeaders.split(\"\\n\").filter((headerLine) => headerLine.toLowerCase().startsWith(\"list-unsubscribe\"))\n\t\tif (unsubHeaders.length > 0) {\n\t\t\tconst recipient = await this.getSenderOfResponseMail()\n\t\t\tawait this.mailModel.unsubscribe(this.mail, recipient, unsubHeaders)\n\t\t\treturn true\n\t\t} else {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tprivate getMailboxDetails(): Promise<MailboxDetail | null> {\n\t\treturn this.mailModel.getMailboxDetailsForMail(this.mail)\n\t}\n\n\t/** @return list of inline referenced cid */\n\tprivate async loadAndProcessAdditionalMailInfo(mail: Mail, delayBodyRenderingUntil: Promise<unknown>): Promise<string[]> {\n\t\t// If the mail is a non-draft and we have loaded it before, we don't need to reload it because it cannot have been edited, so we return early\n\t\t// drafts however can be edited, and we want to receive the changes, so for drafts we will always reload\n\t\tif (this.renderedMail != null && haveSameId(mail, this.renderedMail) && mail.state !== MailState.DRAFT && this.sanitizeResult != null) {\n\t\t\treturn this.sanitizeResult.inlineImageCids\n\t\t}\n\n\t\ttry {\n\t\t\tthis.mailWrapper = await this.loadMailWrapper()\n\t\t} catch (e) {\n\t\t\tif (e instanceof NotFoundError) {\n\t\t\t\tconsole.log(\"could load mail body as it has been moved/deleted already\", e)\n\t\t\t\tthis.errorOccurred = true\n\t\t\t\treturn []\n\t\t\t}\n\n\t\t\tif (e instanceof NotAuthorizedError) {\n\t\t\t\tconsole.log(\"could load mail body as the permission is missing\", e)\n\t\t\t\tthis.errorOccurred = true\n\t\t\t\treturn []\n\t\t\t}\n\n\t\t\tthrow e\n\t\t}\n\n\t\tconst externalImageRule = await this.configFacade.getExternalImageRule(mail.sender.address).catch((e) => {\n\t\t\tconsole.log(\"Error getting external image rule:\", e)\n\t\t\treturn ExternalImageRule.None\n\t\t})\n\t\tconst isAllowedAndAuthenticatedExternalSender =\n\t\t\texternalImageRule === ExternalImageRule.Allow && mail.authStatus === MailAuthenticationStatus.AUTHENTICATED\n\t\t// We should not try to sanitize body while we still animate because it's a heavy operation.\n\t\tawait delayBodyRenderingUntil\n\t\tthis.renderIsDelayed = false\n\n\t\tthis.sanitizeResult = await this.sanitizeMailBody(mail, !isAllowedAndAuthenticatedExternalSender)\n\n\t\tthis.checkMailForPhishing(mail, this.sanitizeResult.links)\n\n\t\tthis.contentBlockingStatus =\n\t\t\texternalImageRule === ExternalImageRule.Block\n\t\t\t\t? ContentBlockingStatus.AlwaysBlock\n\t\t\t\t: isAllowedAndAuthenticatedExternalSender\n\t\t\t\t? ContentBlockingStatus.AlwaysShow\n\t\t\t\t: this.sanitizeResult.externalContent > 0\n\t\t\t\t? ContentBlockingStatus.Block\n\t\t\t\t: ContentBlockingStatus.NoExternalContent\n\t\tm.redraw()\n\t\tthis.renderedMail = this.mail\n\t\treturn this.sanitizeResult.inlineImageCids\n\t}\n\n\tprivate async loadAttachments(mail: Mail, inlineCids: string[]): Promise<void> {\n\t\tif (mail.attachments.length === 0) {\n\t\t\tthis.loadingAttachments = false\n\t\t\tm.redraw()\n\t\t} else {\n\t\t\tthis.loadingAttachments = true\n\t\t\tconst attachmentsListId = listIdPart(mail.attachments[0])\n\t\t\tconst attachmentElementIds = mail.attachments.map((attachment) => elementIdPart(attachment))\n\n\t\t\ttry {\n\t\t\t\tconst files = await this.entityClient.loadMultiple(FileTypeRef, attachmentsListId, attachmentElementIds)\n\n\t\t\t\tthis.handleCalendarFile(files, mail)\n\n\t\t\t\tthis.attachments = files\n\t\t\t\tthis.loadingAttachments = false\n\t\t\t\tm.redraw()\n\n\t\t\t\t// We can load any other part again because they are cached but inline images are fileData e.g. binary blobs so we don't cache them like\n\t\t\t\t// entities. So instead we check here whether we need to load them.\n\t\t\t\tif (this.loadedInlineImages == null) {\n\t\t\t\t\tthis.loadedInlineImages = await loadInlineImages(this.fileController, files, inlineCids)\n\t\t\t\t}\n\t\t\t\tm.redraw()\n\t\t\t} catch (e) {\n\t\t\t\tif (e instanceof NotFoundError) {\n\t\t\t\t\tconsole.log(\"could load attachments as they have been moved/deleted already\", e)\n\t\t\t\t} else {\n\t\t\t\t\tthrow e\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate checkMailForPhishing(mail: Mail, links: Array<HTMLElement>) {\n\t\tif (mail.phishingStatus === MailPhishingStatus.UNKNOWN) {\n\t\t\tconst linkObjects = links.map((link) => {\n\t\t\t\treturn {\n\t\t\t\t\thref: link.getAttribute(\"href\") || \"\",\n\t\t\t\t\tinnerHTML: link.innerHTML,\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tthis.mailModel.checkMailForPhishing(mail, linkObjects).then((isSuspicious) => {\n\t\t\t\tif (isSuspicious) {\n\t\t\t\t\tmail.phishingStatus = MailPhishingStatus.SUSPICIOUS\n\n\t\t\t\t\tthis.entityClient\n\t\t\t\t\t\t.update(mail)\n\t\t\t\t\t\t.catch(ofClass(LockedError, (e) => console.log(\"could not update mail phishing status as mail is locked\")))\n\t\t\t\t\t\t.catch(ofClass(NotFoundError, (e) => console.log(\"mail already moved\")))\n\n\t\t\t\t\tm.redraw()\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t}\n\n\t/**\n\t * Check if the list of files contain an iCal file which we can then load and display details for. An calendar notification\n\t * should contain only one iCal attachment so we only process the first matching one.\n\t */\n\tprivate handleCalendarFile(files: Array<TutanotaFile>, mail: Mail): void {\n\t\tconst calendarFile = files.find((a) => a.mimeType && a.mimeType.startsWith(CALENDAR_MIME_TYPE))\n\n\t\tif (calendarFile && (mail.method === MailMethod.ICAL_REQUEST || mail.method === MailMethod.ICAL_REPLY) && mail.state === MailState.RECEIVED) {\n\t\t\tPromise.all([\n\t\t\t\timport(\"../../calendar/date/CalendarInvites\").then(({ getEventFromFile }) => getEventFromFile(calendarFile)),\n\t\t\t\tthis.getSenderOfResponseMail(),\n\t\t\t]).then(([event, recipient]) => {\n\t\t\t\tthis.calendarEventAttachment = event && {\n\t\t\t\t\tevent,\n\t\t\t\t\tmethod: mailMethodToCalendarMethod(downcast(mail.method)),\n\t\t\t\t\trecipient,\n\t\t\t\t}\n\t\t\t\tm.redraw()\n\t\t\t})\n\t\t}\n\t}\n\n\tprivate getSenderOfResponseMail(): Promise<string> {\n\t\treturn this.mailModel.getMailboxDetailsForMail(this.mail).then(async (mailboxDetails) => {\n\t\t\tassertNonNull(mailboxDetails, \"Mail list does not exist anymore\")\n\t\t\tconst myMailAddresses = getEnabledMailAddressesWithUser(mailboxDetails, this.logins.getUserController().userGroupInfo)\n\t\t\tconst addressesInMail: MailAddress[] = []\n\t\t\tconst mailWrapper = await this.loadMailWrapper()\n\t\t\taddressesInMail.push(...mailWrapper.getToRecipients())\n\t\t\taddressesInMail.push(...mailWrapper.getCcRecipients())\n\t\t\taddressesInMail.push(...mailWrapper.getBccRecipients())\n\t\t\taddressesInMail.push(this.mail.sender)\n\t\t\tconst foundAddress = addressesInMail.find((address) => contains(myMailAddresses, address.address.toLowerCase()))\n\t\t\tif (foundAddress) {\n\t\t\t\treturn foundAddress.address.toLowerCase()\n\t\t\t} else {\n\t\t\t\treturn getDefaultSender(this.logins, mailboxDetails)\n\t\t\t}\n\t\t})\n\t}\n\n\t/** @throws UserError */\n\tasync forward(): Promise<void> {\n\t\tconst sendAllowed = await checkApprovalStatus(this.logins, false)\n\t\tif (sendAllowed) {\n\t\t\tconst args = await this.createResponseMailArgsForForwarding([], [], true)\n\t\t\tconst [mailboxDetails, { newMailEditorAsResponse }] = await Promise.all([this.getMailboxDetails(), import(\"../editor/MailEditor\")])\n\t\t\tif (mailboxDetails == null) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Call this again to make sure everything is loaded, including inline images because this can be called earlier than all the parts are loaded.\n\t\t\tawait this.loadAll(Promise.resolve(), { notify: false })\n\t\t\tconst editor = await newMailEditorAsResponse(args, this.isBlockingExternalImages(), this.getLoadedInlineImages(), mailboxDetails)\n\t\t\teditor.show()\n\t\t}\n\t}\n\n\tprivate async createResponseMailArgsForForwarding(\n\t\trecipients: MailAddress[],\n\t\treplyTos: EncryptedMailAddress[],\n\t\taddSignature: boolean,\n\t): Promise<InitAsResponseArgs> {\n\t\tlet infoLine = lang.get(\"date_label\") + \": \" + formatDateTime(this.mail.receivedDate) + \"<br>\"\n\t\tinfoLine += lang.get(\"from_label\") + \": \" + this.getSender().address + \"<br>\"\n\n\t\tif (this.getToRecipients().length > 0) {\n\t\t\tinfoLine +=\n\t\t\t\tlang.get(\"to_label\") +\n\t\t\t\t\": \" +\n\t\t\t\tthis.getToRecipients()\n\t\t\t\t\t.map((recipient) => recipient.address)\n\t\t\t\t\t.join(\", \")\n\t\t\tinfoLine += \"<br>\"\n\t\t}\n\n\t\tif (this.getCcRecipients().length > 0) {\n\t\t\tinfoLine +=\n\t\t\t\tlang.get(\"cc_label\") +\n\t\t\t\t\": \" +\n\t\t\t\tthis.getCcRecipients()\n\t\t\t\t\t.map((recipient) => recipient.address)\n\t\t\t\t\t.join(\", \")\n\t\t\tinfoLine += \"<br>\"\n\t\t}\n\n\t\tconst mailSubject = this.getSubject() || \"\"\n\t\tinfoLine += lang.get(\"subject_label\") + \": \" + urlEncodeHtmlTags(mailSubject)\n\t\tlet body = infoLine + '<br><br><blockquote class=\"tutanota_quote\">' + this.getMailBody() + \"</blockquote>\"\n\t\tconst { prependEmailSignature } = await import(\"../signature/Signature\")\n\t\tconst senderMailAddress = await this.getSenderOfResponseMail()\n\t\treturn {\n\t\t\tpreviousMail: this.mail,\n\t\t\tconversationType: ConversationType.FORWARD,\n\t\t\tsenderMailAddress,\n\t\t\trecipients,\n\t\t\tattachments: this.attachments.slice(),\n\t\t\tsubject: \"FWD: \" + mailSubject,\n\t\t\tbodyText: addSignature ? prependEmailSignature(body, this.logins) : body,\n\t\t\treplyTos,\n\t\t}\n\t}\n\n\tasync reply(replyAll: boolean): Promise<void> {\n\t\tif (this.isAnnouncement()) {\n\t\t\treturn Promise.resolve()\n\t\t}\n\n\t\tconst sendAllowed = await checkApprovalStatus(this.logins, false)\n\n\t\tif (sendAllowed) {\n\t\t\tconst mailboxDetails = await this.mailModel.getMailboxDetailsForMail(this.mail)\n\t\t\tif (mailboxDetails == null) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlet prefix = \"Re: \"\n\t\t\tconst mailSubject = this.getSubject()\n\t\t\tlet subject = mailSubject ? (startsWith(mailSubject.toUpperCase(), prefix.toUpperCase()) ? mailSubject : prefix + mailSubject) : \"\"\n\t\t\tlet infoLine = formatDateTime(this.getDate()) + \" \" + lang.get(\"by_label\") + \" \" + this.getSender().address + \":\"\n\t\t\tlet body = infoLine + '<br><blockquote class=\"tutanota_quote\">' + this.getMailBody() + \"</blockquote>\"\n\t\t\tlet toRecipients: MailAddress[] = []\n\t\t\tlet ccRecipients: MailAddress[] = []\n\t\t\tlet bccRecipients: MailAddress[] = []\n\n\t\t\tif (!this.logins.getUserController().isInternalUser() && this.isReceivedMail()) {\n\t\t\t\ttoRecipients.push(this.getSender())\n\t\t\t} else if (this.isReceivedMail()) {\n\t\t\t\tif (this.getReplyTos().some((address) => !downcast(address)._errors)) {\n\t\t\t\t\taddAll(toRecipients, this.getReplyTos())\n\t\t\t\t} else {\n\t\t\t\t\ttoRecipients.push(this.getSender())\n\t\t\t\t}\n\n\t\t\t\tif (replyAll) {\n\t\t\t\t\tlet myMailAddresses = getEnabledMailAddressesWithUser(mailboxDetails, this.logins.getUserController().userGroupInfo)\n\t\t\t\t\taddAll(\n\t\t\t\t\t\tccRecipients,\n\t\t\t\t\t\tthis.getToRecipients().filter((recipient) => !contains(myMailAddresses, recipient.address.toLowerCase())),\n\t\t\t\t\t)\n\t\t\t\t\taddAll(\n\t\t\t\t\t\tccRecipients,\n\t\t\t\t\t\tthis.getCcRecipients().filter((recipient) => !contains(myMailAddresses, recipient.address.toLowerCase())),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// this is a sent email, so use the to recipients as new recipients\n\t\t\t\taddAll(toRecipients, this.getToRecipients())\n\n\t\t\t\tif (replyAll) {\n\t\t\t\t\taddAll(ccRecipients, this.getCcRecipients())\n\t\t\t\t\taddAll(bccRecipients, this.getBccRecipients())\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst { prependEmailSignature } = await import(\"../signature/Signature.js\")\n\t\t\tconst { newMailEditorAsResponse } = await import(\"../editor/MailEditor\")\n\n\t\t\tawait this.loadAll(Promise.resolve(), { notify: false })\n\t\t\t// It should be there after loadAll() but if not we just give up\n\t\t\tconst inlineImageCids = this.sanitizeResult?.inlineImageCids ?? []\n\n\t\t\tconst [senderMailAddress, referencedCids] = await Promise.all([this.getSenderOfResponseMail(), inlineImageCids])\n\n\t\t\tconst attachmentsForReply = getReferencedAttachments(this.attachments, referencedCids)\n\t\t\ttry {\n\t\t\t\tconst editor = await newMailEditorAsResponse(\n\t\t\t\t\t{\n\t\t\t\t\t\tpreviousMail: this.mail,\n\t\t\t\t\t\tconversationType: ConversationType.REPLY,\n\t\t\t\t\t\tsenderMailAddress,\n\t\t\t\t\t\trecipients: {\n\t\t\t\t\t\t\tto: toRecipients,\n\t\t\t\t\t\t\tcc: ccRecipients,\n\t\t\t\t\t\t\tbcc: bccRecipients,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tattachments: attachmentsForReply,\n\t\t\t\t\t\tsubject,\n\t\t\t\t\t\tbodyText: prependEmailSignature(body, this.logins),\n\t\t\t\t\t\treplyTos: [],\n\t\t\t\t\t},\n\t\t\t\t\tthis.isBlockingExternalImages(),\n\t\t\t\t\tthis.getLoadedInlineImages(),\n\t\t\t\t\tmailboxDetails,\n\t\t\t\t)\n\t\t\t\teditor.show()\n\t\t\t} catch (e) {\n\t\t\t\tif (e instanceof UserError) {\n\t\t\t\t\tshowUserError(e)\n\t\t\t\t} else {\n\t\t\t\t\tthrow e\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async sanitizeMailBody(mail: Mail, blockExternalContent: boolean): Promise<SanitizedFragment> {\n\t\tconst { htmlSanitizer } = await import(\"../../misc/HtmlSanitizer\")\n\t\tconst rawBody = this.getMailBody()\n\t\tconst urlified = await this.workerFacade.urlify(rawBody).catch((e) => {\n\t\t\tconsole.warn(\"Failed to urlify mail body!\", e)\n\t\t\treturn rawBody\n\t\t})\n\t\tconst sanitizeResult = htmlSanitizer.sanitizeFragment(urlified, {\n\t\t\tblockExternalContent,\n\t\t\tallowRelativeLinks: isTutanotaTeamMail(mail),\n\t\t})\n\t\tconst { fragment, inlineImageCids, links, externalContent } = sanitizeResult\n\n\t\t/**\n\t\t * Check if we need to improve contrast for dark theme. We apply the contrast fix if any of the following is contained in\n\t\t * the html body of the mail\n\t\t *  * any tag with a style attribute that has the color property set (besides \"inherit\")\n\t\t *  * any tag with a style attribute that has the background-color set (besides \"inherit\")\n\t\t *  * any font tag with the color attribute set\n\t\t */\n\t\tthis.contrastFixNeeded =\n\t\t\tArray.from(fragment.querySelectorAll(\"*[style]\"), (e) => (e as HTMLElement).style).some(\n\t\t\t\t(s) => (s.color && s.color !== \"inherit\") || (s.backgroundColor && s.backgroundColor !== \"inherit\"),\n\t\t\t) || fragment.querySelectorAll(\"font[color]\").length > 0\n\n\t\tm.redraw()\n\t\treturn {\n\t\t\t// We want to stringify and return the fragment here, because once a fragment is appended to a DOM Node, it's children are moved\n\t\t\t// and the fragment is left empty. If we cache the fragment and then append that directly to the DOM tree when rendering, there are cases where\n\t\t\t// we would try to do so twice, and on the second pass the mail body will be left blank\n\t\t\tfragment,\n\t\t\tinlineImageCids,\n\t\t\tlinks,\n\t\t\texternalContent,\n\t\t}\n\t}\n\n\tprivate async getAssignableMailRecipients(): Promise<GroupInfo[]> {\n\t\tif (this.mail.restrictions != null && this.mail.restrictions.participantGroupInfos.length > 0) {\n\t\t\tconst participantGroupInfos = this.mail.restrictions.participantGroupInfos\n\t\t\tconst customer = await this.entityClient.load(CustomerTypeRef, neverNull(this.logins.getUserController().user.customer))\n\t\t\tconst { loadGroupInfos } = await import(\"../../settings/LoadingUtils\")\n\t\t\tconst groupInfos = await loadGroupInfos(\n\t\t\t\tparticipantGroupInfos.filter((groupInfoId) => {\n\t\t\t\t\treturn neverNull(customer.contactFormUserGroups).list !== groupInfoId[0]\n\t\t\t\t}),\n\t\t\t)\n\t\t\treturn groupInfos.filter((groupInfo) => groupInfo.deleted == null)\n\t\t} else {\n\t\t\treturn []\n\t\t}\n\t}\n\n\tasync assignMail(userGroupInfo: GroupInfo): Promise<boolean> {\n\t\tif (!this.canAssignMails()) {\n\t\t\tthrow new ProgrammingError(\"Cannot assign mails\")\n\t\t}\n\t\tconst recipient = createMailAddress()\n\t\trecipient.address = neverNull(userGroupInfo.mailAddress)\n\t\trecipient.name = userGroupInfo.name\n\t\tlet newReplyTos\n\n\t\tif (this.getReplyTos().length > 0) {\n\t\t\tnewReplyTos = this.getReplyTos()\n\t\t} else {\n\t\t\tnewReplyTos = [createEncryptedMailAddress()]\n\t\t\tnewReplyTos[0].address = this.getSender().address\n\t\t\tnewReplyTos[0].name = this.getSender().name\n\t\t}\n\n\t\tconst args = await this.createResponseMailArgsForForwarding([recipient], newReplyTos, false)\n\t\tconst mailboxDetails = await this.getMailboxDetails()\n\t\tif (mailboxDetails == null) {\n\t\t\treturn false\n\t\t}\n\t\t// Make sure inline images are loaded\n\t\tawait this.loadAll(Promise.resolve(), { notify: false })\n\t\tconst model = await this.sendMailModelFactory(mailboxDetails)\n\t\tawait model.initAsResponse(args, this.getLoadedInlineImages())\n\t\tawait model.send(MailMethod.NONE)\n\t\tconst folders = await this.mailModel.getMailboxFolders(this.mail)\n\t\tif (folders == null) {\n\t\t\treturn false\n\t\t}\n\t\tconst archive = assertSystemFolderOfType(folders, MailFolderType.ARCHIVE)\n\t\treturn moveMails({ mailModel: this.mailModel, mails: [this.mail], targetMailFolder: archive })\n\t}\n\n\tgetNonInlineAttachments(): TutanotaFile[] {\n\t\t// If we have attachments it is safe to assume that we already have body and referenced cids from it\n\t\tconst inlineFileIds = this.sanitizeResult?.inlineImageCids ?? []\n\t\treturn this.attachments.filter((a) => a.cid == null || !inlineFileIds.includes(a.cid))\n\t}\n\n\tasync downloadAll(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.fileController.downloadAll(this.getNonInlineAttachments())\n\t\t} catch (e) {\n\t\t\tif (e instanceof FileOpenError) {\n\t\t\t\tconsole.warn(\"FileOpenError\", e)\n\t\t\t\tawait Dialog.message(\"canNotOpenFileOnDevice_msg\")\n\t\t\t} else {\n\t\t\t\tconsole.error(\"could not open file:\", e.message ?? \"unknown error\")\n\t\t\t\tawait Dialog.message(\"errorDuringFileOpen_msg\")\n\t\t\t}\n\t\t}\n\t}\n\n\tasync downloadAndOpenAttachment(file: TutanotaFile, open: boolean) {\n\t\ttry {\n\t\t\tif (open) {\n\t\t\t\tawait this.fileController.open(file)\n\t\t\t} else {\n\t\t\t\tawait this.fileController.download(file)\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tif (e instanceof FileOpenError) {\n\t\t\t\tconsole.warn(\"FileOpenError\", e)\n\t\t\t\tawait Dialog.message(\"canNotOpenFileOnDevice_msg\")\n\t\t\t} else {\n\t\t\t\tconsole.error(\"could not open file:\", e.message ?? \"unknown error\")\n\t\t\t\tawait Dialog.message(\"errorDuringFileOpen_msg\")\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Special feature for contact forms with shared mailboxes. */\n\tcanAssignMails(): boolean {\n\t\t// do not allow re-assigning from personal mailbox\n\t\treturn (\n\t\t\tthis.logins.getUserController().isInternalUser() &&\n\t\t\tareParticipantsRestricted(this.mail) &&\n\t\t\tthis.logins.getUserController().getUserMailGroupMembership().group !== this.getMailOwnerGroup()\n\t\t)\n\t}\n\n\tcanReplyAll(): boolean {\n\t\treturn (\n\t\t\tthis.logins.getUserController().isInternalUser() &&\n\t\t\tthis.getToRecipients().length + this.getCcRecipients().length + this.getBccRecipients().length > 1 &&\n\t\t\t!areParticipantsRestricted(this.mail)\n\t\t)\n\t}\n\n\tcanForwardOrMove(): boolean {\n\t\treturn this.logins.getUserController().isInternalUser() && !areParticipantsRestricted(this.mail)\n\t}\n\n\tshouldDelayRendering(): boolean {\n\t\treturn this.renderIsDelayed\n\t}\n\n\tisCollapsed(): boolean {\n\t\treturn this.collapsed\n\t}\n\n\texpandMail(delayBodyRendering: Promise<unknown>): void {\n\t\tthis.loadAll(delayBodyRendering, { notify: true })\n\t\tif (this.isUnread()) {\n\t\t\tthis.setUnread(false)\n\t\t}\n\t\tthis.collapsed = false\n\t}\n\n\tcollapseMail(): void {\n\t\tthis.collapsed = true\n\t}\n\n\tasync getAssignmentGroupInfos(): Promise<GroupInfo[]> {\n\t\t// remove the current mailbox/owner from the recipients list.\n\t\tconst userOrMailGroupInfos = await this.getAssignableMailRecipients()\n\t\treturn userOrMailGroupInfos.filter((userOrMailGroupInfo) => {\n\t\t\tif (this.logins.getUserController().getUserMailGroupMembership().group === this.getMailOwnerGroup()) {\n\t\t\t\treturn userOrMailGroupInfo.group !== this.logins.getUserController().userGroupInfo.group && userOrMailGroupInfo.group !== this.mail._ownerGroup\n\t\t\t} else {\n\t\t\t\treturn userOrMailGroupInfo.group !== this.mail._ownerGroup\n\t\t\t}\n\t\t})\n\t}\n\n\tprivate getMailOwnerGroup(): Id | null {\n\t\treturn this.mail._ownerGroup\n\t}\n\n\tprivate updateMail({ mail, showFolder }: { mail: Mail; showFolder?: boolean }) {\n\t\tif (!isSameId(mail._id, this.mail._id)) {\n\t\t\tthrow new ProgrammingError(\n\t\t\t\t`Trying to update MailViewerViewModel with unrelated email ${JSON.stringify(this.mail._id)} ${JSON.stringify(mail._id)} ${m.route.get()}`,\n\t\t\t)\n\t\t}\n\t\tthis._mail = mail\n\n\t\tthis.folderMailboxText = null\n\t\tif (showFolder) {\n\t\t\tthis.showFolder()\n\t\t}\n\n\t\tthis.relevantRecipient = null\n\t\tthis.determineRelevantRecipient()\n\n\t\tthis.loadAll(Promise.resolve(), { notify: true })\n\t}\n}\n","import { ListModel } from \"../../misc/ListModel.js\"\nimport { MailboxDetail, MailModel } from \"../model/MailModel.js\"\nimport { EntityClient } from \"../../api/common/EntityClient.js\"\nimport { Mail, MailFolder, MailTypeRef } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { firstBiggerThanSecond, GENERATED_MAX_ID, getElementId, isSameId, sortCompareByReverseId } from \"../../api/common/utils/EntityUtils.js\"\nimport { assertNotNull, count, debounce, lastThrow, lazyMemoized, memoized, ofClass, promiseFilter } from \"@tutao/tutanota-utils\"\nimport { ListState } from \"../../gui/base/List.js\"\nimport { ConversationPrefProvider, ConversationViewModel, ConversationViewModelFactory } from \"./ConversationViewModel.js\"\nimport { CreateMailViewerOptions } from \"./MailViewer.js\"\nimport { EntityUpdateData, EventController, isUpdateForTypeRef } from \"../../api/main/EventController.js\"\nimport { isOfflineError } from \"../../api/common/utils/ErrorCheckUtils.js\"\nimport { MailFolderType } from \"../../api/common/TutanotaConstants.js\"\nimport { assertSystemFolderOfType, isOfTypeOrSubfolderOf, isSpamOrTrashFolder, isSubfolderOfType } from \"../../api/common/mail/CommonMailUtils.js\"\nimport { mapWith, mapWithout } from \"@tutao/tutanota-utils/dist/CollectionUtils.js\"\nimport { WsConnectionState } from \"../../api/main/WorkerClient.js\"\nimport { WebsocketConnectivityModel } from \"../../misc/WebsocketConnectivityModel.js\"\nimport { ExposedCacheStorage } from \"../../api/worker/rest/DefaultEntityRestCache.js\"\nimport { PreconditionFailedError } from \"../../api/common/error/RestError.js\"\nimport { UserError } from \"../../api/main/UserError.js\"\nimport { ProgrammingError } from \"../../api/common/error/ProgrammingError.js\"\nimport { getMailFilterForType, MailFilterType } from \"../model/MailUtils.js\"\nimport Stream from \"mithril/stream\"\nimport { InboxRuleHandler } from \"../model/InboxRuleHandler.js\"\nimport { Router } from \"../../gui/ScopedRouter.js\"\nimport { ListFetchResult } from \"../../gui/base/ListUtils.js\"\n\nexport interface MailOpenedListener {\n\tonEmailOpened(mail: Mail): unknown\n}\n\n/** ViewModel for the overall mail view. */\nexport class MailViewModel {\n\tprivate _listId: Id | null = null\n\t/** id of the mail we are trying to load based on the URL */\n\tprivate targetMailId: Id | null = null\n\t/** needed to prevent parallel target loads*/\n\tprivate loadingToTargetId: Id | null = null\n\tprivate conversationViewModel: ConversationViewModel | null = null\n\tprivate _filterType: MailFilterType | null = null\n\n\t/**\n\t * We remember the last URL used for each folder so if we switch between folders we can keep the selected mail.\n\t * There's a similar (but different) hacky mechanism where we store last URL but per each top-level view: navButtonRoutes. This one is per folder.\n\t */\n\tprivate mailListToSelectedMail: ReadonlyMap<Id, Id> = new Map()\n\tprivate listStreamSubscription: Stream<unknown> | null = null\n\tprivate conversationPref: boolean = false\n\n\tconstructor(\n\t\tprivate readonly mailModel: MailModel,\n\t\tprivate readonly entityClient: EntityClient,\n\t\tprivate readonly eventController: EventController,\n\t\tprivate readonly connectivityModel: WebsocketConnectivityModel,\n\t\tprivate readonly cacheStorage: ExposedCacheStorage,\n\t\tprivate readonly conversationViewModelFactory: ConversationViewModelFactory,\n\t\tprivate readonly mailOpenedListener: MailOpenedListener,\n\t\tprivate readonly conversationPrefProvider: ConversationPrefProvider,\n\t\tprivate readonly inboxRuleHandler: InboxRuleHandler,\n\t\tprivate readonly router: Router,\n\t\tprivate readonly updateUi: () => unknown,\n\t) {}\n\n\tget filterType(): MailFilterType | null {\n\t\treturn this._filterType\n\t}\n\n\tsetFilter(filter: MailFilterType | null) {\n\t\tthis._filterType = filter\n\t\tthis.listModel?.setFilter(getMailFilterForType(filter))\n\t}\n\n\tasync showMail(listId?: Id, mailId?: Id) {\n\t\t// an optimization to not open an email that we already display\n\t\tif (listId != null && mailId != null && this.conversationViewModel && isSameId(this.conversationViewModel.primaryMail._id, [listId, mailId])) {\n\t\t\treturn\n\t\t}\n\n\t\t// important to set it early enough because setting listId will trigger URL update.\n\t\t// if we don't set this one before setListId, url update will cause this function to be called again but without target mail and we will lose the\n\t\t// target URL\n\t\tthis.targetMailId = typeof mailId === \"string\" ? mailId : null\n\n\t\tlet listIdToUse\n\t\tif (typeof listId === \"string\") {\n\t\t\tconst mailboxDetail = await this.mailModel.getMailboxDetailsForMailListId(listId)\n\t\t\tif (mailboxDetail) {\n\t\t\t\tlistIdToUse = listId\n\t\t\t} else {\n\t\t\t\tlistIdToUse = await this.getListIdForUserInbox()\n\t\t\t}\n\t\t} else {\n\t\t\tlistIdToUse = this._listId ?? (await this.getListIdForUserInbox())\n\t\t}\n\n\t\tawait this.setListId(listIdToUse)\n\n\t\t// if there is a target id and we are not loading for this id already then start loading towards that id\n\t\tif (this.targetMailId && this.targetMailId != this.loadingToTargetId) {\n\t\t\tthis.mailListToSelectedMail = mapWith(this.mailListToSelectedMail, listIdToUse, this.targetMailId)\n\t\t\ttry {\n\t\t\t\tthis.loadingToTargetId = this.targetMailId\n\t\t\t\tawait this.loadAndSelectMail([listIdToUse, this.targetMailId])\n\t\t\t} finally {\n\t\t\t\tthis.loadingToTargetId = null\n\t\t\t\tthis.targetMailId = null\n\t\t\t}\n\t\t} else {\n\t\t\t// update URL if the view was just opened without any url params\n\t\t\t// setListId might not have done it if the list didn't change for us internally but is changed for the view\n\t\t\tif (listId == null) this.updateUrl()\n\t\t}\n\t}\n\n\tprivate async loadAndSelectMail([listId, mailId]: IdTuple) {\n\t\tconst foundMail = await this.listModel?.loadAndSelect(\n\t\t\tmailId,\n\t\t\t() =>\n\t\t\t\t// if we changed the list, stop\n\t\t\t\tthis.getListId() !== listId ||\n\t\t\t\t// if listModel is gone for some reason, stop\n\t\t\t\t!this.listModel ||\n\t\t\t\t// if the target mail has changed, stop\n\t\t\t\tthis.targetMailId !== mailId ||\n\t\t\t\t// if we loaded past the target item we won't find it, stop\n\t\t\t\t(this.listModel.state.items.length > 0 && firstBiggerThanSecond(mailId, getElementId(lastThrow(this.listModel.state.items)))),\n\t\t)\n\t\tif (foundMail == null) {\n\t\t\tconsole.log(\"did not find mail\", listId, mailId)\n\t\t}\n\t}\n\n\tprivate async getListIdForUserInbox(): Promise<Id> {\n\t\tconst mailboxDetail = await this.mailModel.getUserMailboxDetails()\n\t\treturn assertSystemFolderOfType(mailboxDetail.folders, MailFolderType.INBOX).mails\n\t}\n\n\tinit() {\n\t\tthis.singInit()\n\t\tconst conversationEnabled = this.conversationPrefProvider.getConversationViewShowOnlySelectedMail()\n\t\tif (this.conversationViewModel && this.conversationPref !== conversationEnabled) {\n\t\t\tconst mail = this.conversationViewModel.primaryMail\n\t\t\tthis.createConversationViewModel({\n\t\t\t\tmail,\n\t\t\t\tshowFolder: false,\n\t\t\t\tdelayBodyRenderingUntil: Promise.resolve(),\n\t\t\t})\n\t\t\tthis.mailOpenedListener.onEmailOpened(mail)\n\t\t}\n\t\tthis.conversationPref = conversationEnabled\n\t}\n\n\tprivate readonly singInit = lazyMemoized(() => {\n\t\tthis.eventController.addEntityListener((updates) => this.entityEventsReceived(updates))\n\t})\n\n\tget listModel(): ListModel<Mail> | null {\n\t\treturn this._listId ? this._listModel(this._listId) : null\n\t}\n\n\tgetMailListToSelectedMail(): ReadonlyMap<Id, Id> {\n\t\treturn this.mailListToSelectedMail\n\t}\n\n\tgetListId(): Id | null {\n\t\treturn this._listId\n\t}\n\n\tprivate async setListId(id: Id) {\n\t\tif (id === this._listId) {\n\t\t\treturn\n\t\t}\n\t\t// Cancel old load all\n\t\tthis.listModel?.cancelLoadAll()\n\t\tthis._filterType = null\n\n\t\tthis._listId = id\n\t\tthis.listStreamSubscription?.end(true)\n\t\tthis.listStreamSubscription = this.listModel!.stateStream.map((state) => this.onListStateChange(state))\n\t\tawait this.listModel!.loadInitial()\n\t}\n\n\tgetConversationViewModel(): ConversationViewModel | null {\n\t\treturn this.conversationViewModel\n\t}\n\n\tprivate _listModel = memoized((listId: Id) => {\n\t\treturn new ListModel<Mail>({\n\t\t\ttopId: GENERATED_MAX_ID,\n\t\t\tfetch: async (startId, count) => {\n\t\t\t\tconst { complete, items } = await this.loadMailRange(listId, startId, count)\n\t\t\t\tif (complete) {\n\t\t\t\t\tthis.fixCounterIfNeeded(listId, [])\n\t\t\t\t}\n\t\t\t\treturn { complete, items }\n\t\t\t},\n\t\t\tloadSingle: (elementId: Id): Promise<Mail | null> => this.entityClient.load(MailTypeRef, [listId, elementId]),\n\t\t\tsortCompare: sortCompareByReverseId,\n\t\t})\n\t})\n\n\tprivate fixCounterIfNeeded: (listId: Id, itemsWhenCalled: ReadonlyArray<Mail>) => void = debounce(\n\t\t2000,\n\t\tasync (listId: Id, itemsWhenCalled: ReadonlyArray<Mail>) => {\n\t\t\tif (this._filterType != null && this.filterType !== MailFilterType.Unread) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// If folders are changed, list won't have the data we need.\n\t\t\t// Do not rely on counters if we are not connected\n\t\t\tif (this.getListId() !== listId || this.connectivityModel.wsConnection()() !== WsConnectionState.connected) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// If list was modified in the meantime, we cannot be sure that we will fix counters correctly (e.g. because of the inbox rules)\n\t\t\tif (this.listModel?.state.items !== itemsWhenCalled) {\n\t\t\t\tconsole.log(`list changed, trying again later`)\n\t\t\t\treturn this.fixCounterIfNeeded(listId, this.listModel?.state.items ?? [])\n\t\t\t}\n\n\t\t\tconst unreadMailsCount = count(this.listModel.state.items, (e) => e.unread)\n\n\t\t\tconst counterValue = await this.mailModel.getCounterValue(listId)\n\t\t\tif (counterValue != null && counterValue !== unreadMailsCount) {\n\t\t\t\tconsole.log(`fixing up counter for list ${listId}`)\n\t\t\t\tawait this.mailModel.fixupCounterForMailList(listId, unreadMailsCount)\n\t\t\t} else {\n\t\t\t\tconsole.log(`same counter, no fixup on list ${listId}`)\n\t\t\t}\n\t\t},\n\t)\n\n\tprivate onListStateChange(newState: ListState<Mail>) {\n\t\tif (!newState.inMultiselect && newState.selectedItems.size === 1) {\n\t\t\tconst mail = this.listModel!.getSelectedAsArray()[0]\n\t\t\tif (!this.conversationViewModel || !isSameId(this.conversationViewModel?.primaryMail._id, mail._id)) {\n\t\t\t\tthis.mailListToSelectedMail = mapWith(this.mailListToSelectedMail, assertNotNull(this.getListId()), getElementId(mail))\n\n\t\t\t\tthis.createConversationViewModel({\n\t\t\t\t\tmail,\n\t\t\t\t\tshowFolder: false,\n\t\t\t\t})\n\t\t\t\tthis.mailOpenedListener.onEmailOpened(mail)\n\t\t\t}\n\t\t} else {\n\t\t\tthis.conversationViewModel?.dispose()\n\t\t\tthis.conversationViewModel = null\n\t\t\tthis.mailListToSelectedMail = mapWithout(this.mailListToSelectedMail, assertNotNull(this.getListId()))\n\t\t}\n\t\tthis.updateUrl()\n\t\tthis.updateUi()\n\t}\n\n\tprivate updateUrl() {\n\t\tconst listId = this._listId\n\t\tconst mailId = this.targetMailId ?? (listId ? this.getMailListToSelectedMail().get(listId) : null)\n\t\tif (mailId != null) {\n\t\t\tthis.router.routeTo(\"/mail/:listId/:mailId\", { listId, mailId })\n\t\t} else {\n\t\t\tthis.router.routeTo(\"/mail/:listId\", { listId: listId ?? \"\" })\n\t\t}\n\t}\n\n\tprivate createConversationViewModel(viewModelParams: CreateMailViewerOptions) {\n\t\tthis.conversationViewModel?.dispose()\n\t\tthis.conversationViewModel = this.conversationViewModelFactory(viewModelParams)\n\t}\n\n\tasync entityEventsReceived(updates: ReadonlyArray<EntityUpdateData>) {\n\t\tfor (const update of updates) {\n\t\t\tif (isUpdateForTypeRef(MailTypeRef, update) && update.instanceListId === this._listId) {\n\t\t\t\tawait this.listModel?.entityEventReceived(update.instanceId, update.operation)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async loadMailRange(listId: Id, start: Id, count: number): Promise<ListFetchResult<Mail>> {\n\t\ttry {\n\t\t\tconst items = await this.entityClient.loadRange(MailTypeRef, listId, start, count, true)\n\t\t\tconst mailboxDetail = await this.mailModel.getMailboxDetailsForMailListId(listId)\n\t\t\t// For inbox rules there are two points where we might want to apply them. The first one is MailModel which applied inbox rules as they are received\n\t\t\t// in real time. The second one is here, when we load emails in inbox. If they are unread we want to apply inbox rules to them. If inbox rule is\n\t\t\t// applies the email is moved out of inbox and we don't return it here.\n\t\t\tif (mailboxDetail) {\n\t\t\t\tconst mailsToKeepInInbox = await promiseFilter(items, async (mail) => {\n\t\t\t\t\tconst wasMatched = await this.inboxRuleHandler.findAndApplyMatchingRule(mailboxDetail, mail, true)\n\t\t\t\t\treturn !wasMatched\n\t\t\t\t})\n\t\t\t\treturn { items: mailsToKeepInInbox, complete: items.length < count }\n\t\t\t} else {\n\t\t\t\treturn { items, complete: items.length < count }\n\t\t\t}\n\t\t} catch (e) {\n\t\t\t// The way the cache works is that it tries to fulfill the API contract of returning as many items as requested as long as it can.\n\t\t\t// This is problematic for offline where we might not have the full page of emails loaded (e.g. we delete part as it's too old or we move emails\n\t\t\t// around). Because of that cache will try to load additional items from the server in order to return `count` items. If it fails to load them,\n\t\t\t// it will not return anything and instead will throw an error.\n\t\t\t// This is generally fine but in case of offline we want to display everything that we have cached. For that we fetch directly from the cache,\n\t\t\t// give it to the list and let list make another request (and almost certainly fail that request) to show a retry button. This way we both show\n\t\t\t// the items we have and also show that we couldn't load everything.\n\t\t\tif (isOfflineError(e)) {\n\t\t\t\tconst items = await this.cacheStorage.provideFromRange(MailTypeRef, listId, start, count, true)\n\t\t\t\tif (items.length === 0) throw e\n\t\t\t\treturn { items, complete: false }\n\t\t\t} else {\n\t\t\t\tthrow e\n\t\t\t}\n\t\t}\n\t}\n\n\tasync switchToFolder(folderType: Omit<MailFolderType, MailFolderType.CUSTOM>): Promise<void> {\n\t\tconst mailboxDetail = assertNotNull(await this.getMailboxDetails())\n\t\tconst listId = assertSystemFolderOfType(mailboxDetail.folders, folderType).mails\n\t\tawait this.showMail(listId, this.mailListToSelectedMail.get(listId))\n\t}\n\n\tasync getMailboxDetails(): Promise<MailboxDetail> {\n\t\tconst listId = this.getListId()\n\t\treturn await this.mailboxDetailForListWithFallback(listId)\n\t}\n\n\tgetSelectedFolder(): MailFolder | null {\n\t\tconst listId = this.getListId()\n\t\treturn listId ? this.mailModel.getMailFolder(listId) : null\n\t}\n\n\tasync showingDraftsFolder(): Promise<boolean> {\n\t\tif (!this._listId) return false\n\t\tconst mailboxDetail = await this.mailModel.getMailboxDetailsForMailListId(this._listId)\n\t\tconst selectedFolder = this.getSelectedFolder()\n\t\tif (selectedFolder && mailboxDetail) {\n\t\t\treturn isOfTypeOrSubfolderOf(mailboxDetail.folders, selectedFolder, MailFolderType.DRAFT)\n\t\t} else {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tasync showingTrashOrSpamFolder(): Promise<boolean> {\n\t\tconst listId = this._listId\n\t\tif (!listId) return false\n\t\tconst folder = await this.mailModel.getMailFolder(listId)\n\t\tif (!folder) {\n\t\t\treturn false\n\t\t}\n\t\tconst mailboxDetail = await this.mailModel.getMailboxDetailsForMailListId(listId)\n\t\treturn mailboxDetail != null && isSpamOrTrashFolder(mailboxDetail.folders, folder)\n\t}\n\n\tprivate async mailboxDetailForListWithFallback(listId?: string | null) {\n\t\tconst mailboxDetailForListId = typeof listId === \"string\" ? await this.mailModel.getMailboxDetailsForMailListId(listId) : null\n\t\treturn mailboxDetailForListId ?? (await this.mailModel.getUserMailboxDetails())\n\t}\n\n\tasync finallyDeleteAllMailsInSelectedFolder(folder: MailFolder): Promise<void> {\n\t\t// remove any selection to avoid that the next mail is loaded and selected for each deleted mail event\n\t\tthis.listModel?.selectNone()\n\n\t\tconst mailboxDetail = await this.getMailboxDetails()\n\n\t\t// the request is handled a little differently if it is the system folder vs a subfolder\n\t\tif (folder.folderType === MailFolderType.TRASH || folder.folderType === MailFolderType.SPAM) {\n\t\t\treturn this.mailModel.clearFolder(folder).catch(\n\t\t\t\tofClass(PreconditionFailedError, () => {\n\t\t\t\t\tthrow new UserError(\"operationStillActive_msg\")\n\t\t\t\t}),\n\t\t\t)\n\t\t} else if (\n\t\t\tisSubfolderOfType(mailboxDetail.folders, folder, MailFolderType.TRASH) ||\n\t\t\tisSubfolderOfType(mailboxDetail.folders, folder, MailFolderType.SPAM)\n\t\t) {\n\t\t\treturn this.mailModel.finallyDeleteCustomMailFolder(folder).catch(\n\t\t\t\tofClass(PreconditionFailedError, () => {\n\t\t\t\t\tthrow new UserError(\"operationStillActive_msg\")\n\t\t\t\t}),\n\t\t\t)\n\t\t} else {\n\t\t\tthrow new ProgrammingError(`Cannot delete mails in folder ${String(folder._id)} with type ${folder.folderType}`)\n\t\t}\n\t}\n}\n","import { ConversationEntry, ConversationEntryTypeRef, Mail, MailTypeRef } from \"../../api/entities/tutanota/TypeRefs.js\"\nimport { MailViewerViewModel } from \"./MailViewerViewModel.js\"\nimport { CreateMailViewerOptions } from \"./MailViewer.js\"\nimport { elementIdPart, firstBiggerThanSecond, getElementId, getListId, haveSameId, isSameId, listIdPart } from \"../../api/common/utils/EntityUtils.js\"\nimport { assertNotNull, findLastIndex, groupBy, makeSingleUse } from \"@tutao/tutanota-utils\"\nimport { EntityClient } from \"../../api/common/EntityClient.js\"\nimport { LoadingStateTracker } from \"../../offline/LoadingState.js\"\nimport { EntityEventsListener, EntityUpdateData, EventController, isUpdateForTypeRef } from \"../../api/main/EventController.js\"\nimport { ConversationType, MailFolderType, MailState, OperationType } from \"../../api/common/TutanotaConstants.js\"\nimport { NotAuthorizedError, NotFoundError } from \"../../api/common/error/RestError.js\"\nimport { isOfTypeOrSubfolderOf } from \"../../api/common/mail/CommonMailUtils.js\"\nimport { MailModel } from \"../model/MailModel.js\"\n\nexport type MailViewerViewModelFactory = (options: CreateMailViewerOptions) => MailViewerViewModel\n\nexport type MailItem = { type: \"mail\"; viewModel: MailViewerViewModel; entryId: IdTuple }\nexport type ConversationItem = MailItem\n\nexport interface ConversationPrefProvider {\n\tgetConversationViewShowOnlySelectedMail(): boolean\n}\n\nexport type ConversationViewModelFactory = (options: CreateMailViewerOptions) => ConversationViewModel\n\nexport class ConversationViewModel {\n\t/** Primary viewModel is for the mail that was selected from the list. */\n\tprivate readonly _primaryViewModel: MailViewerViewModel\n\tprivate loadingState = new LoadingStateTracker()\n\tprivate loadingPromise: Promise<void> | null = null\n\t/** Is not set until {@link loadConversation is finished. Until it is finished we display primary mail and subject. */\n\tprivate conversation: ConversationItem[] | null = null\n\n\tconstructor(\n\t\tprivate options: CreateMailViewerOptions,\n\t\tprivate readonly viewModelFactory: MailViewerViewModelFactory,\n\t\tprivate readonly entityClient: EntityClient,\n\t\tprivate readonly eventController: EventController,\n\t\tprivate readonly conversationPrefProvider: ConversationPrefProvider,\n\t\tprivate readonly mailModel: MailModel,\n\t\tprivate readonly onUiUpdate: () => unknown,\n\t) {\n\t\tthis._primaryViewModel = viewModelFactory(options)\n\t}\n\n\treadonly init = makeSingleUse((delayBodyRendering: Promise<unknown>) => {\n\t\tthis.loadingPromise = this.loadingState.trackPromise(this.loadConversation())\n\t\tthis.eventController.addEntityListener(this.onEntityEvent)\n\t\tthis._primaryViewModel.expandMail(delayBodyRendering)\n\t})\n\n\tprivate readonly onEntityEvent: EntityEventsListener = async (updates, eventOwnerGroupId) => {\n\t\t// conversation entry can be created when new email arrives\n\t\t// conversation entry can be updated when email is moved around or deleted\n\t\t// conversation entry is deleted only when every email in the conversation is deleted (the whole conversation list will be deleted)\n\t\tfor (const update of updates) {\n\t\t\tif (isUpdateForTypeRef(ConversationEntryTypeRef, update) && update.instanceListId === this.conversationListId()) {\n\t\t\t\tif (this.conversationPrefProvider.getConversationViewShowOnlySelectedMail()) {\n\t\t\t\t\t// no need to handle CREATE because we only show a single item and we don't want to add new ones\n\t\t\t\t\t// no need to handle UPDATE because the only update that can happen is when email gets deleted and then we should be closed from the\n\t\t\t\t\t// outside anyway\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tswitch (update.operation) {\n\t\t\t\t\tcase OperationType.CREATE:\n\t\t\t\t\t\tawait this.processCreateConversationEntry(update)\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase OperationType.UPDATE:\n\t\t\t\t\t\tawait this.processUpdateConversationEntry(update)\n\t\t\t\t\t\tbreak\n\t\t\t\t\t// don't process DELETE because the primary email (selected from the mail list) will be deleted first anyway\n\t\t\t\t\t// and we should be closed when it happens\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async processCreateConversationEntry(update: EntityUpdateData) {\n\t\tconst id: IdTuple = [update.instanceListId, update.instanceId]\n\t\ttry {\n\t\t\tconst entry = await this.entityClient.load(ConversationEntryTypeRef, id)\n\t\t\tif (entry.mail) {\n\t\t\t\ttry {\n\t\t\t\t\t// first wait that we load the conversation, otherwise we might already have the email\n\t\t\t\t\tawait this.loadingPromise\n\t\t\t\t} catch (e) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst conversation = assertNotNull(this.conversation)\n\t\t\t\tif (conversation.some((item) => item.type === \"mail\" && isSameId(item.viewModel.mail.conversationEntry, id))) {\n\t\t\t\t\t// already loaded\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst mail = await this.entityClient.load(MailTypeRef, entry.mail)\n\t\t\t\tlet index = findLastIndex(conversation, (i) => firstBiggerThanSecond(getElementId(entry), elementIdPart(i.entryId)))\n\t\t\t\tif (index < 0) {\n\t\t\t\t\tindex = conversation.length\n\t\t\t\t} else {\n\t\t\t\t\tindex = index + 1\n\t\t\t\t}\n\t\t\t\tconversation.splice(index, 0, { type: \"mail\", viewModel: this.viewModelFactory({ ...this.options, mail }), entryId: entry._id })\n\t\t\t\tthis.onUiUpdate()\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tif (e instanceof NotFoundError) {\n\t\t\t\t// Ignore, something was already deleted\n\t\t\t} else {\n\t\t\t\tthrow e\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async processUpdateConversationEntry(update: EntityUpdateData) {\n\t\ttry {\n\t\t\t// first wait that we load the conversation, otherwise we might already have the email\n\t\t\tawait this.loadingPromise\n\t\t} catch (e) {\n\t\t\treturn\n\t\t}\n\t\tconst conversation = assertNotNull(this.conversation)\n\t\tconst ceId: IdTuple = [update.instanceListId, update.instanceId]\n\t\tlet conversationEntry: ConversationEntry\n\t\tlet mail: Mail | null\n\t\ttry {\n\t\t\tconversationEntry = await this.entityClient.load(ConversationEntryTypeRef, ceId)\n\t\t\tmail =\n\t\t\t\t// ideally checking the `mail` ref should be enough but we sometimes get an update with UNKNOWN and non-existing email but still with the ref\n\t\t\t\tconversationEntry.conversationType !== ConversationType.UNKNOWN && conversationEntry.mail\n\t\t\t\t\t? await this.entityClient.load(MailTypeRef, conversationEntry.mail)\n\t\t\t\t\t: null\n\t\t} catch (e) {\n\t\t\tif (e instanceof NotFoundError) {\n\t\t\t\t// Ignore, something was already deleted\n\t\t\t\treturn\n\t\t\t} else {\n\t\t\t\tthrow e\n\t\t\t}\n\t\t}\n\n\t\tconst oldItemIndex = conversation.findIndex((e) => e.type === \"mail\" && isSameId(e.viewModel.mail.conversationEntry, ceId))\n\t\tif (oldItemIndex === -1) {\n\t\t\treturn\n\t\t}\n\t\tconst oldItem = conversation[oldItemIndex]\n\t\tif (mail && oldItem.type === \"mail\" && haveSameId(oldItem.viewModel.mail, mail)) {\n\t\t\tconsole.log(\"Noop entry update?\", oldItem.viewModel.mail)\n\t\t\t// nothing to do really, why do we get this update again?\n\t\t} else {\n\t\t\tif (oldItem.type === \"mail\") {\n\t\t\t\toldItem.viewModel.dispose()\n\t\t\t}\n\n\t\t\tif (mail) {\n\t\t\t\t// We do not show trashed drafts\n\t\t\t\tif (mail.state === MailState.DRAFT && (await this.isInTrash(mail))) {\n\t\t\t\t\tconversation.splice(oldItemIndex, 1)\n\t\t\t\t} else {\n\t\t\t\t\tconversation[oldItemIndex] = {\n\t\t\t\t\t\ttype: \"mail\",\n\t\t\t\t\t\tviewModel: this.viewModelFactory({ ...this.options, mail }),\n\t\t\t\t\t\tentryId: conversationEntry._id,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// When DELETED conversation status type is added, replace entry with deleted entry instead of splicing out\n\t\t\t\tconversation.splice(oldItemIndex, 1)\n\t\t\t}\n\t\t\tthis.onUiUpdate()\n\t\t}\n\t}\n\n\tprivate conversationListId() {\n\t\treturn listIdPart(this._primaryViewModel.mail.conversationEntry)\n\t}\n\n\tprivate async loadConversation() {\n\t\ttry {\n\t\t\tif (this.conversationPrefProvider.getConversationViewShowOnlySelectedMail()) {\n\t\t\t\tthis.conversation = this.conversationItemsForSelectedMailOnly()\n\t\t\t} else {\n\t\t\t\t// Catch errors but only for loading conversation entries.\n\t\t\t\t// if success, proceed with loading mails\n\t\t\t\t// otherwise do the error handling\n\t\t\t\tthis.conversation = await this.entityClient.loadAll(ConversationEntryTypeRef, listIdPart(this.primaryMail.conversationEntry)).then(\n\t\t\t\t\tasync (entries) => {\n\t\t\t\t\t\tconst allMails = await this.loadMails(entries)\n\t\t\t\t\t\treturn this.createConversationItems(entries, allMails)\n\t\t\t\t\t},\n\t\t\t\t\tasync (e) => {\n\t\t\t\t\t\tif (e instanceof NotAuthorizedError) {\n\t\t\t\t\t\t\t// Most likely the conversation entry list does not exist anymore. The server does not distinguish between the case when the\n\t\t\t\t\t\t\t// list does not exist and when we have no permission on it (and for good reasons, it prevents enumeration).\n\t\t\t\t\t\t\t// Most often it happens when we are not fully synced with the server yet and the primary mail does not even exist.\n\t\t\t\t\t\t\treturn this.conversationItemsForSelectedMailOnly()\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthrow e\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} finally {\n\t\t\tthis.onUiUpdate()\n\t\t}\n\t}\n\n\tprivate createConversationItems(conversationEntries: ConversationEntry[], allMails: Map<Id, Mail>) {\n\t\tconst newConversation: ConversationItem[] = []\n\t\tfor (const c of conversationEntries) {\n\t\t\tconst mail = c.mail && allMails.get(elementIdPart(c.mail))\n\n\t\t\tif (mail) {\n\t\t\t\tnewConversation.push({\n\t\t\t\t\ttype: \"mail\",\n\t\t\t\t\tviewModel: isSameId(mail._id, this.options.mail._id) ? this._primaryViewModel : this.viewModelFactory({ ...this.options, mail }),\n\t\t\t\t\tentryId: c._id,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\treturn newConversation\n\t}\n\n\tprivate async loadMails(conversationEntries: ConversationEntry[]) {\n\t\tconst byList = groupBy(conversationEntries, (c) => c.mail && listIdPart(c.mail))\n\t\tconst allMails: Map<Id, Mail> = new Map()\n\t\tfor (const [listId, conversations] of byList.entries()) {\n\t\t\tif (!listId) continue\n\t\t\tconst loaded = await this.entityClient.loadMultiple(\n\t\t\t\tMailTypeRef,\n\t\t\t\tlistId,\n\t\t\t\tconversations.map((c) => elementIdPart(assertNotNull(c.mail))),\n\t\t\t)\n\n\t\t\tfor (const mail of loaded) {\n\t\t\t\t// If the mail is a draft and is the primary mail, we will show it no matter what\n\t\t\t\t// otherwise, if a draft is in trash we will not show it\n\t\t\t\tif (isSameId(mail._id, this.primaryMail._id) || mail.state !== MailState.DRAFT || !(await this.isInTrash(mail))) {\n\t\t\t\t\tallMails.set(getElementId(mail), mail)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn allMails\n\t}\n\n\tprivate async isInTrash(mail: Mail) {\n\t\tconst mailboxDetail = await this.mailModel.getMailboxDetailsForMail(mail)\n\t\tconst mailFolder = this.mailModel.getMailFolder(getListId(mail))\n\t\treturn mailFolder && mailboxDetail && isOfTypeOrSubfolderOf(mailboxDetail.folders, mailFolder, MailFolderType.TRASH)\n\t}\n\n\tconversationItems(): ReadonlyArray<ConversationItem> {\n\t\treturn this.conversation ?? this.conversationItemsForSelectedMailOnly()\n\t}\n\n\tprivate conversationItemsForSelectedMailOnly(): ConversationItem[] {\n\t\treturn [{ type: \"mail\", viewModel: this._primaryViewModel, entryId: this._primaryViewModel.mail.conversationEntry }]\n\t}\n\n\tget primaryMail(): Mail {\n\t\treturn this._primaryViewModel.mail\n\t}\n\n\tprimaryViewModel(): MailViewerViewModel {\n\t\treturn this._primaryViewModel\n\t}\n\n\tisFinished(): boolean {\n\t\treturn this.loadingState.isIdle()\n\t}\n\n\tisConnectionLost(): boolean {\n\t\treturn this.loadingState.isConnectionLost()\n\t}\n\n\tretry() {\n\t\tif (this.loadingState.isConnectionLost()) {\n\t\t\tthis.loadingState.trackPromise(\n\t\t\t\tthis.loadConversation().then(async () => {\n\t\t\t\t\tconst mails = (this.conversation?.filter((e) => e.type === \"mail\") ?? []) as Array<MailItem>\n\t\t\t\t\tawait Promise.all(mails.map((m) => m.viewModel.loadAll(Promise.resolve())))\n\t\t\t\t}),\n\t\t\t)\n\t\t}\n\t}\n\n\tdispose() {\n\t\tthis.eventController.removeEntityListener(this.onEntityEvent)\n\t\tfor (const item of this.conversationItems()) {\n\t\t\tif (item.type === \"mail\") {\n\t\t\t\titem.viewModel.dispose()\n\t\t\t}\n\t\t}\n\t}\n}\n"],"names":["async","reportMailsAutomatically","mailReportType","mailModel","mailboxDetails","mails","mailboxProperties","getMailboxProperties","mailboxGroupRoot","allowUndoing","isReportable","reportMovedMails","Promise","resolve","shallRememberDecision","updateSpamReportSetting","areMailsReported","saveReportMovedMails","dialog","close","yesButton","label","click","type","noButton","Dialog","confirmMultiple","lang","get","m","Checkbox","checked","onChecked","v","helpLabel","confirmMailReportDialog","undoClicked","showSnackBar","message","button","onClose","reportMails","showDeleteConfirmationDialog","trashMails","moveMails","mail","folder","locator","getMailFolder","_id","mailboxDetail","getMailboxDetailsForMailListId","getListId","isSpamOrTrashFolder","folders","push","confirmationTextId","length","confirm","promptAndDeleteMails","onConfirm","then","confirmed","deleteMails","catch","e","PreconditionFailedError","LockedError","targetMailFolder","details","system","isOfTypeOrSubfolderOf","MailFolderType","SPAM","reportableMails","map","reportableMail","createMail","getElementId","getMailboxDetailsForMailGroup","assertNotNull","_ownerGroup","archiveMails","getMailboxFolders","assertSystemFolderOfType","ARCHIVE","moveToInbox","INBOX","replaceCidsWithInlineImages","dom","inlineImages","onContext","imageElements","Array","from","querySelectorAll","shadowRoot","shadowImageElements","elementsWithCid","forEach","imageElement","cid","getAttribute","inlineImage","setAttribute","objectUrl","classList","remove","isApp","timeoutId","startCoords","addEventListener","touch","touches","x","clientX","y","clientY","setTimeout","Math","abs","clearTimeout","isDesktop","preventDefault","createInlineImageReference","file","blob","Blob","data","mimeType","URL","createObjectURL","revokeInlineImages","k","revokeObjectURL","getReferencedAttachments","attachments","referencedCids","filter","find","rcid","showMoveMailsDropdown","model","origin","opts","width","withBackground","onSelected","noOp","getMoveTargetFolderSystems","folderButtons","f","getIndentedFolderNameForDropdown","icon","getFolderIcon","size","dropdown","Dropdown","setOrigin","DomRectReadOnlyPolyfilled","left","top","height","modal","displayUnique","getConversationTitle","conversationViewModel","isFinished","numberOfEmails","conversationItems","getMoveMailBounds","hpad_large","vpad_large","newMap","Map","set","random","toString","substring","domClone","cloneNode","removeAttribute","sendMailModel","viewModel","eventController","dispose","saveStatus","closeOverlayFunction","minimizedEditor","minimizeMailEditor","overlayDom","resizeListener","style","transform","px","getVerticalOverlayPosition","windowFacade","addResizeListener","displayOverlay","bottom","MINIMIZED_EDITOR_HEIGHT","right","styles","isUsingBottomNavigation","hpad","hpad_medium","isSingleColumnLayout","MINIMIZED_OVERLAY_WIDTH_SMALL","MINIMIZED_OVERLAY_WIDTH_WIDE","zIndex","view","MinimizedEditorOverlay","removeResizeListener","showMinimizedEditorOverlay","DefaultAnimationTime","ev","handler","showFileChooser","ALLOWED_IMAGE_FORMATS","files","tooBig","MAX_BASE64_IMAGE_SIZE","b64","uint8ArrayToBase64","dataUrlString","insertImage","COUNTER_POS_OFFSET","constructor","vnode","attrs","this","_eventController","_listener","updates","eventOwnerGroupId","promiseMap","update","isUpdateForTypeRef","MailTypeRef","operation","draft","getDraft","isSameId","instanceListId","instanceId","removeMinimizedEditor","addEntityListener","onremove","removeEntityListener","subject","getSubject","CounterBadge","count","getMinimizedEditors","indexOf","position","color","theme","navigation_button_icon","background","getNavButtonIconBackground","onclick","reopenMinimizedEditor","getStatusMessage","Button","colors","_onDeleteClicked","status","reason","assertMainOrNode","button_height","vpad_xs","bottomInset","getSafeAreaInsetBottom","bottom_nav_bar","iconMap","CUSTOM","FontIcons","Folder","Inbox","SENT","Sent","TRASH","Trash","Archive","Spam","DRAFT","Draft","shiftByForCheckbox","translateXHide","translateXShow","MailRow","showFolderIcon","domElement","entity","checkboxWasVisible","shouldAlwaysShowMultiselectCheckbox","folderIconsDom","selected","isInMultiSelect","selectionSetter","checkboxDom","iconsDom","textContent","iconsText","dateDom","formatTimeOrDateOrYesterday","receivedDate","senderDom","getSenderOrRecipientHeading","subjectDom","NBSP","unread","unreadDom","add","setVisibility","teamLabelDom","isTutanotaTeamMail","showCheckboxAnimated","checkboxOpacity","show","shouldShowCheckbox","paddingRight","showTranslateTransform","senderAnim","animate","selectableRowAnimParams","subjectAnim","badgeAnim","checkboxAnim","scaleXHide","scaleXShow","all","finished","showCheckbox","cancel","hideTranslateTransform","translate","scale","padding","disabled","tabIndex","render","SelectableRowContainer","onSelectedChangeRef","changer","oncreate","marginBottom","transformOrigin","stopPropagation","onchange","marginTop","marginLeft","checkbox_size","Badge","classes","iconText","folderIcon","getMailFolderType","_errors","Warning","state","Edit","replyType","Reply","Forward","confidential","Confidential","Attach","makeMailBundle","entityClient","fileController","sanitizer","mailWrapper","loadMailDetails","body","sanitizeHTML","getMailBodyText","blockExternalContent","allowRelativeLinks","usePlaceholderForInlineImages","html","fileId","load","FileTypeRef","getAsDataFile","headers","loadMailHeaders","recipientMapper","address","name","mailId","getLetId","sender","to","getToRecipients","cc","getCcRecipients","bcc","getBccRecipients","replyTo","getReplyTos","isDraft","isRead","sentOn","getSentDate","getTime","receivedOn","generateMailFile","bundle","fileName","mode","stringToUtf8Uint8Array","lines","filteredHeaders","split","line","match","join","formatRecipients","key","recipients","recipient","replace","trim","date","monthNames","getUTCDay","getUTCDate","getUTCMonth","getUTCFullYear","pad","getUTCHours","getUTCMinutes","getUTCSeconds","_formatSmtpDateTime","Date","bodyLine","breakIntoLines","stringToBase64","attachment","base64Filename","fileContentLines","getCleanedMimeType","fileLine","mailToEml","createDataFile","mailToEmlFile","fileApp","mailToMsg","getMailExportMode","ConfigKeys","module","import","mailExportMode","desktopSettingsFacade","getStringConfigValue","DesktopConfigKey","generateExportFileName","filename","formatSortableDateTime","sanitizeFilename","exportMails","downloadPromise","htmlSanitizer","bundles","zipName","sortableTimestamp","zipDataFiles","outputFile","saveDataFile","string","exports","MailListView","showingSpamOrTrash","showingDraft","renderConfig","itemHeight","list_row_height","multiselectionAllowed","createElement","mailRow","_a","mailViewModel","listModel","onSingleExclusiveSelection","swipe","logins","isInternalUserLoggedIn","renderLeftSpacer","renderRightSpacer","swipeLeft","listElement","onSwipeLeft","swipeRight","onSwipeRight","dragStart","event","row","_newDragStart","listId","exportedMails","_listDom","showingTrashOrSpamFolder","result","redraw","showingDraftsFolder","bind","getRecoverFolder","mailUnderCursor","isExportDragEvent","draggedMails","has","_doExportDrag","isDesktopLayout","neverNull","dataTransfer","setData","_dragStart","haveSameId","slice","document","cursor","mouseupPromise","once","filePathsPromise","_prepareMailsForDrag","didComplete","fileNames","race","filePaths","startNativeDrag","desktopSystemFacade","focusApplicationWindow","exportMode","progressMonitor","makeTrackedProgressMonitor","progressTracker","workDone","mapKey","notDownloaded","downloaded","handleNotDownloaded","handleDownloaded","promise","existing","checkFileExistsInExportDir","deduplicatedNames","deduplicateFilenames","Set","newFiles","existingFiles","shift","saveToExportDir","AsyncResult","concat","getSelectedFolder","purgeButtonAttrs","onClearFolder","onKeyDown","isDragAndDropModifierHeld","onKeyUp","downcast","firstChild","canDoDragAndDropExport","onbeforeremove","removeEventListener","ListColumnWrapper","headerContent","renderListHeader","isEmptyAndDone","ColumnEmptyMessageBox","list_message_bg","List","onLoadMore","loadMore","onRetryLoading","retryLoading","onSingleSelection","item","onSingleTogglingMultiselection","onSingleInclusiveSelection","onRangeSelectionTowards","selectRangeTowards","onStopLoading","stopLoading","targetInbox","selectedFolder","folderType","selectNone","getSystemFolderByType","Icon","ctrlKey","altKey","includes","toLowerCase","MultiItemViewer","selectedEntities","getSelectionMessage","content_message_bg","backgroundColor","navigation_bg","bottomContent","renderEmptyMessageButtons","loadingAll","stopLoadAll","loadAll","progressIcon","getMailSelectionMessage","nbrOfSelectedMails","showHeaderDialog","headersPromise","mailHeadersDialog","closeHeadersAction","largeDialog","middle","addShortcut","Keys","ESC","exec","help","setCloseHandler","editDraft","checkApprovalStatus","minimizedMailModel","getEditorForDraft","newMailEditorFromDraft","getMailboxDetailsForMail","n","M","getAttachments","isBlockingExternalImages","getLoadedInlineImages","UserError","showUserError","makeAssignMailsButtons","getAssignmentGroupInfos","userOrMailGroupInfo","getMailAddressDisplayText","mailAddress","assignMail","mailViewerMoreActions","showReadButton","moreButtons","isUnread","setUnread","client","isMobileDevice","canExport","showProgressDialog","exportMail","window","print","canPrint","isListUnsubscribe","unsubscribe","success","canShowHeaders","getHeaders","canReport","sendReport","reportType","reportMail","ofClass","finally","showActionDialog","title","child","ifAllowedTutanotaLinks","link","href","target","okAction","canPersistBlockingStatus","isShowingExternalContent","setContentBlockingStatus","MobileMailActionBar","actions","isAnnouncement","placeholder","deleteButton","moreButton","isDraftMail","moveButton","editButton","canForwardOrMove","replyButton","forwardButton","canAssignMails","assignButton","IconButton","getBoundingClientRect","dropdownWidth","offsetWidth","DROPDOWN_MARGIN","undefined","createDropdown","lazyButtons","forward","canReplyAll","buttons","reply","domRect","_c","_b","createAsyncDropdown","MailFolderRow","hovered","rightButton","expanded","indentationLevel","hasChildren","editMode","indentationMargin","paddingNeeded","hpad_button","buttonWidth","icon_size_large","isNavButtonSelected","stateBgHover","onmouseenter","onmouseleave","fill","navigation_button_selected","navigation_button","class","renderHierarchyLine","paddingLeft","onExpanderClick","large","NavButton","onfocus","onblur","marginRight","showFullCount","numberOfPreviousRows","isLastSibling","onSelectedPath","border","content_border","verticalOffsetInsideRow","verticalOffsetForParent","lengthOfHorizontalLine","leftOffset","borderBottomLeftRadius","borderLeft","borderBottom","MailFoldersView","groupCounters","mailboxCounters","mailGroup","customSystems","customSubtrees","systemSystems","systemSubtrees","children","getIndentedList","isSelectedPrefix","MAIL_PREFIX","path","getPathToFolder","systemChildren","renderFolderTree","SidebarSection","inEditMode","renderCreateFolderAddButton","renderEditFoldersButton","renderAddFolderButtonRow","subSystems","numRows","id","getFolderName","route","mailListToSelectedMail","onFolderClick","dropHandler","droppedMailId","onFolderDrop","disableHoverBackground","currentExpansionState","expandedFolders","summedCount","getTotalFolderCounter","childResult","fragment","createFolderMoreButton","min","MAX_FOLDER_INDENT_LEVEL","onFolderExpanded","last","onShowFolderAddEditDialog","counters","reduce","acc","attachDropdown","mainButtonAttrs","childAttrs","editButtonAttrs","deleteButtonAttrs","addButtonAttrs","onDeleteCustomMailFolder","parentFolder","getFolderById","onEditMailbox","EditFoldersDialog","folderList","_domDialog","focusedBeforeShown","_closeHandler","usedBottomNavBefore","visible","_shortcuts","RETURN","TAB","focusPrevious","focusNext","navbar_height","first_col_max_width","animation","bgcolor","opacity","animations","alpha","delay","requestAnimationFrame","activeElement","blur","defaultFocusOnLoad","onscroll","borderTop","inputs","INPUT","focus","querySelector","hideAnimation","getElevatedBackground","easing","ease","linear","display","shortcuts","backgroundClick","popState","shortcut","keyManager","registerModalShortcuts","static","EventBanner","ReplyButtons","LazyLoaded","E","InfoBanner","getMessage","a9","showEventDetails","method","ownAttendee","findAttendeeInAddresses","attendees","CalendarMethod","REQUEST","isRepliedTo","CalendarAttendeeStatus","NEEDS_ACTION","isLoaded","getLoaded","setParticipation","previousMail","getLatestEvent","replyToEventInvitation","latestEvent","sendResponse","reload","REPLY","MailViewerHeader","detailsExpanded","filesExpanded","dateTime","formatDateWithWeekday","formatTime","dateTimeFull","formatDateWithWeekdayAndYear","renderSubjectActionsLine","renderFolderText","renderAddressesAndDate","ExpanderPanel","renderDetails","bubbleMenuWidth","renderAttachments","renderConnectionLostBanner","renderEventBanner","renderBanners","folderInfo","getFolderInfo","getFolderIconByType","getFolderMailboxText","container","content_button","responsiveCardHMargin","role","String","tabindex","onkeydown","isKeyPressed","keyCode","SPACE","getSenderAddressDisplay","getRecipientEmailAddress","isConfidential","hoverText","makeSubjectActionsLineClasses","collapseMail","hasAttribute","renderUnreadDot","tutaoBadge","paddingTop","isCollapsed","renderPhishingWarning","renderHardAuthenticationFailWarning","renderSoftAuthenticationFailWarning","renderExternalContentBanner","Boolean","isConnectionLost","getCalendarEventAttachment","createMailAddressContextButtons","envelopeSender","getDifferentEnvelopeSender","responsiveCardHPadding","liveDataAttrs","RecipientButton","getSender","defaultInboxRuleField","info","center","bold","createContact","flex","isLoadingAttachments","getNonInlineAttachments","attachmentCount","totalAttachmentSize","Number","renderAttachmentContainer","formatStorageSize","content_fg","isIOSApp","downloadAll","AttachmentBubble","download","isAndroidApp","downloadAndOpenAttachment","open","isMailSuspicious","helpLink","canSeeTutanotaLinks","markAsNotPhishing","isWarningDismissed","authStatus","MailAuthenticationStatus","HARD_FAIL","setWarningDismissed","SOFT_FAIL","differentEnvelopeSender","getContentBlockingStatus","showButton","alwaysOrNeverAllowButtons","isMailAuthenticated","isNotNull","maybeDropdownButtons","createMoreSecondaryButtonAttrs","prepareMoreActions","actionButtons","_","relevantRecipient","getRelevantRecipient","numberOfAllRecipients","getNumberOfRecipients","minWidth","MailViewer","bodyLineHeight","isScaling","lastBodyTouchEndTime","lastTouchStart","time","now","delayProgressSpinner","scrollDom","domBodyDeferred","defer","domBody","shadowDomRoot","currentlyRenderedMailBody","lastContentBlockingStatus","loadAllListener","stream","currentQuoteBehavior","quoteState","setViewModel","isPrimary","updateLineHeight","setupShortcuts","registerShortcuts","clearDomBody","unregisterShortcuts","oldViewModel","end","loadCompleteNotification","sync","replaceInlineImages","handleContentBlockingOnRender","renderMailHeader","renderMailSubject","isContrastFixNeeded","renderMailBodySection","renderQuoteExpanderButton","onupdate","borderRadius","list_border","content_bg","ToggleButton","toggledTitle","toggled","shouldDisplayCollapsedQuotes","onToggled","updateCollapsedQuotes","button_height_compact","onbeforeupdate","isLoading","didErrorsOccur","IconMessageBox","sanitizedMailBody","getSanitizedMailBody","shouldDelayRendering","renderMailBody","renderLoadingIcon","setDomBody","rescale","renderShadowMailBody","defaultQuoteBehavior","onsubmit","line_height","showQuote","quotes","quoteWrap","assertNonNull","wrapNode","className","lineHeight","appendChild","quoteElements","quote","createCollapsedBlockQuote","closest","handleDoubleTap","handleAnchorClick","getStyleSheetElement","replaceWith","quoteIndicator","navigation_menu_icon","attachShadow","getRootNode","eventTargetWithKeyboardInput","loadedInlineImages","inlineAttachment","coords","getCoordsOfMouseOrTouchEvent","showDropdownAtPosition","containerWidth","scrollWidth","heightDiff","scrollHeight","transition","userController","getUserController","enabled","H","R","isInternalUser","F","line_height_l","line_height_m","args","copyToClipboard","isEnabled","FeatureType","DisableContacts","isFullyLoggedIn","contact","contactModel","searchForContact","contactId","contactListId","i","ContactEditor","createNewContact","user","InternalCommunication","rule","getExistingRuleForType","props","createInboxRuleTemplate","c","canCreateSpamRule","addSpamRule","singleClickAction","doubleClickAction","lastClick","changedTouches","cancelable","spamRuleType","SpamRuleType","WHITELIST","BLACKLIST","spamRuleField","A","showAddSpamRuleDialog","createEmailSenderListElement","value","field","shouldDispatchSyntheticClick","startsWith","isNewMailActionAvailable","newMailtoUrlMailEditor","defaultUnconfidential","editor","CancelledError","isSettingsLink","newRoute","syntheticTag","newClickEvent","MouseEvent","dispatchEvent","HTMLElement","matches","CollapsedMailView","expandMail","onkeyup","renderIcon","getMailAddressDisplayClasses","conversationCardMargin","ConversationViewer","containerDom","didScroll","lastItems","PAGE_UP","scrollUp","PAGE_DOWN","scrollDown","HOME","scrollToTop","END","scrollToBottom","delayBodyRendering","init","doScroll","console","log","renderItems","renderLoadingState","renderFooter","offsetHeight","navbar_height_mobile","entries","entry","primaryViewModel","renderViewer","retry","elementIdPart","conversationEntry","items","conversationId","primaryMail","itemIndex","findIndex","entryId","childDom","childNodes","parentTop","scrollTo","scrollBy","clientHeight","behavior","MailViewerActions","renderSingleMailActions","mailViewerViewModel","renderActions","renderMoreButton","renderDeleteButton","renderMoveButton","renderReadButton","isMovingMailsAllowed","renderExportButton","renderEditButton","renderReplyButton","renderForwardButton","renderAssignButton","markAction","markMails","markReadButton","markUnreadButton","isExportingMailsAllowed","MobileMailMultiselectionActionBar","MobileBottomActionBar","referenceDom","MailFilterButton","showDropdown","setFilter","MailFilterType","Unread","Read","WithAttachments","BaseTopLevelView","getConversationViewModel","super","countersStream","mailboxSubscription","expandedState","deviceConfig","getExpandedFolders","userId","cache","folderColumn","createFolderColumn","drawerAttrs","listColumn","ViewColumn","BackgroundColumnLayout","desktopToolbar","DesktopListToolbar","SelectAllCheckbox","selectionAttrsForList","renderFilterButton","columnLayout","inMultiselect","viewSlider","mailColumn","warn","finallyDeleteAllMailsInSelectedFolder","mobileHeader","MultiselectMobileHeader","getSelectedAsArray","MobileHeader","header","getTitle","columnType","EnterMultiselectIconButton","clickAction","enterMultiselect","primaryAction","renderHeaderRightView","backAction","focusPreviousColumn","second_col_min_width","second_col_max_width","renderSingleMailViewer","renderMultiMailViewer","third_col_min_width","third_col_max_width","ViewSlider","focusedColumn","columns","getShortcuts","conversationViewPreference","getConversationViewShowOnlySelectedMail","cancelLoadAll","filterType","mailViewerSingleActions","DesktopViewerToolbar","multicolumnActions","waitForAnimation","mailViewerMultiActions","_d","loadingStatus","ListLoadingState","Done","ondragover","ondrop","getMailboxDetails","readLocalFiles","S","mailbox","dataFiles","appendEmailSignature","newMailEditorFromTemplate","PermissionError","Header","rightView","searchBar","LazySearchBar","bottomNav","BottomNav","getViewSlider","showNewMailDialog","listSelectionKeyboardShortcuts","N","DELETE","I","V","U","toggleUnreadMails","ONE","switchToFolder","TWO","THREE","FOUR","FIVE","SIX","CTRL","P","openPressReleaseEditor","Newsletter","mailList","selectedMails","editingFolderForMailGroup","FolderColumnView","drawer","content","renderFolders","ariaLabel","first_col_min_width","getMailboxName","createMailboxFolderItems","getMailListToSelectedMail","setExpandedState","showFolderAddEditDialog","deleteCustomMailFolder","handleFolderDrop","showEdit","onEditingDone","delete","setExpandedFolders","onNewUrl","requestedPath","location","hash","url","decodedUrl","decodeURIComponent","getUserMailboxDetails","history","pushState","pathname","action","isGlobalAdminUserLoggedIn","writeSupportMail","userGroupInfo","pushService","closePushNotification","mailAddressAliases","alias","showMail","mailsToMove","isItemSelected","newMailEditor","Error","finallyDeleteCustomMailFolder","trashFolderAndSubfolders","logout","mailGroupId","mailBoxDetail","editFolder","noParentFolderOption","folderNameValue","targetFolders","selectedParentFolder","TextField","oninput","newInput","DropDownSelector","selectedValue","selectedValueDisplay","selectionChangedHandler","newFolder","getPathToFolderString","validator","parentFolderId","getCustomFoldersOfParent","some","allowOkWithReturn","mailFacade","createMailFolder","updateMailFolderName","updateMailFolderParent","descendants","getDescendantFoldersOfParent","sort","l","r","level","descendant","sendFolderToSpam","error","isOfflineError","showEditFolderDialog","_mail","showFolder","configFacade","fileFacade","sendMailModelFactory","workerFacade","searchModel","contrastFixNeeded","sanitizeResult","loadingAttachments","contentBlockingStatus","errorOccurred","warningDismissed","calendarEventAttachment","loadingState","LoadingStateTracker","renderIsDelayed","renderedMail","loading","collapsed","entityListener","events","updatedMail","updateMail","NotFoundError","JSON","stringify","folderMailboxText","enabledMailAddresses","getEnabledMailAddressesWithUser","_e","first","notify","loadAndProcessAdditionalMailInfo","inlineImageCids","determineRelevantRecipient","loadAttachments","trackPromise","ConversationEntryTypeRef","getInlineCids","isReceivedMail","phishingStatus","getMailId","getMailBody","getDate","isLegacyMail","toRecipients","ccRecipients","bccRecipients","filterInt","recipientCount","getPhishingStatus","setPhishingStatus","AUTHENTICATED","setAuthenticationStatus","bodyErrors","isLegacy","getBody","getDetails","dismissed","addExternalImageRule","IndexingNotSupportedError","sanitizeMailBody","oldStatus","spamFolder","DisableMailExport","indexingSupported","wrapper","loadMailWrapper","listUnsubscribe","isExcludedMailAddress","mailHeaders","unsubHeaders","headerLine","getSenderOfResponseMail","delayBodyRenderingUntil","NotAuthorizedError","externalImageRule","getExternalImageRule","isAllowedAndAuthenticatedExternalSender","checkMailForPhishing","links","externalContent","inlineCids","attachmentsListId","listIdPart","attachmentElementIds","loadMultiple","handleCalendarFile","filesToLoad","dataFile","sanitizeInlineAttachment","inlineImageReference","loadInlineImages","linkObjects","innerHTML","isSuspicious","calendarFile","a","CALENDAR_MIME_TYPE","getEventFromFile","mailMethodToCalendarMethod","myMailAddresses","addressesInMail","foundAddress","contains","getDefaultSender","createResponseMailArgsForForwarding","newMailEditorAsResponse","replyTos","addSignature","infoLine","formatDateTime","mailSubject","urlEncodeHtmlTags","prependEmailSignature","senderMailAddress","conversationType","bodyText","replyAll","prefix","toUpperCase","addAll","attachmentsForReply","rawBody","urlified","urlify","sanitizeFragment","s","restrictions","participantGroupInfos","customer","CustomerTypeRef","loadGroupInfos","L","groupInfoId","contactFormUserGroups","list","groupInfo","deleted","ProgrammingError","createMailAddress","newReplyTos","createEncryptedMailAddress","initAsResponse","send","archive","inlineFileIds","FileOpenError","areParticipantsRestricted","getUserMailGroupMembership","group","getMailOwnerGroup","getAssignableMailRecipients","connectivityModel","cacheStorage","conversationViewModelFactory","mailOpenedListener","conversationPrefProvider","inboxRuleHandler","router","updateUi","_listId","targetMailId","loadingToTargetId","_filterType","listStreamSubscription","conversationPref","singInit","lazyMemoized","entityEventsReceived","_listModel","memoized","ListModel","topId","GENERATED_MAX_ID","fetch","startId","complete","loadMailRange","fixCounterIfNeeded","loadSingle","elementId","sortCompare","sortCompareByReverseId","debounce","itemsWhenCalled","wsConnection","unreadMailsCount","counterValue","getCounterValue","fixupCounterForMailList","getMailFilterForType","listIdToUse","getListIdForUserInbox","setListId","mapWith","loadAndSelectMail","updateUrl","loadAndSelect","firstBiggerThanSecond","lastThrow","conversationEnabled","createConversationViewModel","onEmailOpened","stateStream","onListStateChange","loadInitial","newState","selectedItems","mapWithout","routeTo","viewModelParams","entityEventReceived","start","loadRange","promiseFilter","findAndApplyMatchingRule","provideFromRange","mailboxDetailForListWithFallback","mailboxDetailForListId","clearFolder","isSubfolderOfType","options","viewModelFactory","onUiUpdate","loadingPromise","conversation","makeSingleUse","loadConversation","onEntityEvent","_primaryViewModel","conversationListId","processCreateConversationEntry","processUpdateConversationEntry","index","findLastIndex","splice","ceId","oldItemIndex","oldItem","isInTrash","conversationItemsForSelectedMailOnly","allMails","loadMails","createConversationItems","conversationEntries","newConversation","byList","groupBy","conversations","loaded","mailFolder","isIdle"],"mappings":"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"}