@conduction/nextcloud-vue 2.144.0 → 2.146.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js +1 -1
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js +1 -0
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnAiCompanion/CnAiChatPanel.vue2.js.map +1 -1
- package/dist/esm/components/CnAiCompanion/CnAiChatPanel.vue3.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue.js +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +55 -2
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue.js +1 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js +16 -0
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js +3 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue.js +1 -1
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +12 -2
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +42 -30
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +22 -0
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +2 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js +3 -3
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +46 -8
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +3 -3
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +2 -2
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +6 -3
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnMailTab/CnMailTab.vue.js +1 -1
- package/dist/esm/components/CnMailTab/CnMailTab.vue2.js.map +1 -1
- package/dist/esm/components/CnMailTab/CnMailTab.vue3.js +2 -2
- package/dist/esm/components/CnMailTab/CnMailTab.vue3.js.map +1 -1
- package/dist/esm/components/CnNotesCard/CnNotesCard.vue.js +1 -1
- package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js.map +1 -1
- package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js +1 -0
- package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js.map +1 -1
- package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue.js +1 -1
- package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue2.js +21 -2
- package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue2.js.map +1 -1
- package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +73 -12
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +165 -126
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js +1 -1
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js +1 -1
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js +14 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js.map +1 -1
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js +2 -1
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +13 -8
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue.js +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +10 -2
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
- package/dist/esm/composables/indexSources.js +35 -9
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +2 -0
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +1204 -467
- package/dist/esm/utils/builtInFormatters.js +18 -1
- package/dist/esm/utils/builtInFormatters.js.map +1 -1
- package/dist/esm/utils/detailObjectContext.js +7 -2
- package/dist/esm/utils/detailObjectContext.js.map +1 -1
- package/dist/esm/utils/localHeaderRows.js +95 -0
- package/dist/esm/utils/localHeaderRows.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/visibleWhen.js +6 -0
- package/dist/esm/utils/visibleWhen.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +686 -239
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +1204 -467
- package/l10n/nl.json +2 -0
- package/package.json +1 -1
- package/src/components/CnActionDialog/CnActionDialog.vue +8 -1
- package/src/components/CnAiCompanion/CnAiChatPanel.vue +3 -0
- package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +55 -2
- package/src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue +18 -0
- package/src/components/CnChecklistRunWidget/CnChecklistRunWidget.vue +4 -2
- package/src/components/CnDataTable/CnDataTable.vue +19 -2
- package/src/components/CnDetailPage/CnDetailPage.md +1 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +23 -0
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue +3 -1
- package/src/components/CnFactsWidget/CnFactsWidget.vue +74 -9
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +2 -2
- package/src/components/CnIndexPage/CnIndexPage.vue +6 -3
- package/src/components/CnMailTab/CnMailTab.vue +6 -4
- package/src/components/CnNotesCard/CnNotesCard.vue +7 -2
- package/src/components/CnNotificationMatrix/CnNotificationMatrix.vue +25 -3
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +132 -20
- package/src/components/CnRetentionWidget/CnRetentionWidget.vue +2 -0
- package/src/components/CnSettingsField/CnSettingsField.vue +15 -0
- package/src/components/CnTabsWidget/CnTabsWidget.vue +6 -1
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +13 -8
- package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +10 -2
- package/src/composables/indexSources.js +35 -9
- package/src/css/dialog.css +25 -9
- package/src/css/look-board-detail.css +370 -30
- package/src/css/look-board-index.css +116 -4
- package/src/css/look-board.css +168 -8
- package/src/css/table.css +17 -1
- package/src/schemas/app-manifest-v2.schema.json +2 -2
- package/src/utils/builtInFormatters.js +17 -0
- package/src/utils/detailObjectContext.js +7 -2
- package/src/utils/localHeaderRows.js +91 -0
- package/src/utils/validateManifestV2.compiled.js +2 -2
- package/src/utils/visibleWhen.js +6 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnMailTab.vue2.js","sources":["../../../../src/components/CnMailTab/CnMailTab.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-mail-tab\" data-testid=\"cn-mail-tab\">\n\t\t<CnEmptyContent\n\t\t\tv-if=\"unavailable\"\n\t\t\t:name=\"t('nextcloud-vue', 'Mail is not available')\"\n\t\t\t:description=\"t('nextcloud-vue', 'Install Nextcloud Mail to draft and link mail here.')\">\n\t\t\t<template #icon>\n\t\t\t\t<EmailOffOutline :size=\"48\" />\n\t\t\t</template>\n\t\t</CnEmptyContent>\n\n\t\t<template v-else>\n\t\t\t<div v-if=\"heading || description || hasHeaderActions\" class=\"cn-mail-tab__header\" data-testid=\"cn-mail-tab-header\">\n\t\t\t\t<div class=\"cn-mail-tab__heading\">\n\t\t\t\t\t<h3 v-if=\"heading\" class=\"cn-mail-tab__title\">\n\t\t\t\t\t\t{{ heading }}\n\t\t\t\t\t</h3>\n\t\t\t\t\t<p v-if=\"description\" class=\"cn-mail-tab__subtitle\">\n\t\t\t\t\t\t{{ description }}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"hasHeaderActions\" class=\"cn-mail-tab__header-actions\">\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"showLinkAction\"\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\tdata-testid=\"cn-mail-tab-header-link\"\n\t\t\t\t\t\t@click=\"openLinkPicker\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Link mail') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"showComposeAction\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t:disabled=\"draftEnabled && (closed || drafting)\"\n\t\t\t\t\t\t:href=\"draftEnabled ? undefined : composeUrl\"\n\t\t\t\t\t\t:target=\"draftEnabled ? undefined : '_blank'\"\n\t\t\t\t\t\tdata-testid=\"cn-mail-tab-header-compose\"\n\t\t\t\t\t\t@click=\"onCompose\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<EmailEditOutline :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Compose mail') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"draftEnabled\" class=\"cn-mail-tab__template-row\" data-testid=\"cn-mail-tab-template-row\">\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-if=\"templatesEnabled\"\n\t\t\t\t\tv-model=\"selectedTemplate\"\n\t\t\t\t\tclass=\"cn-mail-tab__template-select\"\n\t\t\t\t\t:options=\"templates\"\n\t\t\t\t\t:disabled=\"templates.length === 0\"\n\t\t\t\t\t:inputLabel=\"t('nextcloud-vue', 'Email template')\"\n\t\t\t\t\t:label=\"templateLabelField\"\n\t\t\t\t\t:trackBy=\"templateIdField\"\n\t\t\t\t\t:placeholder=\"templatePlaceholder\"\n\t\t\t\t\t:clearable=\"true\" />\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"templatesEnabled\"\n\t\t\t\t\t:disabled=\"closed || drafting || !selectedTemplate\"\n\t\t\t\t\tdata-testid=\"cn-mail-tab-draft-template\"\n\t\t\t\t\t@click=\"draftFromTemplate\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Open draft from template') }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"!showComposeAction || draftSource.blankButton === true\"\n\t\t\t\t\t:disabled=\"closed || drafting\"\n\t\t\t\t\tdata-testid=\"cn-mail-tab-draft-blank\"\n\t\t\t\t\t@click=\"draftBlank\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Empty draft') }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t\t<p v-if=\"hint\" class=\"cn-mail-tab__hint\">\n\t\t\t\t{{ hint }}\n\t\t\t</p>\n\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"error\"\n\t\t\t\ttype=\"error\"\n\t\t\t\t:heading=\"t('nextcloud-vue', 'Could not open draft')\"\n\t\t\t\trole=\"alert\">\n\t\t\t\t{{ error }}\n\t\t\t</NcNoteCard>\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"unresolved.length > 0\"\n\t\t\t\ttype=\"warning\"\n\t\t\t\trole=\"status\"\n\t\t\t\tdata-testid=\"cn-mail-tab-unresolved\">\n\t\t\t\t<p>{{ t('nextcloud-vue', 'Some template fields could not be filled. Fill these in the draft yourself:') }}</p>\n\t\t\t\t<ul class=\"cn-mail-tab__unresolved\">\n\t\t\t\t\t<li v-for=\"name in unresolved\" :key=\"name\">\n\t\t\t\t\t\t<code>{{ placeholderOf(name) }}</code>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</NcNoteCard>\n\n\t\t\t<CnEmailTab\n\t\t\t\tv-if=\"linkedMail && objectKey\"\n\t\t\t\tref=\"linkedMail\"\n\t\t\t\tclass=\"cn-mail-tab__linked\"\n\t\t\t\t:showActions=\"!hasHeaderActions\"\n\t\t\t\t:objectId=\"objectKey\"\n\t\t\t\t:register=\"registerKey\"\n\t\t\t\t:schema=\"schemaKey\" />\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcNoteCard, NcSelect } from '@nextcloud/vue'\nimport EmailEditOutline from 'vue-material-design-icons/EmailEditOutline.vue'\nimport EmailOffOutline from 'vue-material-design-icons/EmailOffOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport CnEmailTab from '../../integrations/builtin/email/CnEmailTab.vue'\nimport CnEmptyContent from '../CnEmptyContent/CnEmptyContent.vue'\nimport { fetchEndpointSource, interpolateUrlTokens } from '../../composables/useEndpointSource.js'\nimport { evaluateVisibleWhenLocal } from '../../utils/visibleWhen.js'\n\n/** The actions `content.headerActions` can put in the tab's header. */\nconst HEADER_ACTIONS = ['compose', 'link']\n\n/**\n * Read a reference that may be a slug, an id or an object carrying either.\n *\n * @param {string|number|object} value The reference.\n * @return {string} The slug or id, or ''.\n */\nfunction keyOf(value) {\n\tif (value === null || value === undefined) {\n\t\treturn ''\n\t}\n\tif (typeof value === 'object') {\n\t\treturn String(value.slug || value.id || value.uuid || '')\n\t}\n\treturn String(value)\n}\n\n/**\n * CnMailTab — the mail of one record: drafts in Nextcloud Mail, from a\n * template or empty, above the mail linked to the record.\n *\n * Registered as the `mail` widget type. The app owns the endpoints and the\n * texts; the tab never sends mail. A draft is a POST to `draft.url`, whose\n * response may carry `draftUrl` (opened in a new tab) and `unresolved` (the\n * template fields the server could not fill). A 404 there means the app has\n * no Mail to draft into, and the tab says so instead of the controls.\n *\n * ```json\n * {\n * \"type\": \"mail\",\n * \"content\": {\n * \"heading\": \"Email\",\n * \"description\": \"Mail linked to this case\",\n * \"hint\": \"The draft opens in Nextcloud Mail.\",\n * \"templates\": { \"url\": \"/apps/app/api/types/@object.type/templates\", \"responsePath\": \"results\", \"labelField\": \"name\", \"idField\": \"id\" },\n * \"noTemplatesText\": \"No templates for this type yet\",\n * \"draft\": { \"url\": \"/apps/app/api/records/@objectId/templates/{templateId}/draft\", \"blankTemplateId\": \"blank\" },\n * \"closedWhen\": { \"field\": \"endDate\", \"op\": \"notEmpty\" },\n * \"linkedMail\": true,\n * \"headerActions\": [\"link\", \"compose\"]\n * }\n * }\n * ```\n *\n * URLs take the `@objectId` / `@object.<field>` tokens of `endpointSource`;\n * `{templateId}` in the draft URL is the chosen template's id, or\n * `blankTemplateId` for an empty draft. Without `templates` the row offers\n * the empty draft only; without `draft` there is no row. `closedWhen` is a\n * local visibleWhen condition on the record that disables drafting.\n * `linkedMail: false` leaves the linked mail to a separate `email`\n * integration card. `headerActions` puts buttons beside the heading:\n * `compose` opens the empty draft (or Nextcloud Mail's composer when there\n * is no `draft`) and replaces the row's empty-draft button, unless\n * `draft.blankButton` is true (DqZaakEmail draws both); `link` opens the\n * linked mail's picker and needs the linked mail. The linked mail then drops\n * its own buttons. Texts go through the host's translate.\n */\nexport default {\n\tname: 'CnMailTab',\n\n\tcomponents: {\n\t\tCnEmailTab,\n\t\tCnEmptyContent,\n\t\tEmailEditOutline,\n\t\tEmailOffOutline,\n\t\tLinkVariant,\n\t\tNcButton,\n\t\tNcNoteCard,\n\t\tNcSelect,\n\t},\n\n\tinject: {\n\t\t/** The detail page's object context (`{ objectId, object }`), for tokens. */\n\t\tcnObjectContext: { default: null },\n\t\t/** The host's translate, for the manifest texts. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget config: `{ heading?, description?, hint?, templates?,\n\t\t * noTemplatesText?, draft?, closedWhen?, linkedMail?, headerActions? }`, as in the\n\t\t * component description and docs/components/cn-mail-tab.md.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The bound record's id, for `@objectId` and the linked mail. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record, for `@object.<field>` and `closedWhen`. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The bound record's register, for the linked mail. */\n\t\tregister: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record's schema, for the linked mail. */\n\t\tschema: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['drafted'],\n\n\tdata() {\n\t\treturn {\n\t\t\ttemplates: [],\n\t\t\tselectedTemplate: null,\n\t\t\tdrafting: false,\n\t\t\terror: '',\n\t\t\tunresolved: [],\n\t\t\tunavailable: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The record, from the prop or the page context. */\n\t\trecord() {\n\t\t\tif (this.objectData) {\n\t\t\t\treturn this.objectData\n\t\t\t}\n\t\t\tconst ctx = this.cnObjectContext\n\t\t\tconst value = ctx && typeof ctx === 'object' && 'value' in ctx ? ctx.value : ctx\n\t\t\treturn (value && value.object) || {}\n\t\t},\n\n\t\t/** @return {string} The record id. */\n\t\tobjectKey() {\n\t\t\treturn keyOf(this.objectId) || keyOf(this.record.id) || keyOf(this.record['@self'] && this.record['@self'].id)\n\t\t},\n\n\t\t/** @return {string} The register slug or id. */\n\t\tregisterKey() {\n\t\t\treturn keyOf(this.register)\n\t\t},\n\n\t\t/** @return {string} The schema slug or id. */\n\t\tschemaKey() {\n\t\t\treturn keyOf(this.schema)\n\t\t},\n\n\t\t/** @return {object} The token context for the URLs. */\n\t\ttokenContext() {\n\t\t\treturn { objectId: this.objectKey, object: this.record }\n\t\t},\n\n\t\t/** @return {string} The heading, translated, or ''. */\n\t\theading() {\n\t\t\treturn this.tr(this.content.heading)\n\t\t},\n\n\t\t/** @return {string} The line under the heading, translated, or ''. */\n\t\tdescription() {\n\t\t\treturn this.tr(this.content.description)\n\t\t},\n\n\t\t/** @return {string} The line under the template row, translated, or ''. */\n\t\thint() {\n\t\t\treturn this.tr(this.content.hint)\n\t\t},\n\n\t\t/** @return {object|null} The templates endpoint, or null. */\n\t\ttemplatesSource() {\n\t\t\tconst source = this.content.templates\n\t\t\treturn source && typeof source === 'object' && source.url ? source : null\n\t\t},\n\n\t\t/** @return {object|null} The draft endpoint, or null. */\n\t\tdraftSource() {\n\t\t\tconst source = this.content.draft\n\t\t\treturn source && typeof source === 'object' && source.url ? source : null\n\t\t},\n\n\t\t/** @return {boolean} Whether the template select and its button show. */\n\t\ttemplatesEnabled() {\n\t\t\treturn this.templatesSource !== null\n\t\t},\n\n\t\t/** @return {boolean} Whether the template row shows. */\n\t\tdraftEnabled() {\n\t\t\treturn this.draftSource !== null\n\t\t},\n\n\t\t/** @return {string} The template field shown in the select. */\n\t\ttemplateLabelField() {\n\t\t\treturn (this.templatesSource && this.templatesSource.labelField) || 'name'\n\t\t},\n\n\t\t/** @return {string} The template field that identifies it. */\n\t\ttemplateIdField() {\n\t\t\treturn (this.templatesSource && this.templatesSource.idField) || 'id'\n\t\t},\n\n\t\t/** @return {string} The select's placeholder. */\n\t\ttemplatePlaceholder() {\n\t\t\tif (this.templates.length === 0) {\n\t\t\t\treturn this.tr(this.content.noTemplatesText) || t('nextcloud-vue', 'No templates yet')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Select a template (optional)…')\n\t\t},\n\n\t\t/** @return {boolean} Whether `closedWhen` holds for the record. */\n\t\tclosed() {\n\t\t\tconst cond = this.content.closedWhen\n\t\t\tif (!cond) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn evaluateVisibleWhenLocal(cond, this.record)\n\t\t},\n\n\t\t/** @return {string} The templates URL after tokens, to refetch on change. */\n\t\ttemplatesUrl() {\n\t\t\treturn this.templatesSource ? interpolateUrlTokens(this.templatesSource.url, this.tokenContext) : ''\n\t\t},\n\n\t\t/** @return {boolean} Whether the linked mail list shows. */\n\t\tlinkedMail() {\n\t\t\treturn this.content.linkedMail !== false\n\t\t},\n\n\t\t/**\n\t\t * The known header actions the config asks for.\n\t\t *\n\t\t * @return {string[]} `compose` and or `link`.\n\t\t * @spec openspec/changes/mail-tab-compose-and-link/specs/mail-tab-compose-and-link/spec.md#requirement-the-mail-tab-can-carry-compose-mail-and-link-mail-in-its-header\n\t\t */\n\t\theaderActions() {\n\t\t\tconst list = Array.isArray(this.content.headerActions) ? this.content.headerActions : []\n\t\t\treturn list.filter((key) => HEADER_ACTIONS.includes(key))\n\t\t},\n\n\t\t/** @return {boolean} Whether the header shows \"Compose mail\". */\n\t\tshowComposeAction() {\n\t\t\treturn this.headerActions.includes('compose')\n\t\t},\n\n\t\t/** @return {boolean} Whether the header shows \"Link mail\": it opens the linked mail's picker. */\n\t\tshowLinkAction() {\n\t\t\treturn this.headerActions.includes('link') && this.linkedMail && this.objectKey !== ''\n\t\t},\n\n\t\t/** @return {boolean} Whether the header holds any button. */\n\t\thasHeaderActions() {\n\t\t\treturn this.showComposeAction || this.showLinkAction\n\t\t},\n\n\t\t/** @return {string} Nextcloud Mail's composer, for an app without a draft endpoint. */\n\t\tcomposeUrl() {\n\t\t\treturn (typeof OC !== 'undefined' && typeof OC.generateUrl === 'function')\n\t\t\t\t? OC.generateUrl('/apps/mail/box/draft')\n\t\t\t\t: '/index.php/apps/mail/box/draft'\n\t\t},\n\t},\n\n\twatch: {\n\t\ttemplatesUrl: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tthis.loadTemplates()\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Translate a manifest text through the host.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The translation, or '' for no text.\n\t\t */\n\t\ttr(text) {\n\t\t\tif (typeof text !== 'string' || text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.cnTranslate(text)\n\t\t},\n\n\t\t/**\n\t\t * Read the templates. A failed read leaves the empty draft usable.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadTemplates() {\n\t\t\tif (!this.templatesSource) {\n\t\t\t\tthis.templates = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst payload = await fetchEndpointSource(this.templatesSource, this.tokenContext)\n\t\t\t\tconst list = Array.isArray(payload) ? payload : (payload && Array.isArray(payload.results) ? payload.results : [])\n\t\t\t\tthis.templates = list\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnMailTab] could not read the templates', err)\n\t\t\t\tthis.templates = []\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tdraftFromTemplate() {\n\t\t\treturn this.openDraft(this.selectedTemplate)\n\t\t},\n\n\t\t/**\n\t\t * \"Compose mail\": the empty draft through the app's endpoint. Without\n\t\t * one the button is a link to Mail's composer and needs no handler.\n\t\t *\n\t\t * @return {Promise<void>|undefined}\n\t\t */\n\t\tonCompose() {\n\t\t\treturn this.draftEnabled ? this.draftBlank() : undefined\n\t\t},\n\n\t\t/**\n\t\t * \"Link mail\": the linked mail's picker, which links the chosen\n\t\t * message to the record through its own endpoint.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\topenLinkPicker() {\n\t\t\tconst linked = this.$refs.linkedMail\n\t\t\tif (linked && typeof linked.openPicker === 'function') {\n\t\t\t\tlinked.openPicker()\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tdraftBlank() {\n\t\t\tthis.selectedTemplate = null\n\t\t\treturn this.openDraft(null)\n\t\t},\n\n\t\t/**\n\t\t * Ask the app to create a Nextcloud Mail draft and open it.\n\t\t *\n\t\t * @param {object|null} template The chosen template, or null for an empty draft.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync openDraft(template) {\n\t\t\tif (this.closed || !this.draftSource) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.drafting = true\n\t\t\tthis.error = ''\n\t\t\tthis.unresolved = []\n\t\t\ttry {\n\t\t\t\tconst id = template ? template[this.templateIdField] : (this.draftSource.blankTemplateId || 'blank')\n\t\t\t\tconst path = interpolateUrlTokens(this.draftSource.url, this.tokenContext)\n\t\t\t\t\t.replace('{templateId}', encodeURIComponent(String(id)))\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst { data } = await axios.post(generateUrl(path), {})\n\t\t\t\tthis.unresolved = Array.isArray(data && data.unresolved) ? data.unresolved : []\n\t\t\t\tif (data && data.draftUrl) {\n\t\t\t\t\twindow.open(data.draftUrl, '_blank', 'noopener')\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event drafted A draft was made from a template. Carries the\n\t\t\t\t * draft endpoint's response, including `draftUrl` and\n\t\t\t\t * `unresolved` when the endpoint returns them.\n\t\t\t\t * @type {object}\n\t\t\t\t */\n\t\t\t\tthis.$emit('drafted', data)\n\t\t\t} catch (err) {\n\t\t\t\tconst response = err && err.response\n\t\t\t\tif (response && response.status === 404) {\n\t\t\t\t\tthis.unavailable = true\n\t\t\t\t}\n\t\t\t\tthis.error = (response && response.data && response.data.message)\n\t\t\t\t\t|| (err && err.message)\n\t\t\t\t\t|| t('nextcloud-vue', 'The draft could not be opened.')\n\t\t\t} finally {\n\t\t\t\tthis.drafting = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write a template field the way the template spells it.\n\t\t *\n\t\t * @param {string} name The field name.\n\t\t * @return {string} `{{name}}`.\n\t\t */\n\t\tplaceholderOf(name) {\n\t\t\treturn `{{${name}}}`\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-mail-tab {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n\tpadding: 12px;\n}\n\n.cn-mail-tab__header {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 10px 16px;\n}\n\n.cn-mail-tab__heading {\n\tdisplay: flex;\n\tflex: 1 1 260px;\n\tflex-direction: column;\n\tgap: 3px;\n\tmin-width: 0;\n}\n\n.cn-mail-tab__header-actions {\n\tdisplay: flex;\n\tflex: none;\n\tflex-wrap: wrap;\n\tgap: 8px;\n}\n\n.cn-mail-tab__title {\n\tmargin: 0;\n\tfont-size: 16px;\n\tfont-weight: bold;\n}\n\n.cn-mail-tab__subtitle,\n.cn-mail-tab__hint {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-mail-tab__template-row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-end;\n\tgap: 8px;\n}\n\n.cn-mail-tab__template-select {\n\tflex: 1 1 260px;\n}\n\n.cn-mail-tab__unresolved {\n\tmargin: 4px 0 0;\n\tpadding-inline-start: 20px;\n}\n\n.cn-mail-tab__unresolved code {\n\tbackground: var(--color-background-dark);\n\tpadding: 2px 6px;\n\tborder-radius: var(--border-radius);\n\tfont-family: var(--font-face-mono, monospace);\n}\n</style>\n"],"names":["CnEmailTab","CnEmptyContent","t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnMailTab.vue2.js","sources":["../../../../src/components/CnMailTab/CnMailTab.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-mail-tab\" data-testid=\"cn-mail-tab\">\n\t\t<CnEmptyContent\n\t\t\tv-if=\"unavailable\"\n\t\t\t:name=\"t('nextcloud-vue', 'Mail is not available')\"\n\t\t\t:description=\"t('nextcloud-vue', 'Install Nextcloud Mail to draft and link mail here.')\">\n\t\t\t<template #icon>\n\t\t\t\t<EmailOffOutline :size=\"48\" />\n\t\t\t</template>\n\t\t</CnEmptyContent>\n\n\t\t<template v-else>\n\t\t\t<div v-if=\"heading || description || hasHeaderActions\" class=\"cn-mail-tab__header\" data-testid=\"cn-mail-tab-header\">\n\t\t\t\t<div class=\"cn-mail-tab__heading\">\n\t\t\t\t\t<h3 v-if=\"heading\" class=\"cn-mail-tab__title\">\n\t\t\t\t\t\t{{ heading }}\n\t\t\t\t\t</h3>\n\t\t\t\t\t<!-- Divs, not <p>: a theme that sizes every paragraph with !important\n\t\t\t\t\t would override the card's 14px description. -->\n\t\t\t\t\t<div v-if=\"description\" class=\"cn-mail-tab__subtitle\">\n\t\t\t\t\t\t{{ description }}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"hasHeaderActions\" class=\"cn-mail-tab__header-actions\">\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"showLinkAction\"\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\tdata-testid=\"cn-mail-tab-header-link\"\n\t\t\t\t\t\t@click=\"openLinkPicker\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Link mail') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"showComposeAction\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t:disabled=\"draftEnabled && (closed || drafting)\"\n\t\t\t\t\t\t:href=\"draftEnabled ? undefined : composeUrl\"\n\t\t\t\t\t\t:target=\"draftEnabled ? undefined : '_blank'\"\n\t\t\t\t\t\tdata-testid=\"cn-mail-tab-header-compose\"\n\t\t\t\t\t\t@click=\"onCompose\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<EmailEditOutline :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Compose mail') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"draftEnabled\" class=\"cn-mail-tab__template-row\" data-testid=\"cn-mail-tab-template-row\">\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-if=\"templatesEnabled\"\n\t\t\t\t\tv-model=\"selectedTemplate\"\n\t\t\t\t\tclass=\"cn-mail-tab__template-select\"\n\t\t\t\t\t:options=\"templates\"\n\t\t\t\t\t:disabled=\"templates.length === 0\"\n\t\t\t\t\t:inputLabel=\"t('nextcloud-vue', 'Email template')\"\n\t\t\t\t\t:label=\"templateLabelField\"\n\t\t\t\t\t:trackBy=\"templateIdField\"\n\t\t\t\t\t:placeholder=\"templatePlaceholder\"\n\t\t\t\t\t:clearable=\"true\" />\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"templatesEnabled\"\n\t\t\t\t\t:disabled=\"closed || drafting || !selectedTemplate\"\n\t\t\t\t\tdata-testid=\"cn-mail-tab-draft-template\"\n\t\t\t\t\t@click=\"draftFromTemplate\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Open draft from template') }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"!showComposeAction || draftSource.blankButton === true\"\n\t\t\t\t\t:disabled=\"closed || drafting\"\n\t\t\t\t\tdata-testid=\"cn-mail-tab-draft-blank\"\n\t\t\t\t\t@click=\"draftBlank\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Empty draft') }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t\t<div v-if=\"hint\" class=\"cn-mail-tab__hint\">\n\t\t\t\t{{ hint }}\n\t\t\t</div>\n\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"error\"\n\t\t\t\ttype=\"error\"\n\t\t\t\t:heading=\"t('nextcloud-vue', 'Could not open draft')\"\n\t\t\t\trole=\"alert\">\n\t\t\t\t{{ error }}\n\t\t\t</NcNoteCard>\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"unresolved.length > 0\"\n\t\t\t\ttype=\"warning\"\n\t\t\t\trole=\"status\"\n\t\t\t\tdata-testid=\"cn-mail-tab-unresolved\">\n\t\t\t\t<p>{{ t('nextcloud-vue', 'Some template fields could not be filled. Fill these in the draft yourself:') }}</p>\n\t\t\t\t<ul class=\"cn-mail-tab__unresolved\">\n\t\t\t\t\t<li v-for=\"name in unresolved\" :key=\"name\">\n\t\t\t\t\t\t<code>{{ placeholderOf(name) }}</code>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</NcNoteCard>\n\n\t\t\t<CnEmailTab\n\t\t\t\tv-if=\"linkedMail && objectKey\"\n\t\t\t\tref=\"linkedMail\"\n\t\t\t\tclass=\"cn-mail-tab__linked\"\n\t\t\t\t:showActions=\"!hasHeaderActions\"\n\t\t\t\t:objectId=\"objectKey\"\n\t\t\t\t:register=\"registerKey\"\n\t\t\t\t:schema=\"schemaKey\" />\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcNoteCard, NcSelect } from '@nextcloud/vue'\nimport EmailEditOutline from 'vue-material-design-icons/EmailEditOutline.vue'\nimport EmailOffOutline from 'vue-material-design-icons/EmailOffOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport CnEmailTab from '../../integrations/builtin/email/CnEmailTab.vue'\nimport CnEmptyContent from '../CnEmptyContent/CnEmptyContent.vue'\nimport { fetchEndpointSource, interpolateUrlTokens } from '../../composables/useEndpointSource.js'\nimport { evaluateVisibleWhenLocal } from '../../utils/visibleWhen.js'\n\n/** The actions `content.headerActions` can put in the tab's header. */\nconst HEADER_ACTIONS = ['compose', 'link']\n\n/**\n * Read a reference that may be a slug, an id or an object carrying either.\n *\n * @param {string|number|object} value The reference.\n * @return {string} The slug or id, or ''.\n */\nfunction keyOf(value) {\n\tif (value === null || value === undefined) {\n\t\treturn ''\n\t}\n\tif (typeof value === 'object') {\n\t\treturn String(value.slug || value.id || value.uuid || '')\n\t}\n\treturn String(value)\n}\n\n/**\n * CnMailTab — the mail of one record: drafts in Nextcloud Mail, from a\n * template or empty, above the mail linked to the record.\n *\n * Registered as the `mail` widget type. The app owns the endpoints and the\n * texts; the tab never sends mail. A draft is a POST to `draft.url`, whose\n * response may carry `draftUrl` (opened in a new tab) and `unresolved` (the\n * template fields the server could not fill). A 404 there means the app has\n * no Mail to draft into, and the tab says so instead of the controls.\n *\n * ```json\n * {\n * \"type\": \"mail\",\n * \"content\": {\n * \"heading\": \"Email\",\n * \"description\": \"Mail linked to this case\",\n * \"hint\": \"The draft opens in Nextcloud Mail.\",\n * \"templates\": { \"url\": \"/apps/app/api/types/@object.type/templates\", \"responsePath\": \"results\", \"labelField\": \"name\", \"idField\": \"id\" },\n * \"noTemplatesText\": \"No templates for this type yet\",\n * \"draft\": { \"url\": \"/apps/app/api/records/@objectId/templates/{templateId}/draft\", \"blankTemplateId\": \"blank\" },\n * \"closedWhen\": { \"field\": \"endDate\", \"op\": \"notEmpty\" },\n * \"linkedMail\": true,\n * \"headerActions\": [\"link\", \"compose\"]\n * }\n * }\n * ```\n *\n * URLs take the `@objectId` / `@object.<field>` tokens of `endpointSource`;\n * `{templateId}` in the draft URL is the chosen template's id, or\n * `blankTemplateId` for an empty draft. Without `templates` the row offers\n * the empty draft only; without `draft` there is no row. `closedWhen` is a\n * local visibleWhen condition on the record that disables drafting.\n * `linkedMail: false` leaves the linked mail to a separate `email`\n * integration card. `headerActions` puts buttons beside the heading:\n * `compose` opens the empty draft (or Nextcloud Mail's composer when there\n * is no `draft`) and replaces the row's empty-draft button, unless\n * `draft.blankButton` is true (DqZaakEmail draws both); `link` opens the\n * linked mail's picker and needs the linked mail. The linked mail then drops\n * its own buttons. Texts go through the host's translate.\n */\nexport default {\n\tname: 'CnMailTab',\n\n\tcomponents: {\n\t\tCnEmailTab,\n\t\tCnEmptyContent,\n\t\tEmailEditOutline,\n\t\tEmailOffOutline,\n\t\tLinkVariant,\n\t\tNcButton,\n\t\tNcNoteCard,\n\t\tNcSelect,\n\t},\n\n\tinject: {\n\t\t/** The detail page's object context (`{ objectId, object }`), for tokens. */\n\t\tcnObjectContext: { default: null },\n\t\t/** The host's translate, for the manifest texts. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget config: `{ heading?, description?, hint?, templates?,\n\t\t * noTemplatesText?, draft?, closedWhen?, linkedMail?, headerActions? }`, as in the\n\t\t * component description and docs/components/cn-mail-tab.md.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The bound record's id, for `@objectId` and the linked mail. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record, for `@object.<field>` and `closedWhen`. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The bound record's register, for the linked mail. */\n\t\tregister: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record's schema, for the linked mail. */\n\t\tschema: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['drafted'],\n\n\tdata() {\n\t\treturn {\n\t\t\ttemplates: [],\n\t\t\tselectedTemplate: null,\n\t\t\tdrafting: false,\n\t\t\terror: '',\n\t\t\tunresolved: [],\n\t\t\tunavailable: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The record, from the prop or the page context. */\n\t\trecord() {\n\t\t\tif (this.objectData) {\n\t\t\t\treturn this.objectData\n\t\t\t}\n\t\t\tconst ctx = this.cnObjectContext\n\t\t\tconst value = ctx && typeof ctx === 'object' && 'value' in ctx ? ctx.value : ctx\n\t\t\treturn (value && value.object) || {}\n\t\t},\n\n\t\t/** @return {string} The record id. */\n\t\tobjectKey() {\n\t\t\treturn keyOf(this.objectId) || keyOf(this.record.id) || keyOf(this.record['@self'] && this.record['@self'].id)\n\t\t},\n\n\t\t/** @return {string} The register slug or id. */\n\t\tregisterKey() {\n\t\t\treturn keyOf(this.register)\n\t\t},\n\n\t\t/** @return {string} The schema slug or id. */\n\t\tschemaKey() {\n\t\t\treturn keyOf(this.schema)\n\t\t},\n\n\t\t/** @return {object} The token context for the URLs. */\n\t\ttokenContext() {\n\t\t\treturn { objectId: this.objectKey, object: this.record }\n\t\t},\n\n\t\t/** @return {string} The heading, translated, or ''. */\n\t\theading() {\n\t\t\treturn this.tr(this.content.heading)\n\t\t},\n\n\t\t/** @return {string} The line under the heading, translated, or ''. */\n\t\tdescription() {\n\t\t\treturn this.tr(this.content.description)\n\t\t},\n\n\t\t/** @return {string} The line under the template row, translated, or ''. */\n\t\thint() {\n\t\t\treturn this.tr(this.content.hint)\n\t\t},\n\n\t\t/** @return {object|null} The templates endpoint, or null. */\n\t\ttemplatesSource() {\n\t\t\tconst source = this.content.templates\n\t\t\treturn source && typeof source === 'object' && source.url ? source : null\n\t\t},\n\n\t\t/** @return {object|null} The draft endpoint, or null. */\n\t\tdraftSource() {\n\t\t\tconst source = this.content.draft\n\t\t\treturn source && typeof source === 'object' && source.url ? source : null\n\t\t},\n\n\t\t/** @return {boolean} Whether the template select and its button show. */\n\t\ttemplatesEnabled() {\n\t\t\treturn this.templatesSource !== null\n\t\t},\n\n\t\t/** @return {boolean} Whether the template row shows. */\n\t\tdraftEnabled() {\n\t\t\treturn this.draftSource !== null\n\t\t},\n\n\t\t/** @return {string} The template field shown in the select. */\n\t\ttemplateLabelField() {\n\t\t\treturn (this.templatesSource && this.templatesSource.labelField) || 'name'\n\t\t},\n\n\t\t/** @return {string} The template field that identifies it. */\n\t\ttemplateIdField() {\n\t\t\treturn (this.templatesSource && this.templatesSource.idField) || 'id'\n\t\t},\n\n\t\t/** @return {string} The select's placeholder. */\n\t\ttemplatePlaceholder() {\n\t\t\tif (this.templates.length === 0) {\n\t\t\t\treturn this.tr(this.content.noTemplatesText) || t('nextcloud-vue', 'No templates yet')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Select a template (optional)…')\n\t\t},\n\n\t\t/** @return {boolean} Whether `closedWhen` holds for the record. */\n\t\tclosed() {\n\t\t\tconst cond = this.content.closedWhen\n\t\t\tif (!cond) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn evaluateVisibleWhenLocal(cond, this.record)\n\t\t},\n\n\t\t/** @return {string} The templates URL after tokens, to refetch on change. */\n\t\ttemplatesUrl() {\n\t\t\treturn this.templatesSource ? interpolateUrlTokens(this.templatesSource.url, this.tokenContext) : ''\n\t\t},\n\n\t\t/** @return {boolean} Whether the linked mail list shows. */\n\t\tlinkedMail() {\n\t\t\treturn this.content.linkedMail !== false\n\t\t},\n\n\t\t/**\n\t\t * The known header actions the config asks for.\n\t\t *\n\t\t * @return {string[]} `compose` and or `link`.\n\t\t * @spec openspec/changes/mail-tab-compose-and-link/specs/mail-tab-compose-and-link/spec.md#requirement-the-mail-tab-can-carry-compose-mail-and-link-mail-in-its-header\n\t\t */\n\t\theaderActions() {\n\t\t\tconst list = Array.isArray(this.content.headerActions) ? this.content.headerActions : []\n\t\t\treturn list.filter((key) => HEADER_ACTIONS.includes(key))\n\t\t},\n\n\t\t/** @return {boolean} Whether the header shows \"Compose mail\". */\n\t\tshowComposeAction() {\n\t\t\treturn this.headerActions.includes('compose')\n\t\t},\n\n\t\t/** @return {boolean} Whether the header shows \"Link mail\": it opens the linked mail's picker. */\n\t\tshowLinkAction() {\n\t\t\treturn this.headerActions.includes('link') && this.linkedMail && this.objectKey !== ''\n\t\t},\n\n\t\t/** @return {boolean} Whether the header holds any button. */\n\t\thasHeaderActions() {\n\t\t\treturn this.showComposeAction || this.showLinkAction\n\t\t},\n\n\t\t/** @return {string} Nextcloud Mail's composer, for an app without a draft endpoint. */\n\t\tcomposeUrl() {\n\t\t\treturn (typeof OC !== 'undefined' && typeof OC.generateUrl === 'function')\n\t\t\t\t? OC.generateUrl('/apps/mail/box/draft')\n\t\t\t\t: '/index.php/apps/mail/box/draft'\n\t\t},\n\t},\n\n\twatch: {\n\t\ttemplatesUrl: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tthis.loadTemplates()\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Translate a manifest text through the host.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The translation, or '' for no text.\n\t\t */\n\t\ttr(text) {\n\t\t\tif (typeof text !== 'string' || text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.cnTranslate(text)\n\t\t},\n\n\t\t/**\n\t\t * Read the templates. A failed read leaves the empty draft usable.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadTemplates() {\n\t\t\tif (!this.templatesSource) {\n\t\t\t\tthis.templates = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst payload = await fetchEndpointSource(this.templatesSource, this.tokenContext)\n\t\t\t\tconst list = Array.isArray(payload) ? payload : (payload && Array.isArray(payload.results) ? payload.results : [])\n\t\t\t\tthis.templates = list\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnMailTab] could not read the templates', err)\n\t\t\t\tthis.templates = []\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tdraftFromTemplate() {\n\t\t\treturn this.openDraft(this.selectedTemplate)\n\t\t},\n\n\t\t/**\n\t\t * \"Compose mail\": the empty draft through the app's endpoint. Without\n\t\t * one the button is a link to Mail's composer and needs no handler.\n\t\t *\n\t\t * @return {Promise<void>|undefined}\n\t\t */\n\t\tonCompose() {\n\t\t\treturn this.draftEnabled ? this.draftBlank() : undefined\n\t\t},\n\n\t\t/**\n\t\t * \"Link mail\": the linked mail's picker, which links the chosen\n\t\t * message to the record through its own endpoint.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\topenLinkPicker() {\n\t\t\tconst linked = this.$refs.linkedMail\n\t\t\tif (linked && typeof linked.openPicker === 'function') {\n\t\t\t\tlinked.openPicker()\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tdraftBlank() {\n\t\t\tthis.selectedTemplate = null\n\t\t\treturn this.openDraft(null)\n\t\t},\n\n\t\t/**\n\t\t * Ask the app to create a Nextcloud Mail draft and open it.\n\t\t *\n\t\t * @param {object|null} template The chosen template, or null for an empty draft.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync openDraft(template) {\n\t\t\tif (this.closed || !this.draftSource) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.drafting = true\n\t\t\tthis.error = ''\n\t\t\tthis.unresolved = []\n\t\t\ttry {\n\t\t\t\tconst id = template ? template[this.templateIdField] : (this.draftSource.blankTemplateId || 'blank')\n\t\t\t\tconst path = interpolateUrlTokens(this.draftSource.url, this.tokenContext)\n\t\t\t\t\t.replace('{templateId}', encodeURIComponent(String(id)))\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst { data } = await axios.post(generateUrl(path), {})\n\t\t\t\tthis.unresolved = Array.isArray(data && data.unresolved) ? data.unresolved : []\n\t\t\t\tif (data && data.draftUrl) {\n\t\t\t\t\twindow.open(data.draftUrl, '_blank', 'noopener')\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event drafted A draft was made from a template. Carries the\n\t\t\t\t * draft endpoint's response, including `draftUrl` and\n\t\t\t\t * `unresolved` when the endpoint returns them.\n\t\t\t\t * @type {object}\n\t\t\t\t */\n\t\t\t\tthis.$emit('drafted', data)\n\t\t\t} catch (err) {\n\t\t\t\tconst response = err && err.response\n\t\t\t\tif (response && response.status === 404) {\n\t\t\t\t\tthis.unavailable = true\n\t\t\t\t}\n\t\t\t\tthis.error = (response && response.data && response.data.message)\n\t\t\t\t\t|| (err && err.message)\n\t\t\t\t\t|| t('nextcloud-vue', 'The draft could not be opened.')\n\t\t\t} finally {\n\t\t\t\tthis.drafting = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write a template field the way the template spells it.\n\t\t *\n\t\t * @param {string} name The field name.\n\t\t * @return {string} `{{name}}`.\n\t\t */\n\t\tplaceholderOf(name) {\n\t\t\treturn `{{${name}}}`\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-mail-tab {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n\tpadding: 12px;\n}\n\n.cn-mail-tab__header {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 10px 16px;\n}\n\n.cn-mail-tab__heading {\n\tdisplay: flex;\n\tflex: 1 1 260px;\n\tflex-direction: column;\n\tgap: 3px;\n\tmin-width: 0;\n}\n\n.cn-mail-tab__header-actions {\n\tdisplay: flex;\n\tflex: none;\n\tflex-wrap: wrap;\n\tgap: 8px;\n}\n\n.cn-mail-tab__title {\n\tmargin: 0;\n\tfont-size: 16px;\n\tfont-weight: bold;\n}\n\n.cn-mail-tab__subtitle,\n.cn-mail-tab__hint {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-mail-tab__template-row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-end;\n\tgap: 8px;\n}\n\n.cn-mail-tab__template-select {\n\tflex: 1 1 260px;\n}\n\n.cn-mail-tab__unresolved {\n\tmargin: 4px 0 0;\n\tpadding-inline-start: 20px;\n}\n\n.cn-mail-tab__unresolved code {\n\tbackground: var(--color-background-dark);\n\tpadding: 2px 6px;\n\tborder-radius: var(--border-radius);\n\tfont-family: var(--font-face-mono, monospace);\n}\n</style>\n"],"names":["CnEmailTab","CnEmptyContent","t"],"mappings":"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"}
|
|
@@ -63,7 +63,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
63
63
|
? (openBlock(), createElementBlock("h3", _hoisted_4, toDisplayString($options.heading), 1 /* TEXT */))
|
|
64
64
|
: createCommentVNode("v-if", true),
|
|
65
65
|
($options.description)
|
|
66
|
-
? (openBlock(), createElementBlock("
|
|
66
|
+
? (openBlock(), createElementBlock("div", _hoisted_5, toDisplayString($options.description), 1 /* TEXT */))
|
|
67
67
|
: createCommentVNode("v-if", true)
|
|
68
68
|
]),
|
|
69
69
|
($options.hasHeaderActions)
|
|
@@ -153,7 +153,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
153
153
|
]))
|
|
154
154
|
: createCommentVNode("v-if", true),
|
|
155
155
|
($options.hint)
|
|
156
|
-
? (openBlock(), createElementBlock("
|
|
156
|
+
? (openBlock(), createElementBlock("div", _hoisted_8, toDisplayString($options.hint), 1 /* TEXT */))
|
|
157
157
|
: createCommentVNode("v-if", true),
|
|
158
158
|
($data.error)
|
|
159
159
|
? (openBlock(), createBlock(_component_NcNoteCard, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnMailTab.vue3.js","sources":["../../../../src/components/CnMailTab/CnMailTab.vue?vue&type=template&id=12407f1c&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-mail-tab\" data-testid=\"cn-mail-tab\">\n\t\t<CnEmptyContent\n\t\t\tv-if=\"unavailable\"\n\t\t\t:name=\"t('nextcloud-vue', 'Mail is not available')\"\n\t\t\t:description=\"t('nextcloud-vue', 'Install Nextcloud Mail to draft and link mail here.')\">\n\t\t\t<template #icon>\n\t\t\t\t<EmailOffOutline :size=\"48\" />\n\t\t\t</template>\n\t\t</CnEmptyContent>\n\n\t\t<template v-else>\n\t\t\t<div v-if=\"heading || description || hasHeaderActions\" class=\"cn-mail-tab__header\" data-testid=\"cn-mail-tab-header\">\n\t\t\t\t<div class=\"cn-mail-tab__heading\">\n\t\t\t\t\t<h3 v-if=\"heading\" class=\"cn-mail-tab__title\">\n\t\t\t\t\t\t{{ heading }}\n\t\t\t\t\t</h3>\n\t\t\t\t\t<p v-if=\"description\" class=\"cn-mail-tab__subtitle\">\n\t\t\t\t\t\t{{ description }}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"hasHeaderActions\" class=\"cn-mail-tab__header-actions\">\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"showLinkAction\"\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\tdata-testid=\"cn-mail-tab-header-link\"\n\t\t\t\t\t\t@click=\"openLinkPicker\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Link mail') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"showComposeAction\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t:disabled=\"draftEnabled && (closed || drafting)\"\n\t\t\t\t\t\t:href=\"draftEnabled ? undefined : composeUrl\"\n\t\t\t\t\t\t:target=\"draftEnabled ? undefined : '_blank'\"\n\t\t\t\t\t\tdata-testid=\"cn-mail-tab-header-compose\"\n\t\t\t\t\t\t@click=\"onCompose\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<EmailEditOutline :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Compose mail') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"draftEnabled\" class=\"cn-mail-tab__template-row\" data-testid=\"cn-mail-tab-template-row\">\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-if=\"templatesEnabled\"\n\t\t\t\t\tv-model=\"selectedTemplate\"\n\t\t\t\t\tclass=\"cn-mail-tab__template-select\"\n\t\t\t\t\t:options=\"templates\"\n\t\t\t\t\t:disabled=\"templates.length === 0\"\n\t\t\t\t\t:inputLabel=\"t('nextcloud-vue', 'Email template')\"\n\t\t\t\t\t:label=\"templateLabelField\"\n\t\t\t\t\t:trackBy=\"templateIdField\"\n\t\t\t\t\t:placeholder=\"templatePlaceholder\"\n\t\t\t\t\t:clearable=\"true\" />\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"templatesEnabled\"\n\t\t\t\t\t:disabled=\"closed || drafting || !selectedTemplate\"\n\t\t\t\t\tdata-testid=\"cn-mail-tab-draft-template\"\n\t\t\t\t\t@click=\"draftFromTemplate\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Open draft from template') }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"!showComposeAction || draftSource.blankButton === true\"\n\t\t\t\t\t:disabled=\"closed || drafting\"\n\t\t\t\t\tdata-testid=\"cn-mail-tab-draft-blank\"\n\t\t\t\t\t@click=\"draftBlank\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Empty draft') }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t\t<p v-if=\"hint\" class=\"cn-mail-tab__hint\">\n\t\t\t\t{{ hint }}\n\t\t\t</p>\n\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"error\"\n\t\t\t\ttype=\"error\"\n\t\t\t\t:heading=\"t('nextcloud-vue', 'Could not open draft')\"\n\t\t\t\trole=\"alert\">\n\t\t\t\t{{ error }}\n\t\t\t</NcNoteCard>\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"unresolved.length > 0\"\n\t\t\t\ttype=\"warning\"\n\t\t\t\trole=\"status\"\n\t\t\t\tdata-testid=\"cn-mail-tab-unresolved\">\n\t\t\t\t<p>{{ t('nextcloud-vue', 'Some template fields could not be filled. Fill these in the draft yourself:') }}</p>\n\t\t\t\t<ul class=\"cn-mail-tab__unresolved\">\n\t\t\t\t\t<li v-for=\"name in unresolved\" :key=\"name\">\n\t\t\t\t\t\t<code>{{ placeholderOf(name) }}</code>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</NcNoteCard>\n\n\t\t\t<CnEmailTab\n\t\t\t\tv-if=\"linkedMail && objectKey\"\n\t\t\t\tref=\"linkedMail\"\n\t\t\t\tclass=\"cn-mail-tab__linked\"\n\t\t\t\t:showActions=\"!hasHeaderActions\"\n\t\t\t\t:objectId=\"objectKey\"\n\t\t\t\t:register=\"registerKey\"\n\t\t\t\t:schema=\"schemaKey\" />\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcNoteCard, NcSelect } from '@nextcloud/vue'\nimport EmailEditOutline from 'vue-material-design-icons/EmailEditOutline.vue'\nimport EmailOffOutline from 'vue-material-design-icons/EmailOffOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport CnEmailTab from '../../integrations/builtin/email/CnEmailTab.vue'\nimport CnEmptyContent from '../CnEmptyContent/CnEmptyContent.vue'\nimport { fetchEndpointSource, interpolateUrlTokens } from '../../composables/useEndpointSource.js'\nimport { evaluateVisibleWhenLocal } from '../../utils/visibleWhen.js'\n\n/** The actions `content.headerActions` can put in the tab's header. */\nconst HEADER_ACTIONS = ['compose', 'link']\n\n/**\n * Read a reference that may be a slug, an id or an object carrying either.\n *\n * @param {string|number|object} value The reference.\n * @return {string} The slug or id, or ''.\n */\nfunction keyOf(value) {\n\tif (value === null || value === undefined) {\n\t\treturn ''\n\t}\n\tif (typeof value === 'object') {\n\t\treturn String(value.slug || value.id || value.uuid || '')\n\t}\n\treturn String(value)\n}\n\n/**\n * CnMailTab — the mail of one record: drafts in Nextcloud Mail, from a\n * template or empty, above the mail linked to the record.\n *\n * Registered as the `mail` widget type. The app owns the endpoints and the\n * texts; the tab never sends mail. A draft is a POST to `draft.url`, whose\n * response may carry `draftUrl` (opened in a new tab) and `unresolved` (the\n * template fields the server could not fill). A 404 there means the app has\n * no Mail to draft into, and the tab says so instead of the controls.\n *\n * ```json\n * {\n * \"type\": \"mail\",\n * \"content\": {\n * \"heading\": \"Email\",\n * \"description\": \"Mail linked to this case\",\n * \"hint\": \"The draft opens in Nextcloud Mail.\",\n * \"templates\": { \"url\": \"/apps/app/api/types/@object.type/templates\", \"responsePath\": \"results\", \"labelField\": \"name\", \"idField\": \"id\" },\n * \"noTemplatesText\": \"No templates for this type yet\",\n * \"draft\": { \"url\": \"/apps/app/api/records/@objectId/templates/{templateId}/draft\", \"blankTemplateId\": \"blank\" },\n * \"closedWhen\": { \"field\": \"endDate\", \"op\": \"notEmpty\" },\n * \"linkedMail\": true,\n * \"headerActions\": [\"link\", \"compose\"]\n * }\n * }\n * ```\n *\n * URLs take the `@objectId` / `@object.<field>` tokens of `endpointSource`;\n * `{templateId}` in the draft URL is the chosen template's id, or\n * `blankTemplateId` for an empty draft. Without `templates` the row offers\n * the empty draft only; without `draft` there is no row. `closedWhen` is a\n * local visibleWhen condition on the record that disables drafting.\n * `linkedMail: false` leaves the linked mail to a separate `email`\n * integration card. `headerActions` puts buttons beside the heading:\n * `compose` opens the empty draft (or Nextcloud Mail's composer when there\n * is no `draft`) and replaces the row's empty-draft button, unless\n * `draft.blankButton` is true (DqZaakEmail draws both); `link` opens the\n * linked mail's picker and needs the linked mail. The linked mail then drops\n * its own buttons. Texts go through the host's translate.\n */\nexport default {\n\tname: 'CnMailTab',\n\n\tcomponents: {\n\t\tCnEmailTab,\n\t\tCnEmptyContent,\n\t\tEmailEditOutline,\n\t\tEmailOffOutline,\n\t\tLinkVariant,\n\t\tNcButton,\n\t\tNcNoteCard,\n\t\tNcSelect,\n\t},\n\n\tinject: {\n\t\t/** The detail page's object context (`{ objectId, object }`), for tokens. */\n\t\tcnObjectContext: { default: null },\n\t\t/** The host's translate, for the manifest texts. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget config: `{ heading?, description?, hint?, templates?,\n\t\t * noTemplatesText?, draft?, closedWhen?, linkedMail?, headerActions? }`, as in the\n\t\t * component description and docs/components/cn-mail-tab.md.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The bound record's id, for `@objectId` and the linked mail. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record, for `@object.<field>` and `closedWhen`. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The bound record's register, for the linked mail. */\n\t\tregister: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record's schema, for the linked mail. */\n\t\tschema: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['drafted'],\n\n\tdata() {\n\t\treturn {\n\t\t\ttemplates: [],\n\t\t\tselectedTemplate: null,\n\t\t\tdrafting: false,\n\t\t\terror: '',\n\t\t\tunresolved: [],\n\t\t\tunavailable: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The record, from the prop or the page context. */\n\t\trecord() {\n\t\t\tif (this.objectData) {\n\t\t\t\treturn this.objectData\n\t\t\t}\n\t\t\tconst ctx = this.cnObjectContext\n\t\t\tconst value = ctx && typeof ctx === 'object' && 'value' in ctx ? ctx.value : ctx\n\t\t\treturn (value && value.object) || {}\n\t\t},\n\n\t\t/** @return {string} The record id. */\n\t\tobjectKey() {\n\t\t\treturn keyOf(this.objectId) || keyOf(this.record.id) || keyOf(this.record['@self'] && this.record['@self'].id)\n\t\t},\n\n\t\t/** @return {string} The register slug or id. */\n\t\tregisterKey() {\n\t\t\treturn keyOf(this.register)\n\t\t},\n\n\t\t/** @return {string} The schema slug or id. */\n\t\tschemaKey() {\n\t\t\treturn keyOf(this.schema)\n\t\t},\n\n\t\t/** @return {object} The token context for the URLs. */\n\t\ttokenContext() {\n\t\t\treturn { objectId: this.objectKey, object: this.record }\n\t\t},\n\n\t\t/** @return {string} The heading, translated, or ''. */\n\t\theading() {\n\t\t\treturn this.tr(this.content.heading)\n\t\t},\n\n\t\t/** @return {string} The line under the heading, translated, or ''. */\n\t\tdescription() {\n\t\t\treturn this.tr(this.content.description)\n\t\t},\n\n\t\t/** @return {string} The line under the template row, translated, or ''. */\n\t\thint() {\n\t\t\treturn this.tr(this.content.hint)\n\t\t},\n\n\t\t/** @return {object|null} The templates endpoint, or null. */\n\t\ttemplatesSource() {\n\t\t\tconst source = this.content.templates\n\t\t\treturn source && typeof source === 'object' && source.url ? source : null\n\t\t},\n\n\t\t/** @return {object|null} The draft endpoint, or null. */\n\t\tdraftSource() {\n\t\t\tconst source = this.content.draft\n\t\t\treturn source && typeof source === 'object' && source.url ? source : null\n\t\t},\n\n\t\t/** @return {boolean} Whether the template select and its button show. */\n\t\ttemplatesEnabled() {\n\t\t\treturn this.templatesSource !== null\n\t\t},\n\n\t\t/** @return {boolean} Whether the template row shows. */\n\t\tdraftEnabled() {\n\t\t\treturn this.draftSource !== null\n\t\t},\n\n\t\t/** @return {string} The template field shown in the select. */\n\t\ttemplateLabelField() {\n\t\t\treturn (this.templatesSource && this.templatesSource.labelField) || 'name'\n\t\t},\n\n\t\t/** @return {string} The template field that identifies it. */\n\t\ttemplateIdField() {\n\t\t\treturn (this.templatesSource && this.templatesSource.idField) || 'id'\n\t\t},\n\n\t\t/** @return {string} The select's placeholder. */\n\t\ttemplatePlaceholder() {\n\t\t\tif (this.templates.length === 0) {\n\t\t\t\treturn this.tr(this.content.noTemplatesText) || t('nextcloud-vue', 'No templates yet')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Select a template (optional)…')\n\t\t},\n\n\t\t/** @return {boolean} Whether `closedWhen` holds for the record. */\n\t\tclosed() {\n\t\t\tconst cond = this.content.closedWhen\n\t\t\tif (!cond) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn evaluateVisibleWhenLocal(cond, this.record)\n\t\t},\n\n\t\t/** @return {string} The templates URL after tokens, to refetch on change. */\n\t\ttemplatesUrl() {\n\t\t\treturn this.templatesSource ? interpolateUrlTokens(this.templatesSource.url, this.tokenContext) : ''\n\t\t},\n\n\t\t/** @return {boolean} Whether the linked mail list shows. */\n\t\tlinkedMail() {\n\t\t\treturn this.content.linkedMail !== false\n\t\t},\n\n\t\t/**\n\t\t * The known header actions the config asks for.\n\t\t *\n\t\t * @return {string[]} `compose` and or `link`.\n\t\t * @spec openspec/changes/mail-tab-compose-and-link/specs/mail-tab-compose-and-link/spec.md#requirement-the-mail-tab-can-carry-compose-mail-and-link-mail-in-its-header\n\t\t */\n\t\theaderActions() {\n\t\t\tconst list = Array.isArray(this.content.headerActions) ? this.content.headerActions : []\n\t\t\treturn list.filter((key) => HEADER_ACTIONS.includes(key))\n\t\t},\n\n\t\t/** @return {boolean} Whether the header shows \"Compose mail\". */\n\t\tshowComposeAction() {\n\t\t\treturn this.headerActions.includes('compose')\n\t\t},\n\n\t\t/** @return {boolean} Whether the header shows \"Link mail\": it opens the linked mail's picker. */\n\t\tshowLinkAction() {\n\t\t\treturn this.headerActions.includes('link') && this.linkedMail && this.objectKey !== ''\n\t\t},\n\n\t\t/** @return {boolean} Whether the header holds any button. */\n\t\thasHeaderActions() {\n\t\t\treturn this.showComposeAction || this.showLinkAction\n\t\t},\n\n\t\t/** @return {string} Nextcloud Mail's composer, for an app without a draft endpoint. */\n\t\tcomposeUrl() {\n\t\t\treturn (typeof OC !== 'undefined' && typeof OC.generateUrl === 'function')\n\t\t\t\t? OC.generateUrl('/apps/mail/box/draft')\n\t\t\t\t: '/index.php/apps/mail/box/draft'\n\t\t},\n\t},\n\n\twatch: {\n\t\ttemplatesUrl: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tthis.loadTemplates()\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Translate a manifest text through the host.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The translation, or '' for no text.\n\t\t */\n\t\ttr(text) {\n\t\t\tif (typeof text !== 'string' || text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.cnTranslate(text)\n\t\t},\n\n\t\t/**\n\t\t * Read the templates. A failed read leaves the empty draft usable.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadTemplates() {\n\t\t\tif (!this.templatesSource) {\n\t\t\t\tthis.templates = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst payload = await fetchEndpointSource(this.templatesSource, this.tokenContext)\n\t\t\t\tconst list = Array.isArray(payload) ? payload : (payload && Array.isArray(payload.results) ? payload.results : [])\n\t\t\t\tthis.templates = list\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnMailTab] could not read the templates', err)\n\t\t\t\tthis.templates = []\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tdraftFromTemplate() {\n\t\t\treturn this.openDraft(this.selectedTemplate)\n\t\t},\n\n\t\t/**\n\t\t * \"Compose mail\": the empty draft through the app's endpoint. Without\n\t\t * one the button is a link to Mail's composer and needs no handler.\n\t\t *\n\t\t * @return {Promise<void>|undefined}\n\t\t */\n\t\tonCompose() {\n\t\t\treturn this.draftEnabled ? this.draftBlank() : undefined\n\t\t},\n\n\t\t/**\n\t\t * \"Link mail\": the linked mail's picker, which links the chosen\n\t\t * message to the record through its own endpoint.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\topenLinkPicker() {\n\t\t\tconst linked = this.$refs.linkedMail\n\t\t\tif (linked && typeof linked.openPicker === 'function') {\n\t\t\t\tlinked.openPicker()\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tdraftBlank() {\n\t\t\tthis.selectedTemplate = null\n\t\t\treturn this.openDraft(null)\n\t\t},\n\n\t\t/**\n\t\t * Ask the app to create a Nextcloud Mail draft and open it.\n\t\t *\n\t\t * @param {object|null} template The chosen template, or null for an empty draft.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync openDraft(template) {\n\t\t\tif (this.closed || !this.draftSource) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.drafting = true\n\t\t\tthis.error = ''\n\t\t\tthis.unresolved = []\n\t\t\ttry {\n\t\t\t\tconst id = template ? template[this.templateIdField] : (this.draftSource.blankTemplateId || 'blank')\n\t\t\t\tconst path = interpolateUrlTokens(this.draftSource.url, this.tokenContext)\n\t\t\t\t\t.replace('{templateId}', encodeURIComponent(String(id)))\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst { data } = await axios.post(generateUrl(path), {})\n\t\t\t\tthis.unresolved = Array.isArray(data && data.unresolved) ? data.unresolved : []\n\t\t\t\tif (data && data.draftUrl) {\n\t\t\t\t\twindow.open(data.draftUrl, '_blank', 'noopener')\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event drafted A draft was made from a template. Carries the\n\t\t\t\t * draft endpoint's response, including `draftUrl` and\n\t\t\t\t * `unresolved` when the endpoint returns them.\n\t\t\t\t * @type {object}\n\t\t\t\t */\n\t\t\t\tthis.$emit('drafted', data)\n\t\t\t} catch (err) {\n\t\t\t\tconst response = err && err.response\n\t\t\t\tif (response && response.status === 404) {\n\t\t\t\t\tthis.unavailable = true\n\t\t\t\t}\n\t\t\t\tthis.error = (response && response.data && response.data.message)\n\t\t\t\t\t|| (err && err.message)\n\t\t\t\t\t|| t('nextcloud-vue', 'The draft could not be opened.')\n\t\t\t} finally {\n\t\t\t\tthis.drafting = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write a template field the way the template spells it.\n\t\t *\n\t\t * @param {string} name The field name.\n\t\t * @return {string} `{{name}}`.\n\t\t */\n\t\tplaceholderOf(name) {\n\t\t\treturn `{{${name}}}`\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-mail-tab {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n\tpadding: 12px;\n}\n\n.cn-mail-tab__header {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 10px 16px;\n}\n\n.cn-mail-tab__heading {\n\tdisplay: flex;\n\tflex: 1 1 260px;\n\tflex-direction: column;\n\tgap: 3px;\n\tmin-width: 0;\n}\n\n.cn-mail-tab__header-actions {\n\tdisplay: flex;\n\tflex: none;\n\tflex-wrap: wrap;\n\tgap: 8px;\n}\n\n.cn-mail-tab__title {\n\tmargin: 0;\n\tfont-size: 16px;\n\tfont-weight: bold;\n}\n\n.cn-mail-tab__subtitle,\n.cn-mail-tab__hint {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-mail-tab__template-row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-end;\n\tgap: 8px;\n}\n\n.cn-mail-tab__template-select {\n\tflex: 1 1 260px;\n}\n\n.cn-mail-tab__unresolved {\n\tmargin: 4px 0 0;\n\tpadding-inline-start: 20px;\n}\n\n.cn-mail-tab__unresolved code {\n\tbackground: var(--color-background-dark);\n\tpadding: 2px 6px;\n\tborder-radius: var(--border-radius);\n\tfont-family: var(--font-face-mono, monospace);\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createBlock","_createVNode","_toDisplayString","_createTextVNode","_createElementVNode","_Fragment","_renderList"],"mappings":";;;AASM,EAAA,KAAK,EAAC,aAAa;AAAC,EAAA,aAAW,EAAC,aAAa;;;;AAW1C,EAAA,KAAA,EAAA,qBAAA;AAAA,EAAA,aAAA,EAAA,oBAAA;;AACA,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,sBAAsB,GAAA;;;AACf,EAAA,KAAA,EAAA,oBAAA;;;;AAGK,EAAA,KAAI,EAAE,uBAAuB;;;;AAIvB,EAAA,KAAK,EAAC,6BAAG;;;;AA2Bd,EAAA,KAAK,EAAC,2BAAM;AAAA,EAAA,aAAA,EAAA,0BAAA;;;;AA2B3B,EAAA,KAAA,EAAA,mBAAA;;AAiBL,MAAA,UAAA,GAAA,EAAA,KAAA,EAAA,yBAAA,GAAA;;;;;;;;;;;;AA3FL,EAAA,QAAAA,SAAA,EAAA,EAAAC,kBAAA,QAAA;KAEM,KAAU,CAAA,WAAA;sBADhBC,WAOgB,CAAA,yBAAA,EAAA;;AALf,UAAA,IAAI,EAAE,QAAC,CAAA,CAAA,CAAA,eAAA,EAAA,uBAAA,CAAA;AACP,UAAA,WAAW,EAAE,QAAC,CAAA,CAAA,CAAA,eAAA,EAAA,qDAAA,CAAA;;wBAEd,MAAc;YAAdC,WAAc,CAAA,0BAAA,EAAA,EAAA,IAAA,EAAA,EAAA,EAAA,CAAA;;;;;WAKT,QAAA,CAAA,OAAA,IAAA,QAAA,CAAA,WAAA,IAAA,QAAA,CAAA,gBAAA;+CAkCA,iBAAA,EAAA;mCA1BC,iBAAA,EAAA;mBANK,QAAO,CAAA,OAAA;oCAAhBF,kBAEG,CAAA,IAAA,EAFH,UAEG,EAAAG,eAAA,CADD,QAAA,CAAA,OAAA,CAAA,EAAA,CAAA,YAAA;;mBAEM,QAAW,CAAA,WAAA;oCAApBH,kBAEI,CAAA,GAAA,EAFJ,UAEI,EAAAG,eAAA,CADA,QAAI,CAAA,WAAA,CAAA,EAAA,CAAA,YAAA;;;iBAGE,QAAgB,CAAA,gBAAA;AAAzB,qBAAAJ,SAAA,EAAA,EAAAC,kBAAA,CAwBI,OAxBJ,UAwBI,EAAA;uBAtBE,QAAS,CAAA,cAAA;mDAQL,CAAA,mBAAA,EAAA;;mCAPH,EAAC,WAAW;AACnB,4BAAA,aAAU,EAAA,yBAAA;AACT,4BAAA,OAAK,EAAE,QAAc,CAAA,cAAA;;AACb,4BAAA,IAAA,UACR,MAAM;8BAANE,WAAM,CAAA,sBAAA,EAAA,EAAA,IAAA,EAAA,EAAA,EAAA,CAAA;;6CACI,MACX;AADW,8BAAAE,eAAA,CAAA,GACX,mBAAG,QAAC,CAAA,CAAA,CAAA,eAAA,EAAA,WAAA,CAAA,CAAA,EAAA,CAAA,YAAA;;;;;uBAGE,QAAgB,CAAA,iBAAA;wCADtBH,WAYU,CAAA,mBAAA,EAAA;;AAVV,4BAAA,OAAO,EAAC,SAAS;AAChB,4BAAA,QAAQ,EAAE,QAAA,CAAA,YAAO,KAAA,QAAA,CAAA,MAAA,IAAA,KAAA,CAAA,QAAA,CAAA;AACjB,4BAAA,IAAI,EAAE,QAAA,CAAA,YAAQ,GAAA,SAAA,GAAA,QAAA,CAAA,UAAA;4BACd,MAAM,EAAE,QAAY,CAAA,YAAA,GAAG,SAAO,GAAA,QAAA;AAC/B,4BAAA,aAAU,EAAA,4BAAA;AACT,4BAAA,OAAK,EAAE,QAAS,CAAA,SAAA;;AACR,4BAAA,IAAA,UACR,MAAE;8BAAFC,WAAE,CAAA,2BAAA,EAAA,EAAA,IAAA,EAAA,EAAA,EAAA,CAAA;;6CACD;AAAA,8BAAAE,eAAA,CAAA;;;;;;;;;WAMM,QAAY,CAAA,YAAA;AAArB,eAAAL,SAAA,EAAA,EAAAC,kBAAA,QAAA;iBAEM,QAAgB,CAAA,gBAAA;kCADtBC,WAUoB,CAAA,mBAAA,EAAA;;kCARX,KAAgB,CAAA,gBAAA;mFAAhB,KAAgB,CAAA,gBAAA,IAAA,MAAA,CAAA,CAAA;AACzB,sBAAA,KAAK,EAAC,8BAAoB;AACzB,sBAAA,OAAO,EAAE,KAAS,CAAA,SAAA;sBAClB,QAAQ,EAAE,KAAS,CAAA,SAAA,CAAC,MAAM,KAAA,CAAA;AAC1B,sBAAA,UAAU,EAAE,QAAC,CAAA,CAAA,CAAA,eAAA,EAAA,gBAAA,CAAA;AACb,sBAAA,KAAK,EAAC,QAAA,CAAA,kBAAA;AACN,sBAAA,OAAO,EAAE,QAAY,CAAA,eAAA;AACrB,sBAAA,WAAW,EAAE,QAAmB,CAAA,mBAAA;AAChC,sBAAA,SAAS,EAAE,IAAI;;;iBAEV,QAAgB,CAAA,gBAAA;6CAKZ,CAAA,mBAAA,EAAA;;AAJT,sBAAA,QAAQ,EAAA,QAAA,CAAA,MAAA,IAAA,KAAA,CAAA,QAAA,KAAA,KAAA,CAAA,gBAAA;AACT,sBAAA,aAAO,EAAA,4BAAA;AACN,sBAAA,OAAK,EAAE,QAAiB,CAAA,iBAAA;;uCACzB,MAA6B;wDAA1B,QAAC,CAAA,CAAA,CAAA,eAAA,EAAA,0BAAA,CAAA,CAAA,EAAA,CAAA,YAAA;;;;;kBAGG,QAAiB,CAAA,iBAAA,IAAG,QAAW,CAAA,WAAA,CAAA,WAAA,KAAA,IAAA;kCADtCA,WAMC,CAAA,mBAAA,EAAA;;sBAJA,QAAQ,EAAA,QAAA,CAAA,MAAA,IAAA,KAAA,CAAA,QAAA;AACT,sBAAA,aAAG,EAAA,yBAAA;AACF,sBAAA,OAAK,EAAE,QAAU,CAAA,UAAA;;uCAClB,MAAQ;wDAAL,QAAC,CAAA,CAAA,CAAA,eAAA,EAAA,aAAA,CAAA,CAAA,EAAA,CAAA,YAAA;;;;;;;WAGG,QAAC,CAAA,IAAA;4BAAVD,kBAEI,CAAA,GAAA,EAFJ,UAEI,EAAAG,eAAA,CADA,QAAI,CAAA,IAAA,CAAA,EAAA,CAAA,YAAA;;WAID,KAAI,CAAA,KAAA;uCAKE,CAAA,qBAAA,EAAA;;AAJZ,gBAAA,IAAI,EAAC,OAAM;AACV,gBAAA,OAAO,EAAC,QAAA,CAAA,CAAA,CAAA,eAAA,EAAA,sBAAA,CAAA;AACT,gBAAA,IAAI,EAAC,OAAO;;iCACZ,MAAW;kDAAR,KAAK,CAAA,KAAA,CAAA,EAAA,CAAA,YAAA;;;;;AAGF,UAAA,CAAA,KAAA,CAAA,UAAU,CAAC,MAAM,GAAA,CAAA;4BADvBF,WAWY,CAAA,qBAAA,EAAA;;AATZ,gBAAA,IAAI,EAAC,SAAS;AACb,gBAAA,IAAI,EAAA,QAAQ;AACX,gBAAA,aAAS,EAAC,wBAAwB;;uCAC/B;qCAAA,2BAAA,QAAA,CAAA,CAAA,CAAA,eAAA,EAAA,6EAAA,CAAA,CAAA,EAAA,CAAA,YAAA;AACL,kBAAAI,kBAAA,CAIK,MAJL,UAIK,EAAA;sCAHJL,kBAEK,CAAAM,QAAA,EAAA,IAAA,EAAAC,UAAA,CAFK,KAAA,CAAA,UAAA,EAAA,CAAA,IAAA,KAAA;2CAAVP,kBAEK,CAAA,IAAA,EAAA,EAFK,GAAA,EAAA,IAAA,EAAA,EAAA;;;;;;;;;AAOL,UAAA,CAAA,QAAA,CAAA,UAAU,IAAI,QAAA,CAAA,SAAA;4BADpBC;;AAEA,gBAAA,GAAG,EAAC,YAAY;AAChB,gBAAA,KAAK,EAAC,qBAAe;AACpB,gBAAA,WAAO,GAAA,QAAA,CAAA,gBAAA;AACP,gBAAA,QAAC,EAAA,QAAA,CAAA,SAAA;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"CnMailTab.vue3.js","sources":["../../../../src/components/CnMailTab/CnMailTab.vue?vue&type=template&id=0fa12b0e&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-mail-tab\" data-testid=\"cn-mail-tab\">\n\t\t<CnEmptyContent\n\t\t\tv-if=\"unavailable\"\n\t\t\t:name=\"t('nextcloud-vue', 'Mail is not available')\"\n\t\t\t:description=\"t('nextcloud-vue', 'Install Nextcloud Mail to draft and link mail here.')\">\n\t\t\t<template #icon>\n\t\t\t\t<EmailOffOutline :size=\"48\" />\n\t\t\t</template>\n\t\t</CnEmptyContent>\n\n\t\t<template v-else>\n\t\t\t<div v-if=\"heading || description || hasHeaderActions\" class=\"cn-mail-tab__header\" data-testid=\"cn-mail-tab-header\">\n\t\t\t\t<div class=\"cn-mail-tab__heading\">\n\t\t\t\t\t<h3 v-if=\"heading\" class=\"cn-mail-tab__title\">\n\t\t\t\t\t\t{{ heading }}\n\t\t\t\t\t</h3>\n\t\t\t\t\t<!-- Divs, not <p>: a theme that sizes every paragraph with !important\n\t\t\t\t\t would override the card's 14px description. -->\n\t\t\t\t\t<div v-if=\"description\" class=\"cn-mail-tab__subtitle\">\n\t\t\t\t\t\t{{ description }}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"hasHeaderActions\" class=\"cn-mail-tab__header-actions\">\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"showLinkAction\"\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\tdata-testid=\"cn-mail-tab-header-link\"\n\t\t\t\t\t\t@click=\"openLinkPicker\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Link mail') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"showComposeAction\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t:disabled=\"draftEnabled && (closed || drafting)\"\n\t\t\t\t\t\t:href=\"draftEnabled ? undefined : composeUrl\"\n\t\t\t\t\t\t:target=\"draftEnabled ? undefined : '_blank'\"\n\t\t\t\t\t\tdata-testid=\"cn-mail-tab-header-compose\"\n\t\t\t\t\t\t@click=\"onCompose\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<EmailEditOutline :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Compose mail') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"draftEnabled\" class=\"cn-mail-tab__template-row\" data-testid=\"cn-mail-tab-template-row\">\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-if=\"templatesEnabled\"\n\t\t\t\t\tv-model=\"selectedTemplate\"\n\t\t\t\t\tclass=\"cn-mail-tab__template-select\"\n\t\t\t\t\t:options=\"templates\"\n\t\t\t\t\t:disabled=\"templates.length === 0\"\n\t\t\t\t\t:inputLabel=\"t('nextcloud-vue', 'Email template')\"\n\t\t\t\t\t:label=\"templateLabelField\"\n\t\t\t\t\t:trackBy=\"templateIdField\"\n\t\t\t\t\t:placeholder=\"templatePlaceholder\"\n\t\t\t\t\t:clearable=\"true\" />\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"templatesEnabled\"\n\t\t\t\t\t:disabled=\"closed || drafting || !selectedTemplate\"\n\t\t\t\t\tdata-testid=\"cn-mail-tab-draft-template\"\n\t\t\t\t\t@click=\"draftFromTemplate\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Open draft from template') }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"!showComposeAction || draftSource.blankButton === true\"\n\t\t\t\t\t:disabled=\"closed || drafting\"\n\t\t\t\t\tdata-testid=\"cn-mail-tab-draft-blank\"\n\t\t\t\t\t@click=\"draftBlank\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Empty draft') }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t\t<div v-if=\"hint\" class=\"cn-mail-tab__hint\">\n\t\t\t\t{{ hint }}\n\t\t\t</div>\n\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"error\"\n\t\t\t\ttype=\"error\"\n\t\t\t\t:heading=\"t('nextcloud-vue', 'Could not open draft')\"\n\t\t\t\trole=\"alert\">\n\t\t\t\t{{ error }}\n\t\t\t</NcNoteCard>\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"unresolved.length > 0\"\n\t\t\t\ttype=\"warning\"\n\t\t\t\trole=\"status\"\n\t\t\t\tdata-testid=\"cn-mail-tab-unresolved\">\n\t\t\t\t<p>{{ t('nextcloud-vue', 'Some template fields could not be filled. Fill these in the draft yourself:') }}</p>\n\t\t\t\t<ul class=\"cn-mail-tab__unresolved\">\n\t\t\t\t\t<li v-for=\"name in unresolved\" :key=\"name\">\n\t\t\t\t\t\t<code>{{ placeholderOf(name) }}</code>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</NcNoteCard>\n\n\t\t\t<CnEmailTab\n\t\t\t\tv-if=\"linkedMail && objectKey\"\n\t\t\t\tref=\"linkedMail\"\n\t\t\t\tclass=\"cn-mail-tab__linked\"\n\t\t\t\t:showActions=\"!hasHeaderActions\"\n\t\t\t\t:objectId=\"objectKey\"\n\t\t\t\t:register=\"registerKey\"\n\t\t\t\t:schema=\"schemaKey\" />\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcNoteCard, NcSelect } from '@nextcloud/vue'\nimport EmailEditOutline from 'vue-material-design-icons/EmailEditOutline.vue'\nimport EmailOffOutline from 'vue-material-design-icons/EmailOffOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport CnEmailTab from '../../integrations/builtin/email/CnEmailTab.vue'\nimport CnEmptyContent from '../CnEmptyContent/CnEmptyContent.vue'\nimport { fetchEndpointSource, interpolateUrlTokens } from '../../composables/useEndpointSource.js'\nimport { evaluateVisibleWhenLocal } from '../../utils/visibleWhen.js'\n\n/** The actions `content.headerActions` can put in the tab's header. */\nconst HEADER_ACTIONS = ['compose', 'link']\n\n/**\n * Read a reference that may be a slug, an id or an object carrying either.\n *\n * @param {string|number|object} value The reference.\n * @return {string} The slug or id, or ''.\n */\nfunction keyOf(value) {\n\tif (value === null || value === undefined) {\n\t\treturn ''\n\t}\n\tif (typeof value === 'object') {\n\t\treturn String(value.slug || value.id || value.uuid || '')\n\t}\n\treturn String(value)\n}\n\n/**\n * CnMailTab — the mail of one record: drafts in Nextcloud Mail, from a\n * template or empty, above the mail linked to the record.\n *\n * Registered as the `mail` widget type. The app owns the endpoints and the\n * texts; the tab never sends mail. A draft is a POST to `draft.url`, whose\n * response may carry `draftUrl` (opened in a new tab) and `unresolved` (the\n * template fields the server could not fill). A 404 there means the app has\n * no Mail to draft into, and the tab says so instead of the controls.\n *\n * ```json\n * {\n * \"type\": \"mail\",\n * \"content\": {\n * \"heading\": \"Email\",\n * \"description\": \"Mail linked to this case\",\n * \"hint\": \"The draft opens in Nextcloud Mail.\",\n * \"templates\": { \"url\": \"/apps/app/api/types/@object.type/templates\", \"responsePath\": \"results\", \"labelField\": \"name\", \"idField\": \"id\" },\n * \"noTemplatesText\": \"No templates for this type yet\",\n * \"draft\": { \"url\": \"/apps/app/api/records/@objectId/templates/{templateId}/draft\", \"blankTemplateId\": \"blank\" },\n * \"closedWhen\": { \"field\": \"endDate\", \"op\": \"notEmpty\" },\n * \"linkedMail\": true,\n * \"headerActions\": [\"link\", \"compose\"]\n * }\n * }\n * ```\n *\n * URLs take the `@objectId` / `@object.<field>` tokens of `endpointSource`;\n * `{templateId}` in the draft URL is the chosen template's id, or\n * `blankTemplateId` for an empty draft. Without `templates` the row offers\n * the empty draft only; without `draft` there is no row. `closedWhen` is a\n * local visibleWhen condition on the record that disables drafting.\n * `linkedMail: false` leaves the linked mail to a separate `email`\n * integration card. `headerActions` puts buttons beside the heading:\n * `compose` opens the empty draft (or Nextcloud Mail's composer when there\n * is no `draft`) and replaces the row's empty-draft button, unless\n * `draft.blankButton` is true (DqZaakEmail draws both); `link` opens the\n * linked mail's picker and needs the linked mail. The linked mail then drops\n * its own buttons. Texts go through the host's translate.\n */\nexport default {\n\tname: 'CnMailTab',\n\n\tcomponents: {\n\t\tCnEmailTab,\n\t\tCnEmptyContent,\n\t\tEmailEditOutline,\n\t\tEmailOffOutline,\n\t\tLinkVariant,\n\t\tNcButton,\n\t\tNcNoteCard,\n\t\tNcSelect,\n\t},\n\n\tinject: {\n\t\t/** The detail page's object context (`{ objectId, object }`), for tokens. */\n\t\tcnObjectContext: { default: null },\n\t\t/** The host's translate, for the manifest texts. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget config: `{ heading?, description?, hint?, templates?,\n\t\t * noTemplatesText?, draft?, closedWhen?, linkedMail?, headerActions? }`, as in the\n\t\t * component description and docs/components/cn-mail-tab.md.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The bound record's id, for `@objectId` and the linked mail. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record, for `@object.<field>` and `closedWhen`. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The bound record's register, for the linked mail. */\n\t\tregister: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record's schema, for the linked mail. */\n\t\tschema: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['drafted'],\n\n\tdata() {\n\t\treturn {\n\t\t\ttemplates: [],\n\t\t\tselectedTemplate: null,\n\t\t\tdrafting: false,\n\t\t\terror: '',\n\t\t\tunresolved: [],\n\t\t\tunavailable: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The record, from the prop or the page context. */\n\t\trecord() {\n\t\t\tif (this.objectData) {\n\t\t\t\treturn this.objectData\n\t\t\t}\n\t\t\tconst ctx = this.cnObjectContext\n\t\t\tconst value = ctx && typeof ctx === 'object' && 'value' in ctx ? ctx.value : ctx\n\t\t\treturn (value && value.object) || {}\n\t\t},\n\n\t\t/** @return {string} The record id. */\n\t\tobjectKey() {\n\t\t\treturn keyOf(this.objectId) || keyOf(this.record.id) || keyOf(this.record['@self'] && this.record['@self'].id)\n\t\t},\n\n\t\t/** @return {string} The register slug or id. */\n\t\tregisterKey() {\n\t\t\treturn keyOf(this.register)\n\t\t},\n\n\t\t/** @return {string} The schema slug or id. */\n\t\tschemaKey() {\n\t\t\treturn keyOf(this.schema)\n\t\t},\n\n\t\t/** @return {object} The token context for the URLs. */\n\t\ttokenContext() {\n\t\t\treturn { objectId: this.objectKey, object: this.record }\n\t\t},\n\n\t\t/** @return {string} The heading, translated, or ''. */\n\t\theading() {\n\t\t\treturn this.tr(this.content.heading)\n\t\t},\n\n\t\t/** @return {string} The line under the heading, translated, or ''. */\n\t\tdescription() {\n\t\t\treturn this.tr(this.content.description)\n\t\t},\n\n\t\t/** @return {string} The line under the template row, translated, or ''. */\n\t\thint() {\n\t\t\treturn this.tr(this.content.hint)\n\t\t},\n\n\t\t/** @return {object|null} The templates endpoint, or null. */\n\t\ttemplatesSource() {\n\t\t\tconst source = this.content.templates\n\t\t\treturn source && typeof source === 'object' && source.url ? source : null\n\t\t},\n\n\t\t/** @return {object|null} The draft endpoint, or null. */\n\t\tdraftSource() {\n\t\t\tconst source = this.content.draft\n\t\t\treturn source && typeof source === 'object' && source.url ? source : null\n\t\t},\n\n\t\t/** @return {boolean} Whether the template select and its button show. */\n\t\ttemplatesEnabled() {\n\t\t\treturn this.templatesSource !== null\n\t\t},\n\n\t\t/** @return {boolean} Whether the template row shows. */\n\t\tdraftEnabled() {\n\t\t\treturn this.draftSource !== null\n\t\t},\n\n\t\t/** @return {string} The template field shown in the select. */\n\t\ttemplateLabelField() {\n\t\t\treturn (this.templatesSource && this.templatesSource.labelField) || 'name'\n\t\t},\n\n\t\t/** @return {string} The template field that identifies it. */\n\t\ttemplateIdField() {\n\t\t\treturn (this.templatesSource && this.templatesSource.idField) || 'id'\n\t\t},\n\n\t\t/** @return {string} The select's placeholder. */\n\t\ttemplatePlaceholder() {\n\t\t\tif (this.templates.length === 0) {\n\t\t\t\treturn this.tr(this.content.noTemplatesText) || t('nextcloud-vue', 'No templates yet')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Select a template (optional)…')\n\t\t},\n\n\t\t/** @return {boolean} Whether `closedWhen` holds for the record. */\n\t\tclosed() {\n\t\t\tconst cond = this.content.closedWhen\n\t\t\tif (!cond) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn evaluateVisibleWhenLocal(cond, this.record)\n\t\t},\n\n\t\t/** @return {string} The templates URL after tokens, to refetch on change. */\n\t\ttemplatesUrl() {\n\t\t\treturn this.templatesSource ? interpolateUrlTokens(this.templatesSource.url, this.tokenContext) : ''\n\t\t},\n\n\t\t/** @return {boolean} Whether the linked mail list shows. */\n\t\tlinkedMail() {\n\t\t\treturn this.content.linkedMail !== false\n\t\t},\n\n\t\t/**\n\t\t * The known header actions the config asks for.\n\t\t *\n\t\t * @return {string[]} `compose` and or `link`.\n\t\t * @spec openspec/changes/mail-tab-compose-and-link/specs/mail-tab-compose-and-link/spec.md#requirement-the-mail-tab-can-carry-compose-mail-and-link-mail-in-its-header\n\t\t */\n\t\theaderActions() {\n\t\t\tconst list = Array.isArray(this.content.headerActions) ? this.content.headerActions : []\n\t\t\treturn list.filter((key) => HEADER_ACTIONS.includes(key))\n\t\t},\n\n\t\t/** @return {boolean} Whether the header shows \"Compose mail\". */\n\t\tshowComposeAction() {\n\t\t\treturn this.headerActions.includes('compose')\n\t\t},\n\n\t\t/** @return {boolean} Whether the header shows \"Link mail\": it opens the linked mail's picker. */\n\t\tshowLinkAction() {\n\t\t\treturn this.headerActions.includes('link') && this.linkedMail && this.objectKey !== ''\n\t\t},\n\n\t\t/** @return {boolean} Whether the header holds any button. */\n\t\thasHeaderActions() {\n\t\t\treturn this.showComposeAction || this.showLinkAction\n\t\t},\n\n\t\t/** @return {string} Nextcloud Mail's composer, for an app without a draft endpoint. */\n\t\tcomposeUrl() {\n\t\t\treturn (typeof OC !== 'undefined' && typeof OC.generateUrl === 'function')\n\t\t\t\t? OC.generateUrl('/apps/mail/box/draft')\n\t\t\t\t: '/index.php/apps/mail/box/draft'\n\t\t},\n\t},\n\n\twatch: {\n\t\ttemplatesUrl: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tthis.loadTemplates()\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Translate a manifest text through the host.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The translation, or '' for no text.\n\t\t */\n\t\ttr(text) {\n\t\t\tif (typeof text !== 'string' || text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.cnTranslate(text)\n\t\t},\n\n\t\t/**\n\t\t * Read the templates. A failed read leaves the empty draft usable.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadTemplates() {\n\t\t\tif (!this.templatesSource) {\n\t\t\t\tthis.templates = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst payload = await fetchEndpointSource(this.templatesSource, this.tokenContext)\n\t\t\t\tconst list = Array.isArray(payload) ? payload : (payload && Array.isArray(payload.results) ? payload.results : [])\n\t\t\t\tthis.templates = list\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnMailTab] could not read the templates', err)\n\t\t\t\tthis.templates = []\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tdraftFromTemplate() {\n\t\t\treturn this.openDraft(this.selectedTemplate)\n\t\t},\n\n\t\t/**\n\t\t * \"Compose mail\": the empty draft through the app's endpoint. Without\n\t\t * one the button is a link to Mail's composer and needs no handler.\n\t\t *\n\t\t * @return {Promise<void>|undefined}\n\t\t */\n\t\tonCompose() {\n\t\t\treturn this.draftEnabled ? this.draftBlank() : undefined\n\t\t},\n\n\t\t/**\n\t\t * \"Link mail\": the linked mail's picker, which links the chosen\n\t\t * message to the record through its own endpoint.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\topenLinkPicker() {\n\t\t\tconst linked = this.$refs.linkedMail\n\t\t\tif (linked && typeof linked.openPicker === 'function') {\n\t\t\t\tlinked.openPicker()\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tdraftBlank() {\n\t\t\tthis.selectedTemplate = null\n\t\t\treturn this.openDraft(null)\n\t\t},\n\n\t\t/**\n\t\t * Ask the app to create a Nextcloud Mail draft and open it.\n\t\t *\n\t\t * @param {object|null} template The chosen template, or null for an empty draft.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync openDraft(template) {\n\t\t\tif (this.closed || !this.draftSource) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.drafting = true\n\t\t\tthis.error = ''\n\t\t\tthis.unresolved = []\n\t\t\ttry {\n\t\t\t\tconst id = template ? template[this.templateIdField] : (this.draftSource.blankTemplateId || 'blank')\n\t\t\t\tconst path = interpolateUrlTokens(this.draftSource.url, this.tokenContext)\n\t\t\t\t\t.replace('{templateId}', encodeURIComponent(String(id)))\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst { data } = await axios.post(generateUrl(path), {})\n\t\t\t\tthis.unresolved = Array.isArray(data && data.unresolved) ? data.unresolved : []\n\t\t\t\tif (data && data.draftUrl) {\n\t\t\t\t\twindow.open(data.draftUrl, '_blank', 'noopener')\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event drafted A draft was made from a template. Carries the\n\t\t\t\t * draft endpoint's response, including `draftUrl` and\n\t\t\t\t * `unresolved` when the endpoint returns them.\n\t\t\t\t * @type {object}\n\t\t\t\t */\n\t\t\t\tthis.$emit('drafted', data)\n\t\t\t} catch (err) {\n\t\t\t\tconst response = err && err.response\n\t\t\t\tif (response && response.status === 404) {\n\t\t\t\t\tthis.unavailable = true\n\t\t\t\t}\n\t\t\t\tthis.error = (response && response.data && response.data.message)\n\t\t\t\t\t|| (err && err.message)\n\t\t\t\t\t|| t('nextcloud-vue', 'The draft could not be opened.')\n\t\t\t} finally {\n\t\t\t\tthis.drafting = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write a template field the way the template spells it.\n\t\t *\n\t\t * @param {string} name The field name.\n\t\t * @return {string} `{{name}}`.\n\t\t */\n\t\tplaceholderOf(name) {\n\t\t\treturn `{{${name}}}`\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-mail-tab {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n\tpadding: 12px;\n}\n\n.cn-mail-tab__header {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 10px 16px;\n}\n\n.cn-mail-tab__heading {\n\tdisplay: flex;\n\tflex: 1 1 260px;\n\tflex-direction: column;\n\tgap: 3px;\n\tmin-width: 0;\n}\n\n.cn-mail-tab__header-actions {\n\tdisplay: flex;\n\tflex: none;\n\tflex-wrap: wrap;\n\tgap: 8px;\n}\n\n.cn-mail-tab__title {\n\tmargin: 0;\n\tfont-size: 16px;\n\tfont-weight: bold;\n}\n\n.cn-mail-tab__subtitle,\n.cn-mail-tab__hint {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-mail-tab__template-row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-end;\n\tgap: 8px;\n}\n\n.cn-mail-tab__template-select {\n\tflex: 1 1 260px;\n}\n\n.cn-mail-tab__unresolved {\n\tmargin: 4px 0 0;\n\tpadding-inline-start: 20px;\n}\n\n.cn-mail-tab__unresolved code {\n\tbackground: var(--color-background-dark);\n\tpadding: 2px 6px;\n\tborder-radius: var(--border-radius);\n\tfont-family: var(--font-face-mono, monospace);\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createBlock","_createVNode","_toDisplayString","_createTextVNode","_createElementVNode","_Fragment","_renderList"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import script from './CnNotesCard.vue2.js';
|
|
|
2
2
|
import { render } from './CnNotesCard.vue3.js';
|
|
3
3
|
|
|
4
4
|
script.render = render;
|
|
5
|
-
script.__scopeId = "data-v-
|
|
5
|
+
script.__scopeId = "data-v-25437d6c";
|
|
6
6
|
const __sfc_main = script;
|
|
7
7
|
|
|
8
8
|
export { __sfc_main as default };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnNotesCard.vue2.js","sources":["../../../../src/components/CnNotesCard/CnNotesCard.vue"],"sourcesContent":["<!--\n CnNotesCard — Inline notes card for detail pages.\n\n Displays up to 5 recent notes with author, content, and timestamp.\n Includes an add-note form and integrates CnUserActionMenu on author names.\n Wraps CnDetailCard for consistent styling.\n-->\n<template>\n\t<CnDetailCard :title=\"titleLabel\"\n\t\t:icon=\"CommentTextOutline\"\n\t\t:collapsible=\"collapsible\"\n\t\t:chromeless=\"chromeless\">\n\t\t<template v-if=\"composerBehindButton\" #actions>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"secondary\"\n\t\t\t\t:aria-expanded=\"composerShown ? 'true' : 'false'\"\n\t\t\t\tdata-testid=\"cn-notes-card-open-composer\"\n\t\t\t\t@click=\"composerOpen = !composerOpen\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<NotePlusOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ addNoteLabel }}\n\t\t\t</NcButton>\n\t\t</template>\n\t\t<div class=\"cn-notes-card\">\n\t\t\t<!-- Add note input -->\n\t\t\t<div v-if=\"!unavailable && composerShown\" class=\"cn-notes-card__add-form\">\n\t\t\t\t<!-- Replying: the form says to whom; a reply to a reply goes to the top of its thread. -->\n\t\t\t\t<p\n\t\t\t\t\tv-if=\"replyTo\"\n\t\t\t\t\tclass=\"cn-notes-card__replying\"\n\t\t\t\t\trole=\"status\"\n\t\t\t\t\tdata-testid=\"cn-notes-card-replying\">\n\t\t\t\t\t{{ replyingLabel }}\n\t\t\t\t\t<NcButton variant=\"tertiary\" @click=\"replyTo = null\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Cancel') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</p>\n\t\t\t\t<CnNoteComposer\n\t\t\t\t\tclass=\"cn-notes-card__textarea\"\n\t\t\t\t\t:modelValue=\"newNoteText\"\n\t\t\t\t\t:placeholder=\"addNotePlaceholder\"\n\t\t\t\t\t:register=\"isFileSource || isSourceMode ? '' : registerId\"\n\t\t\t\t\t:schema=\"isFileSource || isSourceMode ? '' : schemaId\"\n\t\t\t\t\t:objectId=\"isFileSource || isSourceMode ? '' : objectId\"\n\t\t\t\t\t:apiBase=\"apiBase\"\n\t\t\t\t\t@update:modelValue=\"newNoteText = $event\"\n\t\t\t\t\t@submit=\"submitNote\" />\n\t\t\t\t<!-- Internal or public, chosen before the note is written (only when the host says the caller may set it). -->\n\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\tv-if=\"visibilityToggleShown\"\n\t\t\t\t\t:modelValue=\"newNoteVisibility === 'public'\"\n\t\t\t\t\ttype=\"switch\"\n\t\t\t\t\tdata-testid=\"cn-notes-card-visibility-switch\"\n\t\t\t\t\t@update:modelValue=\"(on) => newNoteVisibility = on ? 'public' : 'internal'\">\n\t\t\t\t\t{{ publicSwitchLabel }}\n\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t:disabled=\"!newNoteText.trim() || noteSaving\"\n\t\t\t\t\t@click=\"submitNote\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<Send :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ addNoteLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\n\t\t\t<!-- Loading state -->\n\t\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t\t<!-- Empty state -->\n\t\t\t<div v-else-if=\"unavailable\" class=\"cn-notes-card__empty\" data-testid=\"cn-notes-card-unavailable\">\n\t\t\t\t{{ unavailableLabel }}\n\t\t\t</div>\n\t\t\t<p v-else-if=\"failed\"\n\t\t\t\tclass=\"cn-notes-card__empty\"\n\t\t\t\trole=\"alert\"\n\t\t\t\tdata-testid=\"cn-notes-card-failed\">\n\t\t\t\t{{ failedLabel }}\n\t\t\t</p>\n\t\t\t<div v-else-if=\"allNotes.length === 0\" class=\"cn-notes-card__empty\">\n\t\t\t\t{{ noNotesLabel }}\n\t\t\t</div>\n\n\t\t\t<!-- Notes list (last 5), threaded: a note, then its replies. -->\n\t\t\t<div v-else class=\"cn-notes-card__list\">\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"thread in threads\"\n\t\t\t\t\t:key=\"thread.note.id\"\n\t\t\t\t\tclass=\"cn-notes-card__thread\"\n\t\t\t\t\trole=\"group\"\n\t\t\t\t\t:aria-label=\"threadLabel(thread.note)\">\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-for=\"row in threadRows(thread)\"\n\t\t\t\t\t\t:key=\"row.note.id\"\n\t\t\t\t\t\tclass=\"cn-notes-card__note\"\n\t\t\t\t\t\t:class=\"{ 'cn-notes-card__note--reply': row.isReply }\">\n\t\t\t\t\t\t<span class=\"cn-notes-card__initials\" aria-hidden=\"true\">{{ initialsOf(getNoteAuthorName(row.note)) }}</span>\n\t\t\t\t\t\t<div class=\"cn-notes-card__note-header\">\n\t\t\t\t\t\t\t<CnUserActionMenu\n\t\t\t\t\t\t\t\tv-if=\"!isCurrentUser(row.note)\"\n\t\t\t\t\t\t\t\t:userId=\"getNoteAuthorId(row.note)\"\n\t\t\t\t\t\t\t\t:displayName=\"getNoteAuthorName(row.note)\">\n\t\t\t\t\t\t\t\t<strong class=\"cn-notes-card__author\">{{ getNoteAuthorName(row.note) }}</strong>\n\t\t\t\t\t\t\t</CnUserActionMenu>\n\t\t\t\t\t\t\t<strong v-else class=\"cn-notes-card__author cn-notes-card__author--self\">\n\t\t\t\t\t\t\t\t{{ getNoteAuthorName(row.note) }}\n\t\t\t\t\t\t\t</strong>\n\t\t\t\t\t\t\t<span class=\"cn-notes-card__time\">{{ formatDate(row.note.creationDateTime || row.note.created || row.note.createdAt) }}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div v-if=\"visibilityShown\" class=\"cn-notes-card__visibility\">\n\t\t\t\t\t\t\t<CnVisibilityChip :visibility=\"row.note.visibility\" />\n\t\t\t\t\t\t\t<NcButton\n\t\t\t\t\t\t\t\tv-if=\"visibilityToggleShown\"\n\t\t\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-notes-card-visibility-toggle\"\n\t\t\t\t\t\t\t\t@click=\"toggleVisibility(row.note)\">\n\t\t\t\t\t\t\t\t{{ noteVisibility(row.note) === 'public' ? makeInternalLabel : makePublicLabel }}\n\t\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<p v-if=\"row.quote\" class=\"cn-notes-card__quote\" data-testid=\"cn-notes-card-quote\">\n\t\t\t\t\t\t\t{{ row.quote }}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<p class=\"cn-notes-card__body\">\n\t\t\t\t\t\t\t<CnNoteBody :message=\"row.note.message || row.note.content || ''\" :record=\"isFileSource || isSourceMode ? null : record\" />\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<NcButton\n\t\t\t\t\t\t\tv-if=\"canReply\"\n\t\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t:aria-label=\"replyAria(row.note)\"\n\t\t\t\t\t\t\tdata-testid=\"cn-notes-card-reply\"\n\t\t\t\t\t\t\t@click=\"replyTo = row.note\">\n\t\t\t\t\t\t\t{{ replyLabel }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t\t<NcButton\n\t\t\t\t\t\t\tv-if=\"canDeleteNote(row.note)\"\n\t\t\t\t\t\t\tvariant=\"tertiary-no-background\"\n\t\t\t\t\t\t\tclass=\"cn-notes-card__delete-btn\"\n\t\t\t\t\t\t\t:aria-label=\"deleteLabel\"\n\t\t\t\t\t\t\t@click=\"confirmDelete(row.note)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<Delete :size=\"16\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Footer: \"Show all\" link -->\n\t\t<template v-if=\"allNotes.length > maxDisplay\" #footer>\n\t\t\t<button\n\t\t\t\tclass=\"cn-notes-card__show-all\"\n\t\t\t\t@click=\"$emit('show-all')\">\n\t\t\t\t{{ showAllLabel }} ({{ allNotes.length }})\n\t\t\t</button>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcCheckboxRadioSwitch, NcLoadingIcon } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport CommentTextOutline from 'vue-material-design-icons/CommentTextOutline.vue'\nimport Delete from 'vue-material-design-icons/Delete.vue'\nimport NotePlusOutline from 'vue-material-design-icons/NotePlusOutline.vue'\nimport Send from 'vue-material-design-icons/Send.vue'\nimport CnDetailCard from '../CnDetailCard/CnDetailCard.vue'\nimport CnNoteBody from '../CnNoteBody/CnNoteBody.vue'\nimport CnNoteComposer from '../CnNoteComposer/CnNoteComposer.vue'\nimport CnUserActionMenu from '../CnUserActionMenu/CnUserActionMenu.vue'\nimport CnVisibilityChip from '../CnVisibilityChip/CnVisibilityChip.vue'\nimport { useFileComments } from '../../composables/useFileComments.js'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { initialsOf } from '../../utils/initials.js'\nimport { leafSourceUrl } from '../../utils/leafSourceUrl.js'\nimport { extractMentionedGroupIds, extractMentionedIds } from '../../utils/mentions.js'\nimport { quoteFor, supportsReplies, threadNotes, threadRootId } from '../../utils/noteThreads.js'\nimport { serverErrorMessage } from '../../utils/serverErrorMessage.js'\nimport { userLocale } from '../../utils/userLocale.js'\n\n/**\n * CnNotesCard — Inline notes widget for detail pages.\n *\n * Shows up to 5 recent notes with add/delete functionality.\n * Integrates CnUserActionMenu on author names for quick communication.\n *\n * Basic usage\n * ```vue\n * <CnNotesCard\n * register-id=\"uuid-register\"\n * schema-id=\"uuid-schema\"\n * object-id=\"uuid-object\" />\n * ```\n *\n * With sidebar sync\n * ```vue\n * <CnNotesCard\n * register-id=\"reg\"\n * schema-id=\"schema\"\n * object-id=\"obj\"\n * @note-added=\"refreshNotes\"\n * @note-deleted=\"refreshNotes\"\n * @show-all=\"openSidebarNotesTab\" />\n * ```\n *\n * @event note-added Emitted after a new note is successfully persisted. Payload: the created note object.\n * @event note-deleted Emitted after a note is successfully deleted. Payload: the deleted note ID.\n * @event visibility-changed Emitted after a note's visibility was changed. Payload: `{ id, visibility }`.\n * @event mention Emitted after a note that mentions someone was added. Payload: `{ objectId, register, schema, noteId, mentionedUserIds }`, plus `mentionedGroupIds` when a group (`@\"group/<gid>\"`) is mentioned. nc-vue notifies nobody; the listener does.\n * @event show-all Emitted when the user clicks the \"Show all\" button — parents typically open a full notes sidebar tab.\n */\n/**\n * The server's own sentence on a refused note write, '' when it gave none.\n *\n * @param {Response} response The refused response.\n * @return {Promise<string>} The message.\n */\nasync function refusalMessage(response) {\n\ttry {\n\t\tconst data = await response.json()\n\t\tconst message = serverErrorMessage({ message: '-', response: { data } })\n\t\treturn message === '-' ? '' : message\n\t} catch {\n\t\treturn ''\n\t}\n}\n\nexport default {\n\tname: 'CnNotesCard',\n\n\tcomponents: {\n\t\tCnDetailCard,\n\t\tCnNoteBody,\n\t\tCnNoteComposer,\n\t\tCnUserActionMenu,\n\t\tCnVisibilityChip,\n\t\tNcButton,\n\t\tNcCheckboxRadioSwitch,\n\t\tNcLoadingIcon,\n\t\tNotePlusOutline,\n\t\tSend,\n\t\tDelete,\n\t},\n\n\tprops: {\n\t\t/** OpenRegister register ID */\n\t\tregisterId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** OpenRegister schema ID */\n\t\tschemaId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Object UUID */\n\t\tobjectId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Show the notes of a plain Nextcloud file instead of an OpenRegister\n\t\t * object: the file's id. Used when no `objectId` is given; the notes\n\t\t * are the file's comments (`/remote.php/dav/comments/files/{fileId}`),\n\t\t * the same ones the Files sidebar shows.\n\t\t */\n\t\tfileId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Base API URL for OpenRegister */\n\t\tapiBase: {\n\t\t\ttype: String,\n\t\t\tdefault: '/apps/openregister/api',\n\t\t},\n\n\t\t/**\n\t\t * Read the notes of something that is not an OpenRegister object:\n\t\t * `{ kind: 'flow-task', id }` reads `${apiBase}/flow-tasks/{id}/notes`.\n\t\t * `id` may be left out or written `@objectId` for `objectId`.\n\t\t *\n\t\t * @type {{kind: string, id?: (string|number)}|null}\n\t\t */\n\t\tsource: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * A notes URL that replaces the object's own: the list and the add go\n\t\t * to it, a delete to `{endpoint}/{noteId}`. `{id}` is filled with the\n\t\t * source's id or `objectId`.\n\t\t */\n\t\tendpoint: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Maximum number of notes to display */\n\t\tmaxDisplay: {\n\t\t\ttype: Number,\n\t\t\tdefault: 5,\n\t\t},\n\n\t\t/** Whether the card is collapsible */\n\t\tcollapsible: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Render the notes body without the surrounding CnDetailCard.\n\t\t *\n\t\t * Set when a surface already supplies the card and the title, such as a\n\t\t * tab panel whose open tab names the panel. Leaving the card on there\n\t\t * nests a card inside a card and shows the label twice.\n\t\t */\n\t\tchromeless: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Where the add form lives: `inline` (default) keeps it open above the\n\t\t * notes; `button` puts it behind an \"Add note\" button in the card's\n\t\t * header (DqTaak \"Notities\"), and closes it again once the note is\n\t\t * saved. A reply opens it either way. Without the card's header\n\t\t * (`chromeless`) the form stays inline.\n\t\t *\n\t\t * @spec openspec/changes/notes-card-composer-button/specs/notes-card-composer-button/spec.md#requirement-a-notes-card-can-keep-its-composer-behind-a-header-button\n\t\t */\n\t\tcomposer: {\n\t\t\ttype: String,\n\t\t\tdefault: 'inline',\n\t\t\tvalidator: (v) => ['inline', 'button'].includes(v),\n\t\t},\n\n\t\t/**\n\t\t * Show a chip on every note reading internal or public. A note without a\n\t\t * value reads as internal. Off by default, so a host that passes nothing\n\t\t * renders the card as before. Not used for a file source (file comments\n\t\t * carry no flag).\n\t\t */\n\t\tshowVisibility: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Whether the caller may set visibility on this object: the answer the\n\t\t * server gives for `update` on it. The host passes it; the card never\n\t\t * infers it from the current user. When true the card offers the\n\t\t * public switch in the add-note form and a toggle on each note.\n\t\t */\n\t\tcanSetVisibility: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t// --- Pre-translated labels ---\n\t\t/** Card header title. */\n\t\ttitleLabel: { type: String, default: () => t('nextcloud-vue', 'Notes') },\n\t\t/** Label for the submit button that creates a new note. */\n\t\taddNoteLabel: { type: String, default: () => t('nextcloud-vue', 'Add note') },\n\t\t/** Placeholder shown inside the new-note textarea before any input. */\n\t\taddNotePlaceholder: { type: String, default: () => t('nextcloud-vue', 'Write a note…') },\n\t\t/** Empty-state text shown when the object has zero notes. */\n\t\tnoNotesLabel: { type: String, default: () => t('nextcloud-vue', 'No notes yet') },\n\t\t/** Label for the \"Show all\" button rendered when the note list is truncated. */\n\t\tshowAllLabel: { type: String, default: () => t('nextcloud-vue', 'Show all') },\n\t\t/** Text shown instead of the notes when Nextcloud refuses a file's comments (no access). */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'Notes are not available for this file') },\n\t\t/** Label of the add-note switch that makes the note public. */\n\t\tpublicSwitchLabel: { type: String, default: () => t('nextcloud-vue', 'Public (visible to the customer)') },\n\t\t/** Label of the per-note action that makes an internal note public. */\n\t\tmakePublicLabel: { type: String, default: () => t('nextcloud-vue', 'Make public') },\n\t\t/** Label of the per-note action that makes a public note internal. */\n\t\tmakeInternalLabel: { type: String, default: () => t('nextcloud-vue', 'Make internal') },\n\t\t/** Label of the Reply button (shown only when the backend supports replies). */\n\t\treplyLabel: { type: String, default: () => t('nextcloud-vue', 'Reply') },\n\t\t/** Aria label for the per-note delete icon button. */\n\t\tdeleteLabel: { type: String, default: () => t('nextcloud-vue', 'Delete note') },\n\t\t/** Text shown when a `source` or `endpoint` read failed. */\n\t\tfailedLabel: { type: String, default: () => t('nextcloud-vue', 'The notes could not be loaded') },\n\t},\n\n\temits: ['mention', 'note-added', 'note-deleted', 'show-all', 'visibility-changed'],\n\n\tdata() {\n\t\treturn {\n\t\t\tCommentTextOutline: markRaw(CommentTextOutline),\n\t\t\tallNotes: [],\n\t\t\tloading: false,\n\t\t\tnewNoteText: '',\n\t\t\t/** Whether the add form is open while `composer` is `button`. */\n\t\t\tcomposerOpen: false,\n\t\t\t/** Visibility the next note is written with (only sent when the caller may set it). */\n\t\t\tnewNoteVisibility: 'internal',\n\t\t\tnoteSaving: false,\n\t\t\tdeleteConfirmId: null,\n\t\t\t/** The file source answered 403 or 404: no notes and no add field. */\n\t\t\tunavailable: false,\n\t\t\t/** A `source` or `endpoint` read failed: said, rather than shown as no notes. */\n\t\t\tfailed: false,\n\t\t\t/** The note a reply is being written to, or null. */\n\t\t\treplyTo: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {boolean} Whether the add form sits behind the header's button. */\n\t\tcomposerBehindButton() {\n\t\t\treturn this.composer === 'button' && !this.chromeless && !this.unavailable\n\t\t},\n\n\t\t/** @return {boolean} Whether the add form shows. */\n\t\tcomposerShown() {\n\t\t\treturn !this.composerBehindButton || this.composerOpen || Boolean(this.replyTo)\n\t\t},\n\n\t\t/** Chips are shown: the host asked for them (or lets the caller set visibility) and the notes are object notes. */\n\t\tvisibilityShown() {\n\t\t\treturn (this.showVisibility || this.canSetVisibility) && !this.isFileSource\n\t\t},\n\n\t\t/** The switch and the per-note toggle are offered: only when the host says the caller may set it. */\n\t\tvisibilityToggleShown() {\n\t\t\treturn this.canSetVisibility && !this.isFileSource\n\t\t},\n\n\t\t/**\n\t\t * The notes URL a `source` or `endpoint` names, '' while its record id\n\t\t * is unknown, or null when the card reads an object.\n\t\t *\n\t\t * @return {string|null}\n\t\t * @spec openspec/changes/leaf-cards-read-a-source/specs/leaf-cards-read-a-source/spec.md#requirement-a-leaf-card-reads-a-source-or-an-endpoint\n\t\t */\n\t\tsourceUrl() {\n\t\t\treturn leafSourceUrl({ source: this.source, endpoint: this.endpoint, apiBase: this.apiBase, objectId: this.objectId }, 'notes')\n\t\t},\n\n\t\t/** The notes come from a `source` or `endpoint`, not from an object. */\n\t\tisSourceMode() {\n\t\t\treturn this.sourceUrl !== null\n\t\t},\n\n\t\t/** @return {string} The notes URL the list, add and delete use. */\n\t\tnotesUrl() {\n\t\t\tif (this.isSourceMode) {\n\t\t\t\treturn this.sourceUrl\n\t\t\t}\n\t\t\treturn `${this.apiBase}/objects/${this.registerId}/${this.schemaId}/${this.objectId}/notes`\n\t\t},\n\n\t\t/** The file source applies: a file id and no object. */\n\t\tisFileSource() {\n\t\t\treturn this.fileId !== null && this.fileId !== undefined && this.fileId !== '' && !this.objectId\n\t\t},\n\n\t\t/** @return {{apiBase: string, register: string, schema: string, objectId: string}} Where this record's files live, for pasted images. */\n\t\trecord() {\n\t\t\treturn { apiBase: this.apiBase, register: this.registerId, schema: this.schemaId, objectId: this.objectId }\n\t\t},\n\n\t\t/** @return {boolean} Whether the backend supports replies: its notes carry a `parentId` key. Not for a file's comments. */\n\t\tcanReply() {\n\t\t\treturn !this.isFileSource && supportsReplies(this.allNotes)\n\t\t},\n\n\t\t/** @return {Array<{note: object, replies: object[]}>} The shown notes as threads. */\n\t\tthreads() {\n\t\t\treturn threadNotes(this.displayedNotes)\n\t\t},\n\n\t\t/** @return {string} The line above the form while replying. */\n\t\treplyingLabel() {\n\t\t\treturn this.replyTo ? t('nextcloud-vue', 'Replying to {author}', { author: this.getNoteAuthorName(this.replyTo) }) : ''\n\t\t},\n\n\t\tdisplayedNotes() {\n\t\t\t// Reverse chronological, limited to maxDisplay\n\t\t\tconst sorted = [...this.allNotes].sort((a, b) => {\n\t\t\t\tconst dateA = new Date(a.creationDateTime || a.created || a.createdAt || 0)\n\t\t\t\tconst dateB = new Date(b.creationDateTime || b.created || b.createdAt || 0)\n\t\t\t\treturn dateB - dateA\n\t\t\t})\n\t\t\treturn sorted.slice(0, this.maxDisplay)\n\t\t},\n\t},\n\n\twatch: {\n\t\tfileId: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tif (this.isFileSource) {\n\t\t\t\t\tthis.fetchNotes()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(newId) {\n\t\t\t\tif (newId && this.registerId && this.schemaId && !this.isSourceMode) {\n\t\t\t\t\tthis.fetchNotes()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\n\t\tsourceUrl: {\n\t\t\timmediate: true,\n\t\t\thandler(url) {\n\t\t\t\tif (typeof url === 'string' && url !== '') {\n\t\t\t\t\tthis.fetchNotes()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * A note's visibility; an entry with no value reads as internal.\n\t\t *\n\t\t * @param {object} note The note.\n\t\t * @return {'public'|'internal'} The visibility.\n\t\t */\n\t\tnoteVisibility(note) {\n\t\t\treturn note && note.visibility === 'public' ? 'public' : 'internal'\n\t\t},\n\n\t\t/**\n\t\t * The one place a note write goes out: creating a note and changing its\n\t\t * visibility both send through here, so the value is sent from one spot.\n\t\t *\n\t\t * @param {'POST'|'PATCH'} method The HTTP method.\n\t\t * @param {string} url The notes URL (with the note id for a change).\n\t\t * @param {object} body The JSON body.\n\t\t * @return {Promise<Response>} The response.\n\t\t */\n\t\twriteNote(method, url, body) {\n\t\t\treturn fetch(prefixUrl(url), { method, headers: buildHeaders(), body: JSON.stringify(body) })\n\t\t},\n\n\t\t/**\n\t\t * Flip a note between internal and public.\n\t\t *\n\t\t * @param {object} note The note.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync toggleVisibility(note) {\n\t\t\tif (!this.visibilityToggleShown) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst next = this.noteVisibility(note) === 'public' ? 'internal' : 'public'\n\t\t\ttry {\n\t\t\t\tconst url = `${this.notesUrl}/${note.id}`\n\t\t\t\tconst response = await this.writeNote('PATCH', url, { visibility: next })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.allNotes = this.allNotes.map((n) => (n.id === note.id ? { ...n, visibility: next } : n))\n\t\t\t\t\t/** @event visibility-changed Emitted after a note's visibility was changed. Payload: `{ id, visibility }`. */\n\t\t\t\t\tthis.$emit('visibility-changed', { id: note.id, visibility: next })\n\t\t\t\t} else {\n\t\t\t\t\tthis.showError('Failed to change visibility')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\t\t\tconsole.error('CnNotesCard: Failed to change visibility', err)\n\t\t\t\tthis.showError('Failed to change visibility')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The rows of one thread: the note, then its replies. A reply whose parent\n\t\t * is far up the list carries a one-line quote of it.\n\t\t *\n\t\t * @param {{note: object, replies: object[]}} thread The thread.\n\t\t * @return {Array<{note: object, isReply: boolean, quote: string}>} The rows.\n\t\t */\n\t\tthreadRows(thread) {\n\t\t\tconst flat = this.threads.flatMap((t2) => [t2.note, ...t2.replies])\n\t\t\treturn [\n\t\t\t\t{ note: thread.note, isReply: false, quote: '' },\n\t\t\t\t...thread.replies.map((reply) => ({ note: reply, isReply: true, quote: quoteFor(reply, flat) })),\n\t\t\t]\n\t\t},\n\n\t\t/**\n\t\t * @param {object} note A top-level note.\n\t\t * @return {string} The name of its thread for assistive technology.\n\t\t */\n\t\tthreadLabel(note) {\n\t\t\treturn t('nextcloud-vue', 'Note by {author} and its replies', { author: this.getNoteAuthorName(note) })\n\t\t},\n\n\t\t/**\n\t\t * @param {object} note The note a Reply button answers.\n\t\t * @return {string} The button's accessible name, naming the author.\n\t\t */\n\t\treplyAria(note) {\n\t\t\treturn t('nextcloud-vue', 'Reply to {author}', { author: this.getNoteAuthorName(note) })\n\t\t},\n\n\t\t/**\n\t\t * Tell the host a saved note mentions someone. The payload keeps the\n\t\t * user ids and adds the group ids only when a group is mentioned.\n\t\t *\n\t\t * @param {string} text The saved text.\n\t\t * @param {string|null} noteId The note's id.\n\t\t * @return {void}\n\t\t */\n\t\temitMentionEvent(text, noteId) {\n\t\t\tconst mentionedUserIds = extractMentionedIds(text)\n\t\t\tconst mentionedGroupIds = extractMentionedGroupIds(text)\n\t\t\tif (mentionedUserIds.length === 0 && mentionedGroupIds.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('mention', {\n\t\t\t\tobjectId: this.objectId,\n\t\t\t\tregister: this.registerId,\n\t\t\t\tschema: this.schemaId,\n\t\t\t\tnoteId,\n\t\t\t\tmentionedUserIds,\n\t\t\t\t...(mentionedGroupIds.length > 0 ? { mentionedGroupIds } : {}),\n\t\t\t})\n\t\t},\n\n\t\tgetNoteAuthorId(note) {\n\t\t\treturn note.actorId || note.author || ''\n\t\t},\n\n\t\tinitialsOf,\n\n\t\tgetNoteAuthorName(note) {\n\t\t\treturn note.actorDisplayName || note.author || 'Unknown'\n\t\t},\n\n\t\tisCurrentUser(note) {\n\t\t\tif (note.isCurrentUser === true) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tconst authorId = this.getNoteAuthorId(note)\n\t\t\tconst currentUser = typeof OC !== 'undefined' ? OC?.currentUser : null\n\t\t\treturn authorId === currentUser\n\t\t},\n\n\t\tcanDeleteNote(note) {\n\t\t\treturn this.isCurrentUser(note)\n\t\t},\n\n\t\tfileComments() {\n\t\t\treturn useFileComments(this.fileId)\n\t\t},\n\n\t\tasync fetchNotes() {\n\t\t\tif (this.isFileSource) {\n\t\t\t\tthis.loading = true\n\t\t\t\tthis.unavailable = false\n\t\t\t\ttry {\n\t\t\t\t\tthis.allNotes = await this.fileComments().list()\n\t\t\t\t} catch (err) {\n\t\t\t\t\tthis.allNotes = []\n\t\t\t\t\tthis.unavailable = err && (err.status === 403 || err.status === 404)\n\t\t\t\t\tif (!this.unavailable) {\n\t\t\t\t\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\t\t\t\t\tconsole.error('CnNotesCard: Failed to fetch file comments', err)\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tthis.loading = false\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.isSourceMode ? this.notesUrl === '' : (!this.registerId || !this.schemaId || !this.objectId)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.failed = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.notesUrl), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.allNotes = data.results || data || []\n\t\t\t\t} else if (this.isSourceMode) {\n\t\t\t\t\tthis.allNotes = []\n\t\t\t\t\tthis.failed = true\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tthis.failed = this.isSourceMode\n\t\t\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\t\t\tconsole.error('CnNotesCard: Failed to fetch notes', err)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync submitNote() {\n\t\t\tif (!this.newNoteText.trim() || this.noteSaving) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.noteSaving = true\n\t\t\tif (this.isFileSource) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.fileComments().add(this.newNoteText.trim())\n\t\t\t\t\tthis.composerOpen = false\n\t\t\t\t\tthis.newNoteText = ''\n\t\t\t\t\tawait this.fetchNotes()\n\t\t\t\t\tthis.$emit('note-added')\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\t\t\t\tconsole.error('CnNotesCard: Failed to add file comment', err)\n\t\t\t\t\tthis.showError('Failed to add note')\n\t\t\t\t} finally {\n\t\t\t\t\tthis.noteSaving = false\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst url = this.notesUrl\n\t\t\t\tconst body = { message: this.newNoteText.trim() }\n\t\t\t\tif (this.visibilityToggleShown) {\n\t\t\t\t\tbody.visibility = this.newNoteVisibility\n\t\t\t\t}\n\t\t\t\tif (this.replyTo) {\n\t\t\t\t\tbody.parentId = threadRootId(this.replyTo, this.allNotes)\n\t\t\t\t}\n\t\t\t\tconst response = await this.writeNote('POST', url, body)\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tlet noteId = null\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst created = await response.json()\n\t\t\t\t\t\tnoteId = (created && (created.id || (created.results && created.results.id))) || null\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Body not JSON: the event still fires with noteId null.\n\t\t\t\t\t}\n\t\t\t\t\tthis.emitMentionEvent(body.message, noteId)\n\t\t\t\t\tthis.replyTo = null\n\t\t\t\t\tthis.composerOpen = false\n\t\t\t\t\tthis.newNoteText = ''\n\t\t\t\t\tthis.newNoteVisibility = 'internal'\n\t\t\t\t\tawait this.fetchNotes()\n\t\t\t\t\tthis.$emit('note-added')\n\t\t\t\t} else {\n\t\t\t\t\tthis.showError(await refusalMessage(response) || 'Failed to add note')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\t\t\tconsole.error('CnNotesCard: Failed to add note', err)\n\t\t\t\tthis.showError('Failed to add note')\n\t\t\t} finally {\n\t\t\t\tthis.noteSaving = false\n\t\t\t}\n\t\t},\n\n\t\tasync confirmDelete(note) {\n\t\t\t// Simple inline confirmation — delete directly\n\t\t\tif (this.isFileSource) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.fileComments().remove(note.id)\n\t\t\t\t\tthis.allNotes = this.allNotes.filter((n) => n.id !== note.id)\n\t\t\t\t\tthis.$emit('note-deleted')\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\t\t\t\tconsole.error('CnNotesCard: Failed to delete file comment', err)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst url = `${this.notesUrl}/${note.id}`\n\t\t\t\tconst response = await fetch(prefixUrl(url), {\n\t\t\t\t\tmethod: 'DELETE',\n\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.allNotes = this.allNotes.filter((n) => n.id !== note.id)\n\t\t\t\t\tthis.$emit('note-deleted')\n\t\t\t\t} else {\n\t\t\t\t\tthis.showError(await refusalMessage(response) || 'Failed to delete note')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\t\t\tconsole.error('CnNotesCard: Failed to delete note', err)\n\t\t\t}\n\t\t},\n\n\t\tformatDate(dateStr) {\n\t\t\tif (!dateStr) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Date(dateStr).toLocaleString(userLocale(), {\n\t\t\t\t\tyear: 'numeric',\n\t\t\t\t\tmonth: 'short',\n\t\t\t\t\tday: 'numeric',\n\t\t\t\t\thour: '2-digit',\n\t\t\t\t\tminute: '2-digit',\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\treturn dateStr\n\t\t\t}\n\t\t},\n\n\t\tshowError(message) {\n\t\t\ttry {\n\t\t\t\timport('@nextcloud/dialogs').then(({ showError }) => {\n\t\t\t\t\tshowError(message)\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\t\t\tconsole.error(message)\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-notes-card__add-form {\n\tmargin-bottom: 12px;\n}\n\n.cn-notes-card__textarea {\n\twidth: 100%;\n\tmargin-bottom: 8px;\n\tbox-sizing: border-box;\n}\n\n.cn-notes-card__replying {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tmargin: 0 0 4px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-notes-card__note--reply {\n\tmargin-inline-start: 24px;\n}\n\n.cn-notes-card__quote {\n\tmargin: 0 0 2px;\n\tpadding-inline-start: 8px;\n\tborder-inline-start: 2px solid var(--color-border-dark);\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-notes-card__empty {\n\ttext-align: center;\n\tpadding: 16px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-notes-card__list {\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-notes-card__note {\n\tpadding: 10px 0;\n\tborder-bottom: 1px solid var(--color-border);\n\tposition: relative;\n}\n\n.cn-notes-card__note:last-child {\n\tborder-bottom: none;\n}\n\n.cn-notes-card__initials {\n\tdisplay: none;\n}\n\n.cn-notes-card__note-header {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\talign-items: baseline;\n\tmargin-bottom: 4px;\n}\n\n.cn-notes-card__author {\n\tfont-size: 13px;\n}\n\n.cn-notes-card__author--self {\n\tcolor: var(--color-main-text);\n}\n\n.cn-notes-card__visibility {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tmargin-bottom: 4px;\n}\n\n.cn-notes-card__time {\n\tfont-size: 11px;\n\tcolor: var(--color-text-maxcontrast);\n\tflex-shrink: 0;\n\tmargin-left: 8px;\n}\n\n.cn-notes-card__body {\n\tfont-size: 13px;\n\tmargin: 0;\n\twhite-space: pre-wrap;\n\toverflow-wrap: anywhere;\n\tpadding-right: 32px;\n}\n\n.cn-notes-card__delete-btn {\n\tposition: absolute;\n\ttop: 8px;\n\tright: -4px;\n\topacity: 0;\n\ttransition: opacity 0.15s ease;\n}\n\n.cn-notes-card__note:hover .cn-notes-card__delete-btn {\n\topacity: 1;\n}\n\n.cn-notes-card__show-all {\n\tbackground: none;\n\tborder: none;\n\tcolor: var(--color-primary-element);\n\tfont-size: 13px;\n\tfont-weight: 500;\n\tcursor: pointer;\n\tpadding: 0;\n\twidth: 100%;\n\ttext-align: center;\n}\n\n.cn-notes-card__show-all:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["CnDetailCard","CnNoteBody","CnNoteComposer","CnUserActionMenu","CnVisibilityChip","t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnNotesCard.vue2.js","sources":["../../../../src/components/CnNotesCard/CnNotesCard.vue"],"sourcesContent":["<!--\n CnNotesCard — Inline notes card for detail pages.\n\n Displays up to 5 recent notes with author, content, and timestamp.\n Includes an add-note form and integrates CnUserActionMenu on author names.\n Wraps CnDetailCard for consistent styling.\n-->\n<template>\n\t<CnDetailCard :title=\"titleLabel\"\n\t\t:icon=\"CommentTextOutline\"\n\t\t:collapsible=\"collapsible\"\n\t\t:chromeless=\"chromeless\">\n\t\t<template v-if=\"composerBehindButton\" #actions>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"secondary\"\n\t\t\t\t:aria-expanded=\"composerShown ? 'true' : 'false'\"\n\t\t\t\tdata-testid=\"cn-notes-card-open-composer\"\n\t\t\t\t@click=\"composerOpen = !composerOpen\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<NotePlusOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ addNoteLabel }}\n\t\t\t</NcButton>\n\t\t</template>\n\t\t<div class=\"cn-notes-card\">\n\t\t\t<!-- Add note input -->\n\t\t\t<div v-if=\"!unavailable && composerShown\" class=\"cn-notes-card__add-form\">\n\t\t\t\t<!-- Replying: the form says to whom; a reply to a reply goes to the top of its thread. -->\n\t\t\t\t<p\n\t\t\t\t\tv-if=\"replyTo\"\n\t\t\t\t\tclass=\"cn-notes-card__replying\"\n\t\t\t\t\trole=\"status\"\n\t\t\t\t\tdata-testid=\"cn-notes-card-replying\">\n\t\t\t\t\t{{ replyingLabel }}\n\t\t\t\t\t<NcButton variant=\"tertiary\" @click=\"replyTo = null\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Cancel') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</p>\n\t\t\t\t<CnNoteComposer\n\t\t\t\t\tclass=\"cn-notes-card__textarea\"\n\t\t\t\t\t:modelValue=\"newNoteText\"\n\t\t\t\t\t:placeholder=\"addNotePlaceholder\"\n\t\t\t\t\t:register=\"isFileSource || isSourceMode ? '' : registerId\"\n\t\t\t\t\t:schema=\"isFileSource || isSourceMode ? '' : schemaId\"\n\t\t\t\t\t:objectId=\"isFileSource || isSourceMode ? '' : objectId\"\n\t\t\t\t\t:apiBase=\"apiBase\"\n\t\t\t\t\t@update:modelValue=\"newNoteText = $event\"\n\t\t\t\t\t@submit=\"submitNote\" />\n\t\t\t\t<!-- Internal or public, chosen before the note is written (only when the host says the caller may set it). -->\n\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\tv-if=\"visibilityToggleShown\"\n\t\t\t\t\t:modelValue=\"newNoteVisibility === 'public'\"\n\t\t\t\t\ttype=\"switch\"\n\t\t\t\t\tdata-testid=\"cn-notes-card-visibility-switch\"\n\t\t\t\t\t@update:modelValue=\"(on) => newNoteVisibility = on ? 'public' : 'internal'\">\n\t\t\t\t\t{{ publicSwitchLabel }}\n\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t:disabled=\"!newNoteText.trim() || noteSaving\"\n\t\t\t\t\t@click=\"submitNote\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<Send :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ addNoteLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\n\t\t\t<!-- Loading state -->\n\t\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t\t<!-- Empty state -->\n\t\t\t<div v-else-if=\"unavailable\" class=\"cn-notes-card__empty\" data-testid=\"cn-notes-card-unavailable\">\n\t\t\t\t{{ unavailableLabel }}\n\t\t\t</div>\n\t\t\t<p v-else-if=\"failed\"\n\t\t\t\tclass=\"cn-notes-card__empty\"\n\t\t\t\trole=\"alert\"\n\t\t\t\tdata-testid=\"cn-notes-card-failed\">\n\t\t\t\t{{ failedLabel }}\n\t\t\t</p>\n\t\t\t<div v-else-if=\"allNotes.length === 0\" class=\"cn-notes-card__empty\">\n\t\t\t\t{{ noNotesLabel }}\n\t\t\t</div>\n\n\t\t\t<!-- Notes list (last 5), threaded: a note, then its replies. -->\n\t\t\t<div v-else class=\"cn-notes-card__list\">\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"thread in threads\"\n\t\t\t\t\t:key=\"thread.note.id\"\n\t\t\t\t\tclass=\"cn-notes-card__thread\"\n\t\t\t\t\trole=\"group\"\n\t\t\t\t\t:aria-label=\"threadLabel(thread.note)\">\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-for=\"row in threadRows(thread)\"\n\t\t\t\t\t\t:key=\"row.note.id\"\n\t\t\t\t\t\tclass=\"cn-notes-card__note\"\n\t\t\t\t\t\t:class=\"{ 'cn-notes-card__note--reply': row.isReply }\">\n\t\t\t\t\t\t<span class=\"cn-notes-card__initials\" aria-hidden=\"true\">{{ initialsOf(getNoteAuthorName(row.note)) }}</span>\n\t\t\t\t\t\t<div class=\"cn-notes-card__note-header\">\n\t\t\t\t\t\t\t<CnUserActionMenu\n\t\t\t\t\t\t\t\tv-if=\"!isCurrentUser(row.note)\"\n\t\t\t\t\t\t\t\t:userId=\"getNoteAuthorId(row.note)\"\n\t\t\t\t\t\t\t\t:displayName=\"getNoteAuthorName(row.note)\">\n\t\t\t\t\t\t\t\t<strong class=\"cn-notes-card__author\">{{ getNoteAuthorName(row.note) }}</strong>\n\t\t\t\t\t\t\t</CnUserActionMenu>\n\t\t\t\t\t\t\t<strong v-else class=\"cn-notes-card__author cn-notes-card__author--self\">\n\t\t\t\t\t\t\t\t{{ getNoteAuthorName(row.note) }}\n\t\t\t\t\t\t\t</strong>\n\t\t\t\t\t\t\t<span class=\"cn-notes-card__time\">{{ formatDate(row.note.creationDateTime || row.note.created || row.note.createdAt) }}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div v-if=\"visibilityShown\" class=\"cn-notes-card__visibility\">\n\t\t\t\t\t\t\t<CnVisibilityChip :visibility=\"row.note.visibility\" />\n\t\t\t\t\t\t\t<NcButton\n\t\t\t\t\t\t\t\tv-if=\"visibilityToggleShown\"\n\t\t\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-notes-card-visibility-toggle\"\n\t\t\t\t\t\t\t\t@click=\"toggleVisibility(row.note)\">\n\t\t\t\t\t\t\t\t{{ noteVisibility(row.note) === 'public' ? makeInternalLabel : makePublicLabel }}\n\t\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<p v-if=\"row.quote\" class=\"cn-notes-card__quote\" data-testid=\"cn-notes-card-quote\">\n\t\t\t\t\t\t\t{{ row.quote }}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<p class=\"cn-notes-card__body\">\n\t\t\t\t\t\t\t<CnNoteBody :message=\"row.note.message || row.note.content || ''\" :record=\"isFileSource || isSourceMode ? null : record\" />\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<NcButton\n\t\t\t\t\t\t\tv-if=\"canReply\"\n\t\t\t\t\t\t\tclass=\"cn-notes-card__reply-btn\"\n\t\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t:aria-label=\"replyAria(row.note)\"\n\t\t\t\t\t\t\tdata-testid=\"cn-notes-card-reply\"\n\t\t\t\t\t\t\t@click=\"replyTo = row.note\">\n\t\t\t\t\t\t\t{{ replyLabel }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t\t<NcButton\n\t\t\t\t\t\t\tv-if=\"canDeleteNote(row.note)\"\n\t\t\t\t\t\t\tvariant=\"tertiary-no-background\"\n\t\t\t\t\t\t\tclass=\"cn-notes-card__delete-btn\"\n\t\t\t\t\t\t\t:aria-label=\"deleteLabel\"\n\t\t\t\t\t\t\t@click=\"confirmDelete(row.note)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<Delete :size=\"16\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Footer: \"Show all\" link -->\n\t\t<template v-if=\"allNotes.length > maxDisplay\" #footer>\n\t\t\t<button\n\t\t\t\tclass=\"cn-notes-card__show-all\"\n\t\t\t\t@click=\"$emit('show-all')\">\n\t\t\t\t{{ showAllLabel }} ({{ allNotes.length }})\n\t\t\t</button>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcCheckboxRadioSwitch, NcLoadingIcon } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport CommentTextOutline from 'vue-material-design-icons/CommentTextOutline.vue'\nimport Delete from 'vue-material-design-icons/Delete.vue'\nimport NotePlusOutline from 'vue-material-design-icons/NotePlusOutline.vue'\nimport Send from 'vue-material-design-icons/Send.vue'\nimport CnDetailCard from '../CnDetailCard/CnDetailCard.vue'\nimport CnNoteBody from '../CnNoteBody/CnNoteBody.vue'\nimport CnNoteComposer from '../CnNoteComposer/CnNoteComposer.vue'\nimport CnUserActionMenu from '../CnUserActionMenu/CnUserActionMenu.vue'\nimport CnVisibilityChip from '../CnVisibilityChip/CnVisibilityChip.vue'\nimport { useFileComments } from '../../composables/useFileComments.js'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { initialsOf } from '../../utils/initials.js'\nimport { leafSourceUrl } from '../../utils/leafSourceUrl.js'\nimport { extractMentionedGroupIds, extractMentionedIds } from '../../utils/mentions.js'\nimport { quoteFor, supportsReplies, threadNotes, threadRootId } from '../../utils/noteThreads.js'\nimport { serverErrorMessage } from '../../utils/serverErrorMessage.js'\nimport { userLocale } from '../../utils/userLocale.js'\n\n/**\n * CnNotesCard — Inline notes widget for detail pages.\n *\n * Shows up to 5 recent notes with add/delete functionality.\n * Integrates CnUserActionMenu on author names for quick communication.\n *\n * Basic usage\n * ```vue\n * <CnNotesCard\n * register-id=\"uuid-register\"\n * schema-id=\"uuid-schema\"\n * object-id=\"uuid-object\" />\n * ```\n *\n * With sidebar sync\n * ```vue\n * <CnNotesCard\n * register-id=\"reg\"\n * schema-id=\"schema\"\n * object-id=\"obj\"\n * @note-added=\"refreshNotes\"\n * @note-deleted=\"refreshNotes\"\n * @show-all=\"openSidebarNotesTab\" />\n * ```\n *\n * @event note-added Emitted after a new note is successfully persisted. Payload: the created note object.\n * @event note-deleted Emitted after a note is successfully deleted. Payload: the deleted note ID.\n * @event visibility-changed Emitted after a note's visibility was changed. Payload: `{ id, visibility }`.\n * @event mention Emitted after a note that mentions someone was added. Payload: `{ objectId, register, schema, noteId, mentionedUserIds }`, plus `mentionedGroupIds` when a group (`@\"group/<gid>\"`) is mentioned. nc-vue notifies nobody; the listener does.\n * @event show-all Emitted when the user clicks the \"Show all\" button — parents typically open a full notes sidebar tab.\n */\n/**\n * The server's own sentence on a refused note write, '' when it gave none.\n *\n * @param {Response} response The refused response.\n * @return {Promise<string>} The message.\n */\nasync function refusalMessage(response) {\n\ttry {\n\t\tconst data = await response.json()\n\t\tconst message = serverErrorMessage({ message: '-', response: { data } })\n\t\treturn message === '-' ? '' : message\n\t} catch {\n\t\treturn ''\n\t}\n}\n\nexport default {\n\tname: 'CnNotesCard',\n\n\tcomponents: {\n\t\tCnDetailCard,\n\t\tCnNoteBody,\n\t\tCnNoteComposer,\n\t\tCnUserActionMenu,\n\t\tCnVisibilityChip,\n\t\tNcButton,\n\t\tNcCheckboxRadioSwitch,\n\t\tNcLoadingIcon,\n\t\tNotePlusOutline,\n\t\tSend,\n\t\tDelete,\n\t},\n\n\tprops: {\n\t\t/** OpenRegister register ID */\n\t\tregisterId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** OpenRegister schema ID */\n\t\tschemaId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Object UUID */\n\t\tobjectId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Show the notes of a plain Nextcloud file instead of an OpenRegister\n\t\t * object: the file's id. Used when no `objectId` is given; the notes\n\t\t * are the file's comments (`/remote.php/dav/comments/files/{fileId}`),\n\t\t * the same ones the Files sidebar shows.\n\t\t */\n\t\tfileId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Base API URL for OpenRegister */\n\t\tapiBase: {\n\t\t\ttype: String,\n\t\t\tdefault: '/apps/openregister/api',\n\t\t},\n\n\t\t/**\n\t\t * Read the notes of something that is not an OpenRegister object:\n\t\t * `{ kind: 'flow-task', id }` reads `${apiBase}/flow-tasks/{id}/notes`.\n\t\t * `id` may be left out or written `@objectId` for `objectId`.\n\t\t *\n\t\t * @type {{kind: string, id?: (string|number)}|null}\n\t\t */\n\t\tsource: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * A notes URL that replaces the object's own: the list and the add go\n\t\t * to it, a delete to `{endpoint}/{noteId}`. `{id}` is filled with the\n\t\t * source's id or `objectId`.\n\t\t */\n\t\tendpoint: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Maximum number of notes to display */\n\t\tmaxDisplay: {\n\t\t\ttype: Number,\n\t\t\tdefault: 5,\n\t\t},\n\n\t\t/** Whether the card is collapsible */\n\t\tcollapsible: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Render the notes body without the surrounding CnDetailCard.\n\t\t *\n\t\t * Set when a surface already supplies the card and the title, such as a\n\t\t * tab panel whose open tab names the panel. Leaving the card on there\n\t\t * nests a card inside a card and shows the label twice.\n\t\t */\n\t\tchromeless: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Where the add form lives: `inline` (default) keeps it open above the\n\t\t * notes; `button` puts it behind an \"Add note\" button in the card's\n\t\t * header (DqTaak \"Notities\"), and closes it again once the note is\n\t\t * saved. A reply opens it either way. Without the card's header\n\t\t * (`chromeless`) the form stays inline.\n\t\t *\n\t\t * @spec openspec/changes/notes-card-composer-button/specs/notes-card-composer-button/spec.md#requirement-a-notes-card-can-keep-its-composer-behind-a-header-button\n\t\t */\n\t\tcomposer: {\n\t\t\ttype: String,\n\t\t\tdefault: 'inline',\n\t\t\tvalidator: (v) => ['inline', 'button'].includes(v),\n\t\t},\n\n\t\t/**\n\t\t * Show a chip on every note reading internal or public. A note without a\n\t\t * value reads as internal. Off by default, so a host that passes nothing\n\t\t * renders the card as before. Not used for a file source (file comments\n\t\t * carry no flag).\n\t\t */\n\t\tshowVisibility: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Whether the caller may set visibility on this object: the answer the\n\t\t * server gives for `update` on it. The host passes it; the card never\n\t\t * infers it from the current user. When true the card offers the\n\t\t * public switch in the add-note form and a toggle on each note.\n\t\t */\n\t\tcanSetVisibility: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t// --- Pre-translated labels ---\n\t\t/** Card header title. */\n\t\ttitleLabel: { type: String, default: () => t('nextcloud-vue', 'Notes') },\n\t\t/** Label for the submit button that creates a new note. */\n\t\taddNoteLabel: { type: String, default: () => t('nextcloud-vue', 'Add note') },\n\t\t/** Placeholder shown inside the new-note textarea before any input. */\n\t\taddNotePlaceholder: { type: String, default: () => t('nextcloud-vue', 'Write a note…') },\n\t\t/** Empty-state text shown when the object has zero notes. */\n\t\tnoNotesLabel: { type: String, default: () => t('nextcloud-vue', 'No notes yet') },\n\t\t/** Label for the \"Show all\" button rendered when the note list is truncated. */\n\t\tshowAllLabel: { type: String, default: () => t('nextcloud-vue', 'Show all') },\n\t\t/** Text shown instead of the notes when Nextcloud refuses a file's comments (no access). */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'Notes are not available for this file') },\n\t\t/** Label of the add-note switch that makes the note public. */\n\t\tpublicSwitchLabel: { type: String, default: () => t('nextcloud-vue', 'Public (visible to the customer)') },\n\t\t/** Label of the per-note action that makes an internal note public. */\n\t\tmakePublicLabel: { type: String, default: () => t('nextcloud-vue', 'Make public') },\n\t\t/** Label of the per-note action that makes a public note internal. */\n\t\tmakeInternalLabel: { type: String, default: () => t('nextcloud-vue', 'Make internal') },\n\t\t/** Label of the Reply button (shown only when the backend supports replies). */\n\t\treplyLabel: { type: String, default: () => t('nextcloud-vue', 'Reply') },\n\t\t/** Aria label for the per-note delete icon button. */\n\t\tdeleteLabel: { type: String, default: () => t('nextcloud-vue', 'Delete note') },\n\t\t/** Text shown when a `source` or `endpoint` read failed. */\n\t\tfailedLabel: { type: String, default: () => t('nextcloud-vue', 'The notes could not be loaded') },\n\t},\n\n\temits: ['mention', 'note-added', 'note-deleted', 'show-all', 'visibility-changed'],\n\n\tdata() {\n\t\treturn {\n\t\t\tCommentTextOutline: markRaw(CommentTextOutline),\n\t\t\tallNotes: [],\n\t\t\tloading: false,\n\t\t\tnewNoteText: '',\n\t\t\t/** Whether the add form is open while `composer` is `button`. */\n\t\t\tcomposerOpen: false,\n\t\t\t/** Visibility the next note is written with (only sent when the caller may set it). */\n\t\t\tnewNoteVisibility: 'internal',\n\t\t\tnoteSaving: false,\n\t\t\tdeleteConfirmId: null,\n\t\t\t/** The file source answered 403 or 404: no notes and no add field. */\n\t\t\tunavailable: false,\n\t\t\t/** A `source` or `endpoint` read failed: said, rather than shown as no notes. */\n\t\t\tfailed: false,\n\t\t\t/** The note a reply is being written to, or null. */\n\t\t\treplyTo: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {boolean} Whether the add form sits behind the header's button. */\n\t\tcomposerBehindButton() {\n\t\t\treturn this.composer === 'button' && !this.chromeless && !this.unavailable\n\t\t},\n\n\t\t/** @return {boolean} Whether the add form shows. */\n\t\tcomposerShown() {\n\t\t\treturn !this.composerBehindButton || this.composerOpen || Boolean(this.replyTo)\n\t\t},\n\n\t\t/** Chips are shown: the host asked for them (or lets the caller set visibility) and the notes are object notes. */\n\t\tvisibilityShown() {\n\t\t\treturn (this.showVisibility || this.canSetVisibility) && !this.isFileSource\n\t\t},\n\n\t\t/** The switch and the per-note toggle are offered: only when the host says the caller may set it. */\n\t\tvisibilityToggleShown() {\n\t\t\treturn this.canSetVisibility && !this.isFileSource\n\t\t},\n\n\t\t/**\n\t\t * The notes URL a `source` or `endpoint` names, '' while its record id\n\t\t * is unknown, or null when the card reads an object.\n\t\t *\n\t\t * @return {string|null}\n\t\t * @spec openspec/changes/leaf-cards-read-a-source/specs/leaf-cards-read-a-source/spec.md#requirement-a-leaf-card-reads-a-source-or-an-endpoint\n\t\t */\n\t\tsourceUrl() {\n\t\t\treturn leafSourceUrl({ source: this.source, endpoint: this.endpoint, apiBase: this.apiBase, objectId: this.objectId }, 'notes')\n\t\t},\n\n\t\t/** The notes come from a `source` or `endpoint`, not from an object. */\n\t\tisSourceMode() {\n\t\t\treturn this.sourceUrl !== null\n\t\t},\n\n\t\t/** @return {string} The notes URL the list, add and delete use. */\n\t\tnotesUrl() {\n\t\t\tif (this.isSourceMode) {\n\t\t\t\treturn this.sourceUrl\n\t\t\t}\n\t\t\treturn `${this.apiBase}/objects/${this.registerId}/${this.schemaId}/${this.objectId}/notes`\n\t\t},\n\n\t\t/** The file source applies: a file id and no object. */\n\t\tisFileSource() {\n\t\t\treturn this.fileId !== null && this.fileId !== undefined && this.fileId !== '' && !this.objectId\n\t\t},\n\n\t\t/** @return {{apiBase: string, register: string, schema: string, objectId: string}} Where this record's files live, for pasted images. */\n\t\trecord() {\n\t\t\treturn { apiBase: this.apiBase, register: this.registerId, schema: this.schemaId, objectId: this.objectId }\n\t\t},\n\n\t\t/** @return {boolean} Whether the backend supports replies: its notes carry a `parentId` key. Not for a file's comments. */\n\t\tcanReply() {\n\t\t\treturn !this.isFileSource && supportsReplies(this.allNotes)\n\t\t},\n\n\t\t/** @return {Array<{note: object, replies: object[]}>} The shown notes as threads. */\n\t\tthreads() {\n\t\t\treturn threadNotes(this.displayedNotes)\n\t\t},\n\n\t\t/** @return {string} The line above the form while replying. */\n\t\treplyingLabel() {\n\t\t\treturn this.replyTo ? t('nextcloud-vue', 'Replying to {author}', { author: this.getNoteAuthorName(this.replyTo) }) : ''\n\t\t},\n\n\t\tdisplayedNotes() {\n\t\t\t// Reverse chronological, limited to maxDisplay\n\t\t\tconst sorted = [...this.allNotes].sort((a, b) => {\n\t\t\t\tconst dateA = new Date(a.creationDateTime || a.created || a.createdAt || 0)\n\t\t\t\tconst dateB = new Date(b.creationDateTime || b.created || b.createdAt || 0)\n\t\t\t\treturn dateB - dateA\n\t\t\t})\n\t\t\treturn sorted.slice(0, this.maxDisplay)\n\t\t},\n\t},\n\n\twatch: {\n\t\tfileId: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tif (this.isFileSource) {\n\t\t\t\t\tthis.fetchNotes()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(newId) {\n\t\t\t\tif (newId && this.registerId && this.schemaId && !this.isSourceMode) {\n\t\t\t\t\tthis.fetchNotes()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\n\t\tsourceUrl: {\n\t\t\timmediate: true,\n\t\t\thandler(url) {\n\t\t\t\tif (typeof url === 'string' && url !== '') {\n\t\t\t\t\tthis.fetchNotes()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * A note's visibility; an entry with no value reads as internal.\n\t\t *\n\t\t * @param {object} note The note.\n\t\t * @return {'public'|'internal'} The visibility.\n\t\t */\n\t\tnoteVisibility(note) {\n\t\t\treturn note && note.visibility === 'public' ? 'public' : 'internal'\n\t\t},\n\n\t\t/**\n\t\t * The one place a note write goes out: creating a note and changing its\n\t\t * visibility both send through here, so the value is sent from one spot.\n\t\t *\n\t\t * @param {'POST'|'PATCH'} method The HTTP method.\n\t\t * @param {string} url The notes URL (with the note id for a change).\n\t\t * @param {object} body The JSON body.\n\t\t * @return {Promise<Response>} The response.\n\t\t */\n\t\twriteNote(method, url, body) {\n\t\t\treturn fetch(prefixUrl(url), { method, headers: buildHeaders(), body: JSON.stringify(body) })\n\t\t},\n\n\t\t/**\n\t\t * Flip a note between internal and public.\n\t\t *\n\t\t * @param {object} note The note.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync toggleVisibility(note) {\n\t\t\tif (!this.visibilityToggleShown) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst next = this.noteVisibility(note) === 'public' ? 'internal' : 'public'\n\t\t\ttry {\n\t\t\t\tconst url = `${this.notesUrl}/${note.id}`\n\t\t\t\tconst response = await this.writeNote('PATCH', url, { visibility: next })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.allNotes = this.allNotes.map((n) => (n.id === note.id ? { ...n, visibility: next } : n))\n\t\t\t\t\t/** @event visibility-changed Emitted after a note's visibility was changed. Payload: `{ id, visibility }`. */\n\t\t\t\t\tthis.$emit('visibility-changed', { id: note.id, visibility: next })\n\t\t\t\t} else {\n\t\t\t\t\tthis.showError('Failed to change visibility')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\t\t\tconsole.error('CnNotesCard: Failed to change visibility', err)\n\t\t\t\tthis.showError('Failed to change visibility')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The rows of one thread: the note, then its replies. A reply whose parent\n\t\t * is far up the list carries a one-line quote of it.\n\t\t *\n\t\t * @param {{note: object, replies: object[]}} thread The thread.\n\t\t * @return {Array<{note: object, isReply: boolean, quote: string}>} The rows.\n\t\t */\n\t\tthreadRows(thread) {\n\t\t\tconst flat = this.threads.flatMap((t2) => [t2.note, ...t2.replies])\n\t\t\treturn [\n\t\t\t\t{ note: thread.note, isReply: false, quote: '' },\n\t\t\t\t...thread.replies.map((reply) => ({ note: reply, isReply: true, quote: quoteFor(reply, flat) })),\n\t\t\t]\n\t\t},\n\n\t\t/**\n\t\t * @param {object} note A top-level note.\n\t\t * @return {string} The name of its thread for assistive technology.\n\t\t */\n\t\tthreadLabel(note) {\n\t\t\treturn t('nextcloud-vue', 'Note by {author} and its replies', { author: this.getNoteAuthorName(note) })\n\t\t},\n\n\t\t/**\n\t\t * @param {object} note The note a Reply button answers.\n\t\t * @return {string} The button's accessible name, naming the author.\n\t\t */\n\t\treplyAria(note) {\n\t\t\treturn t('nextcloud-vue', 'Reply to {author}', { author: this.getNoteAuthorName(note) })\n\t\t},\n\n\t\t/**\n\t\t * Tell the host a saved note mentions someone. The payload keeps the\n\t\t * user ids and adds the group ids only when a group is mentioned.\n\t\t *\n\t\t * @param {string} text The saved text.\n\t\t * @param {string|null} noteId The note's id.\n\t\t * @return {void}\n\t\t */\n\t\temitMentionEvent(text, noteId) {\n\t\t\tconst mentionedUserIds = extractMentionedIds(text)\n\t\t\tconst mentionedGroupIds = extractMentionedGroupIds(text)\n\t\t\tif (mentionedUserIds.length === 0 && mentionedGroupIds.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('mention', {\n\t\t\t\tobjectId: this.objectId,\n\t\t\t\tregister: this.registerId,\n\t\t\t\tschema: this.schemaId,\n\t\t\t\tnoteId,\n\t\t\t\tmentionedUserIds,\n\t\t\t\t...(mentionedGroupIds.length > 0 ? { mentionedGroupIds } : {}),\n\t\t\t})\n\t\t},\n\n\t\tgetNoteAuthorId(note) {\n\t\t\treturn note.actorId || note.author || ''\n\t\t},\n\n\t\tinitialsOf,\n\n\t\tgetNoteAuthorName(note) {\n\t\t\treturn note.actorDisplayName || note.author || 'Unknown'\n\t\t},\n\n\t\tisCurrentUser(note) {\n\t\t\tif (note.isCurrentUser === true) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tconst authorId = this.getNoteAuthorId(note)\n\t\t\tconst currentUser = typeof OC !== 'undefined' ? OC?.currentUser : null\n\t\t\treturn authorId === currentUser\n\t\t},\n\n\t\tcanDeleteNote(note) {\n\t\t\treturn this.isCurrentUser(note)\n\t\t},\n\n\t\tfileComments() {\n\t\t\treturn useFileComments(this.fileId)\n\t\t},\n\n\t\tasync fetchNotes() {\n\t\t\tif (this.isFileSource) {\n\t\t\t\tthis.loading = true\n\t\t\t\tthis.unavailable = false\n\t\t\t\ttry {\n\t\t\t\t\tthis.allNotes = await this.fileComments().list()\n\t\t\t\t} catch (err) {\n\t\t\t\t\tthis.allNotes = []\n\t\t\t\t\tthis.unavailable = err && (err.status === 403 || err.status === 404)\n\t\t\t\t\tif (!this.unavailable) {\n\t\t\t\t\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\t\t\t\t\tconsole.error('CnNotesCard: Failed to fetch file comments', err)\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tthis.loading = false\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.isSourceMode ? this.notesUrl === '' : (!this.registerId || !this.schemaId || !this.objectId)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.failed = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.notesUrl), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.allNotes = data.results || data || []\n\t\t\t\t} else if (this.isSourceMode) {\n\t\t\t\t\tthis.allNotes = []\n\t\t\t\t\tthis.failed = true\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tthis.failed = this.isSourceMode\n\t\t\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\t\t\tconsole.error('CnNotesCard: Failed to fetch notes', err)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync submitNote() {\n\t\t\tif (!this.newNoteText.trim() || this.noteSaving) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.noteSaving = true\n\t\t\tif (this.isFileSource) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.fileComments().add(this.newNoteText.trim())\n\t\t\t\t\tthis.composerOpen = false\n\t\t\t\t\tthis.newNoteText = ''\n\t\t\t\t\tawait this.fetchNotes()\n\t\t\t\t\tthis.$emit('note-added')\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\t\t\t\tconsole.error('CnNotesCard: Failed to add file comment', err)\n\t\t\t\t\tthis.showError('Failed to add note')\n\t\t\t\t} finally {\n\t\t\t\t\tthis.noteSaving = false\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst url = this.notesUrl\n\t\t\t\tconst body = { message: this.newNoteText.trim() }\n\t\t\t\tif (this.visibilityToggleShown) {\n\t\t\t\t\tbody.visibility = this.newNoteVisibility\n\t\t\t\t}\n\t\t\t\tif (this.replyTo) {\n\t\t\t\t\tbody.parentId = threadRootId(this.replyTo, this.allNotes)\n\t\t\t\t}\n\t\t\t\tconst response = await this.writeNote('POST', url, body)\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tlet noteId = null\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst created = await response.json()\n\t\t\t\t\t\tnoteId = (created && (created.id || (created.results && created.results.id))) || null\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Body not JSON: the event still fires with noteId null.\n\t\t\t\t\t}\n\t\t\t\t\tthis.emitMentionEvent(body.message, noteId)\n\t\t\t\t\tthis.replyTo = null\n\t\t\t\t\tthis.composerOpen = false\n\t\t\t\t\tthis.newNoteText = ''\n\t\t\t\t\tthis.newNoteVisibility = 'internal'\n\t\t\t\t\tawait this.fetchNotes()\n\t\t\t\t\tthis.$emit('note-added')\n\t\t\t\t} else {\n\t\t\t\t\tthis.showError(await refusalMessage(response) || 'Failed to add note')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\t\t\tconsole.error('CnNotesCard: Failed to add note', err)\n\t\t\t\tthis.showError('Failed to add note')\n\t\t\t} finally {\n\t\t\t\tthis.noteSaving = false\n\t\t\t}\n\t\t},\n\n\t\tasync confirmDelete(note) {\n\t\t\t// Simple inline confirmation — delete directly\n\t\t\tif (this.isFileSource) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.fileComments().remove(note.id)\n\t\t\t\t\tthis.allNotes = this.allNotes.filter((n) => n.id !== note.id)\n\t\t\t\t\tthis.$emit('note-deleted')\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\t\t\t\tconsole.error('CnNotesCard: Failed to delete file comment', err)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst url = `${this.notesUrl}/${note.id}`\n\t\t\t\tconst response = await fetch(prefixUrl(url), {\n\t\t\t\t\tmethod: 'DELETE',\n\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.allNotes = this.allNotes.filter((n) => n.id !== note.id)\n\t\t\t\t\tthis.$emit('note-deleted')\n\t\t\t\t} else {\n\t\t\t\t\tthis.showError(await refusalMessage(response) || 'Failed to delete note')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\t\t\tconsole.error('CnNotesCard: Failed to delete note', err)\n\t\t\t}\n\t\t},\n\n\t\tformatDate(dateStr) {\n\t\t\tif (!dateStr) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Date(dateStr).toLocaleString(userLocale(), {\n\t\t\t\t\tyear: 'numeric',\n\t\t\t\t\tmonth: 'short',\n\t\t\t\t\tday: 'numeric',\n\t\t\t\t\thour: '2-digit',\n\t\t\t\t\tminute: '2-digit',\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\treturn dateStr\n\t\t\t}\n\t\t},\n\n\t\tshowError(message) {\n\t\t\ttry {\n\t\t\t\timport('@nextcloud/dialogs').then(({ showError }) => {\n\t\t\t\t\tshowError(message)\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\t// eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from\n\t\t\t\tconsole.error(message)\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-notes-card__add-form {\n\tmargin-bottom: 12px;\n}\n\n.cn-notes-card__textarea {\n\twidth: 100%;\n\tmargin-bottom: 8px;\n\tbox-sizing: border-box;\n}\n\n.cn-notes-card__replying {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tmargin: 0 0 4px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-notes-card__note--reply {\n\tmargin-inline-start: 24px;\n}\n\n.cn-notes-card__quote {\n\tmargin: 0 0 2px;\n\tpadding-inline-start: 8px;\n\tborder-inline-start: 2px solid var(--color-border-dark);\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-notes-card__empty {\n\ttext-align: center;\n\tpadding: 16px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-notes-card__list {\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-notes-card__note {\n\tpadding: 10px 0;\n\tborder-bottom: 1px solid var(--color-border);\n\tposition: relative;\n}\n\n.cn-notes-card__note:last-child {\n\tborder-bottom: none;\n}\n\n.cn-notes-card__initials {\n\tdisplay: none;\n}\n\n.cn-notes-card__note-header {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\talign-items: baseline;\n\tmargin-bottom: 4px;\n}\n\n.cn-notes-card__author {\n\tfont-size: 13px;\n}\n\n.cn-notes-card__author--self {\n\tcolor: var(--color-main-text);\n}\n\n.cn-notes-card__visibility {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tmargin-bottom: 4px;\n}\n\n.cn-notes-card__time {\n\tfont-size: 11px;\n\tcolor: var(--color-text-maxcontrast);\n\tflex-shrink: 0;\n\tmargin-left: 8px;\n}\n\n.cn-notes-card__body {\n\tfont-size: 13px;\n\tmargin: 0;\n\twhite-space: pre-wrap;\n\toverflow-wrap: anywhere;\n\tpadding-right: 32px;\n}\n\n/* The note's own class leads so the position outranks NcButton's\n `position: relative`, which loads later at the same weight and put the\n button in the flow under the note. */\n.cn-notes-card__note > .cn-notes-card__delete-btn {\n\tposition: absolute;\n\ttop: 8px;\n\tright: -4px;\n\topacity: 0;\n\ttransition: opacity 0.15s ease;\n}\n\n.cn-notes-card__note:hover > .cn-notes-card__delete-btn,\n.cn-notes-card__note:focus-within > .cn-notes-card__delete-btn {\n\topacity: 1;\n}\n\n.cn-notes-card__show-all {\n\tbackground: none;\n\tborder: none;\n\tcolor: var(--color-primary-element);\n\tfont-size: 13px;\n\tfont-weight: 500;\n\tcursor: pointer;\n\tpadding: 0;\n\twidth: 100%;\n\ttext-align: center;\n}\n\n.cn-notes-card__show-all:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["CnDetailCard","CnNoteBody","CnNoteComposer","CnUserActionMenu","CnVisibilityChip","t"],"mappings":"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"}
|
|
@@ -200,6 +200,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
200
200
|
($options.canReply)
|
|
201
201
|
? (openBlock(), createBlock(_component_NcButton, {
|
|
202
202
|
key: 2,
|
|
203
|
+
class: "cn-notes-card__reply-btn",
|
|
203
204
|
variant: "tertiary",
|
|
204
205
|
size: "small",
|
|
205
206
|
"aria-label": $options.replyAria(row.note),
|