@conduction/nextcloud-vue 2.76.0 → 2.77.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/CnAppNav/CnAppNav.vue2.js +18 -3
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +49 -3
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +42 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +228 -203
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue.js +9 -0
- package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue.js.map +1 -0
- package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue2.js +50 -0
- package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue2.js.map +1 -0
- package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue3.js +13 -0
- package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue3.js.map +1 -0
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +41 -4
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +90 -51
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +16 -28
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +12 -24
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnFavouriteToggle/CnFavouriteToggle.vue2.js +7 -2
- package/dist/esm/components/CnFavouriteToggle/CnFavouriteToggle.vue2.js.map +1 -1
- package/dist/esm/components/CnFavouriteToggle/CnFavouriteToggle.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +114 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +115 -62
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue2.js +89 -7
- package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue2.js.map +1 -1
- package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue3.js +62 -30
- package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +98 -13
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +13 -9
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +199 -7
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js +155 -119
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +6 -0
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +4 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +13 -0
- 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/composables/useListView.js +4 -0
- package/dist/esm/composables/useListView.js.map +1 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +25 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +25 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +288 -74
- package/dist/esm/store/plugins/interactions.js +20 -10
- package/dist/esm/store/plugins/interactions.js.map +1 -1
- package/dist/esm/store/useObjectStore.js +17 -0
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/builtInFormatters.js +15 -7
- package/dist/esm/utils/builtInFormatters.js.map +1 -1
- package/dist/esm/utils/groupAutocomplete.js +63 -1
- package/dist/esm/utils/groupAutocomplete.js.map +1 -1
- package/dist/esm/utils/lensAvailability.js +90 -0
- package/dist/esm/utils/lensAvailability.js.map +1 -0
- package/dist/esm/utils/personalLenses.js +13 -6
- package/dist/esm/utils/personalLenses.js.map +1 -1
- package/dist/esm/utils/recordInteractions.js +31 -13
- package/dist/esm/utils/recordInteractions.js.map +1 -1
- package/dist/esm/utils/resolveFilterTokens.js +46 -3
- package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
- package/dist/esm/utils/schema.js +1 -1
- package/dist/esm/utils/schema.js.map +1 -1
- package/dist/esm/utils/sentinelTokens.js +4 -4
- package/dist/esm/utils/sentinelTokens.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/widgetVisibility.js +46 -1
- package/dist/esm/utils/widgetVisibility.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +3873 -2674
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +288 -74
- package/l10n/en.json +25 -1
- package/l10n/nl.json +25 -1
- package/package.json +1 -1
- package/src/components/CnAppNav/CnAppNav.vue +44 -5
- package/src/components/CnCellRenderer/CnCellRenderer.vue +56 -1
- package/src/components/CnCellRenderer/CnGroupNameCell.vue +61 -0
- package/src/components/CnDataTable/CnDataTable.md +9 -0
- package/src/components/CnDataTable/CnDataTable.vue +80 -20
- package/src/components/CnDetailPage/CnDetailPage.vue +10 -27
- package/src/components/CnFavouriteToggle/CnFavouriteToggle.vue +7 -2
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +204 -5
- package/src/components/CnFollowToggle/CnFollowToggle.vue +118 -13
- package/src/components/CnIndexPage/CnIndexPage.md +36 -0
- package/src/components/CnIndexPage/CnIndexPage.vue +106 -19
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +226 -2
- package/src/components/CnObjectSidebar/CnFilesTab.vue +9 -0
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +13 -0
- package/src/composables/useListView.js +4 -0
- package/src/css/look-board-detail.css +40 -2
- package/src/css/table.css +82 -17
- package/src/index.js +2 -1
- package/src/schemas/app-manifest-v2.schema.json +11 -7
- package/src/store/plugins/interactions.js +20 -10
- package/src/store/useObjectStore.js +17 -0
- package/src/utils/builtInFormatters.js +15 -7
- package/src/utils/groupAutocomplete.js +75 -0
- package/src/utils/index.js +1 -1
- package/src/utils/lensAvailability.js +86 -0
- package/src/utils/personalLenses.js +13 -6
- package/src/utils/recordInteractions.js +31 -13
- package/src/utils/resolveFilterTokens.js +45 -2
- package/src/utils/schema.js +1 -1
- package/src/utils/sentinelTokens.js +4 -4
- package/src/utils/validateManifestV2.compiled.js +2 -2
- package/src/utils/widgetVisibility.js +45 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnFollowToggle.vue2.js","sources":["../../../../src/components/CnFollowToggle/CnFollowToggle.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<span class=\"cn-follow-toggle\" data-testid=\"cn-follow-toggle\">\n\t\t<NcButton\n\t\t\t:variant=\"state ? 'secondary' : 'tertiary'\"\n\t\t\t:aria-pressed=\"state ? 'true' : 'false'\"\n\t\t\t:title=\"tooltip\"\n\t\t\t:disabled=\"busy\"\n\t\t\tdata-testid=\"cn-follow-toggle-button\"\n\t\t\t@click.stop=\"toggle\">\n\t\t\t<template #icon>\n\t\t\t\t<Bell v-if=\"state\" :size=\"20\" />\n\t\t\t\t<BellOutline v-else :size=\"20\" />\n\t\t\t</template>\n\t\t\t{{ state ? followingLabel : followLabel }}\n\t\t</NcButton>\n\t\t<NcPopover\n\t\t\tv-if=\"count !== null\"\n\t\t\tv-model:shown=\"open\"\n\t\t\tplacement=\"bottom-start\">\n\t\t\t<template #trigger>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\tclass=\"cn-follow-toggle__count\"\n\t\t\t\t\t:aria-label=\"followersLabel\"\n\t\t\t\t\tdata-testid=\"cn-follow-toggle-count\">\n\t\t\t\t\t{{ count }}\n\t\t\t\t</NcButton>\n\t\t\t</template>\n\t\t\t<div class=\"cn-follow-toggle__popover\" data-testid=\"cn-follow-toggle-popover\">\n\t\t\t\t<NcLoadingIcon v-if=\"loadingWatchers\" :size=\"20\" :name=\"loadingLabel\" />\n\t\t\t\t<ul v-else class=\"cn-follow-toggle__list\">\n\t\t\t\t\t<li v-for=\"watcher in watchers\" :key=\"watcher.userId\" class=\"cn-follow-toggle__row\">\n\t\t\t\t\t\t<NcAvatar :user=\"watcher.userId\"\n\t\t\t\t\t\t\t:displayName=\"watcher.displayName || watcher.userId\"\n\t\t\t\t\t\t\t:size=\"28\"\n\t\t\t\t\t\t\thideStatus />\n\t\t\t\t\t\t<span class=\"cn-follow-toggle__who\">\n\t\t\t\t\t\t\t<span class=\"cn-follow-toggle__name\">{{ watcher.displayName || watcher.userId }}</span>\n\t\t\t\t\t\t\t<span v-if=\"watcher.created\" class=\"cn-follow-toggle__since\">{{ sinceText(watcher.created) }}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<NcButton\n\t\t\t\t\t\t\tv-if=\"canRemove(watcher)\"\n\t\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t\t:aria-label=\"removeFollowerLabel(watcher)\"\n\t\t\t\t\t\t\t:title=\"removeFollowerLabel(watcher)\"\n\t\t\t\t\t\t\tdata-testid=\"cn-follow-toggle-remove\"\n\t\t\t\t\t\t\t@click=\"removeWatcher(watcher)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<Close :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</li>\n\t\t\t\t</ul>\n\t\t\t\t<div v-if=\"canManage\" class=\"cn-follow-toggle__picker\">\n\t\t\t\t\t<NcSelect\n\t\t\t\t\t\t:inputLabel=\"addColleagueLabel\"\n\t\t\t\t\t\t:modelValue=\"null\"\n\t\t\t\t\t\t:options=\"pickerOptions\"\n\t\t\t\t\t\t:loading=\"pickerLoading\"\n\t\t\t\t\t\t:filterable=\"false\"\n\t\t\t\t\t\tlabel=\"label\"\n\t\t\t\t\t\tdata-testid=\"cn-follow-toggle-picker\"\n\t\t\t\t\t\t@search=\"onPickerSearch\"\n\t\t\t\t\t\t@update:modelValue=\"addWatcher\" />\n\t\t\t\t</div>\n\t\t\t\t<p v-if=\"popoverError\"\n\t\t\t\t\tclass=\"cn-follow-toggle__error\"\n\t\t\t\t\trole=\"alert\"\n\t\t\t\t\tdata-testid=\"cn-follow-toggle-error\">\n\t\t\t\t\t{{ popoverError }}\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t</NcPopover>\n\t</span>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcButton, NcLoadingIcon, NcPopover, NcSelect } from '@nextcloud/vue'\nimport Bell from 'vue-material-design-icons/Bell.vue'\nimport BellOutline from 'vue-material-design-icons/BellOutline.vue'\nimport Close from 'vue-material-design-icons/Close.vue'\nimport { patchStoredSelf } from '../../utils/patchStoredSelf.js'\nimport { listWatchers, setWatcher, setWatching } from '../../utils/recordInteractions.js'\nimport { searchNextcloudUsers } from '../../utils/userAutocomplete.js'\n\n/**\n * CnFollowToggle — a Follow toggle bound to a record's `@self.watching` marker,\n * with the follower count and a followers popover for users who may see them.\n *\n * A click flips the toggle at once and sends `PUT` or `DELETE .../watch`; on\n * failure it flips back and shows the server's message, and a 404 says the\n * user can no longer see the record and emits `not-found`. The count shows\n * only when `watcherCount` is given (OpenRegister sets `@self.watcherCount` for\n * a reader with `update` only); opening the popover then fetches\n * `GET .../watchers`, never before, and a 403 closes it quietly. With\n * `canManage` (`@self.can.manage`) the popover offers an \"Add a colleague\"\n * picker and a remove action on each follower; without it, only the user's own\n * row can be removed.\n *\n * Example:\n * ```vue\n * <CnFollowToggle register=\"pipelinq\" schema=\"ticket\" :object-id=\"id\"\n * :watching=\"self.watching\" :watcher-count=\"self.watcherCount\" :can-manage=\"self.can && self.can.manage\" />\n * ```\n */\nexport default {\n\tname: 'CnFollowToggle',\n\n\tcomponents: { Bell, BellOutline, Close, NcAvatar, NcButton, NcLoadingIcon, NcPopover, NcSelect },\n\n\tprops: {\n\t\t/** Register slug of the record. */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Schema slug of the record. */\n\t\tschema: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Id of the record. */\n\t\tobjectId: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Whether the current user follows the record (`@self.watching`). */\n\t\twatching: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Number of followers (`@self.watcherCount`). Null hides the count and the popover. */\n\t\twatcherCount: {\n\t\t\ttype: Number,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether the user may add and remove other followers (`@self.can.manage`). */\n\t\tcanManage: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether following sends change notifications. False changes the tooltip to say it does not. */\n\t\tnotifies: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/** User id of the current user. Defaults to Nextcloud's `OC.currentUser`. */\n\t\tcurrentUser: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['change', 'error', 'not-found'],\n\n\tdata() {\n\t\treturn {\n\t\t\tstate: this.watching,\n\t\t\tcount: this.watcherCount,\n\t\t\tbusy: false,\n\t\t\topen: false,\n\t\t\twatchers: [],\n\t\t\tloadingWatchers: false,\n\t\t\tpopoverError: '',\n\t\t\tpickerOptions: [],\n\t\t\tpickerLoading: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tfollowLabel() {\n\t\t\treturn t('nextcloud-vue', 'Follow')\n\t\t},\n\n\t\tfollowingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Following')\n\t\t},\n\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading …')\n\t\t},\n\n\t\taddColleagueLabel() {\n\t\t\treturn t('nextcloud-vue', 'Add a colleague')\n\t\t},\n\n\t\tfollowersLabel() {\n\t\t\treturn t('nextcloud-vue', '{count} followers', { count: this.count })\n\t\t},\n\n\t\ttooltip() {\n\t\t\treturn this.notifies\n\t\t\t\t? t('nextcloud-vue', 'Follow this record to get its updates')\n\t\t\t\t: t('nextcloud-vue', 'You will see it under Following. This register sends no change notifications.')\n\t\t},\n\n\t\tme() {\n\t\t\treturn this.currentUser || (typeof OC !== 'undefined' && OC ? OC.currentUser : '') || ''\n\t\t},\n\t},\n\n\twatch: {\n\t\twatching(value) {\n\t\t\tthis.state = value\n\t\t},\n\n\t\twatcherCount(value) {\n\t\t\tthis.count = value\n\t\t},\n\n\t\topen(value) {\n\t\t\tif (value) {\n\t\t\t\tthis.loadWatchers()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tsinceText(created) {\n\t\t\tconst d = new Date(created)\n\t\t\treturn Number.isNaN(d.getTime())\n\t\t\t\t? ''\n\t\t\t\t: t('nextcloud-vue', 'Since {date}', { date: d.toLocaleDateString() })\n\t\t},\n\n\t\tremoveFollowerLabel(watcher) {\n\t\t\treturn t('nextcloud-vue', 'Remove {name} as a follower', { name: watcher.displayName || watcher.userId })\n\t\t},\n\n\t\tcanRemove(watcher) {\n\t\t\treturn this.canManage || (this.me !== '' && watcher.userId === this.me)\n\t\t},\n\n\t\tasync toggle() {\n\t\t\tif (this.busy) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst previous = this.state\n\t\t\tthis.state = !previous\n\t\t\tif (this.count !== null) {\n\t\t\t\tthis.count = Math.max(0, this.count + (this.state ? 1 : -1))\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\tconst result = await setWatching(this.register, this.schema, this.objectId, this.state)\n\t\t\tthis.busy = false\n\t\t\tif (!result.ok) {\n\t\t\t\tthis.state = previous\n\t\t\t\tif (this.count !== null) {\n\t\t\t\t\tthis.count = Math.max(0, this.count + (previous ? 1 : -1))\n\t\t\t\t}\n\t\t\t\tconst message = result.status === 404\n\t\t\t\t\t? t('nextcloud-vue', 'You can no longer see this record.')\n\t\t\t\t\t: (result.message || t('nextcloud-vue', 'Could not change following.'))\n\t\t\t\timport('@nextcloud/dialogs').then(({ showError }) => showError(message)).catch(() => {})\n\t\t\t\t/** @event error Emitted when a call failed; payload is the message shown. */\n\t\t\t\tthis.$emit('error', message)\n\t\t\t\tif (result.status === 404) {\n\t\t\t\t\t/** @event not-found Emitted on a 404: the record went away or access was withdrawn. */\n\t\t\t\t\tthis.$emit('not-found')\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tpatchStoredSelf(this.register, this.schema, this.objectId, {\n\t\t\t\twatching: this.state,\n\t\t\t\t...(this.count !== null ? { watcherCount: this.count } : {}),\n\t\t\t})\n\t\t\t/** @event change Emitted after a successful call. Payload: `{ watching, count }`. */\n\t\t\tthis.$emit('change', { watching: this.state, count: this.count })\n\t\t},\n\n\t\tasync loadWatchers() {\n\t\t\tthis.popoverError = ''\n\t\t\tthis.loadingWatchers = true\n\t\t\tconst result = await listWatchers(this.register, this.schema, this.objectId)\n\t\t\tthis.loadingWatchers = false\n\t\t\tif (!result.ok) {\n\t\t\t\t// No right to see the list (403) or a failure: close without a toast.\n\t\t\t\tthis.open = false\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst data = result.data || {}\n\t\t\tthis.watchers = Array.isArray(data.results) ? data.results : []\n\t\t\tthis.count = Number.isFinite(data.total) ? data.total : this.watchers.length\n\t\t},\n\n\t\tasync onPickerSearch(query) {\n\t\t\tthis.pickerLoading = true\n\t\t\tconst found = await searchNextcloudUsers(query || '', { limit: 10 })\n\t\t\tconst already = new Set(this.watchers.map((w) => w.userId))\n\t\t\tthis.pickerOptions = found.filter((u) => !already.has(u.id)).map((u) => ({ ...u, label: u.label || u.displayName || u.id }))\n\t\t\tthis.pickerLoading = false\n\t\t},\n\n\t\tasync addWatcher(option) {\n\t\t\tif (!option || !option.id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.popoverError = ''\n\t\t\tconst result = await setWatcher(this.register, this.schema, this.objectId, option.id, true)\n\t\t\tif (!result.ok) {\n\t\t\t\tthis.popoverError = result.message || t('nextcloud-vue', 'Could not add this colleague.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tawait this.loadWatchers()\n\t\t},\n\n\t\tasync removeWatcher(watcher) {\n\t\t\tthis.popoverError = ''\n\t\t\tconst result = await setWatcher(this.register, this.schema, this.objectId, watcher.userId, false)\n\t\t\tif (!result.ok) {\n\t\t\t\tthis.popoverError = result.message || t('nextcloud-vue', 'Could not remove this follower.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (watcher.userId === this.me) {\n\t\t\t\tthis.state = false\n\t\t\t\tthis.$emit('change', { watching: false, count: this.count })\n\t\t\t}\n\t\t\tawait this.loadWatchers()\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-follow-toggle {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n}\n\n.cn-follow-toggle__popover {\n\tmin-width: 260px;\n\tpadding: 12px;\n}\n\n.cn-follow-toggle__list {\n\tlist-style: none;\n\tmargin: 0 0 8px;\n\tpadding: 0;\n}\n\n.cn-follow-toggle__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 4px 0;\n}\n\n.cn-follow-toggle__who {\n\tdisplay: flex;\n\tflex-direction: column;\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n.cn-follow-toggle__since {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: var(--font-size-small, 13px);\n}\n\n.cn-follow-toggle__error {\n\tmargin: 8px 0 0;\n\tcolor: var(--color-error-text);\n}\n</style>\n"],"names":["Bell","t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnFollowToggle.vue2.js","sources":["../../../../src/components/CnFollowToggle/CnFollowToggle.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<span class=\"cn-follow-toggle\"\n\t\t:class=\"{ 'cn-follow-toggle--compact': compact }\"\n\t\tdata-testid=\"cn-follow-toggle\">\n\t\t<NcButton\n\t\t\t:variant=\"state && !compact ? 'secondary' : 'tertiary'\"\n\t\t\t:aria-pressed=\"state ? 'true' : 'false'\"\n\t\t\t:aria-label=\"compact ? (state ? followingLabel : followLabel) : undefined\"\n\t\t\t:title=\"tooltip\"\n\t\t\t:disabled=\"busy\"\n\t\t\tdata-testid=\"cn-follow-toggle-button\"\n\t\t\t@click.stop=\"toggle\">\n\t\t\t<template #icon>\n\t\t\t\t<Eye v-if=\"state\" :size=\"20\" class=\"cn-follow-toggle__on\" />\n\t\t\t\t<EyeOutline v-else :size=\"20\" />\n\t\t\t</template>\n\t\t\t<template v-if=\"!compact\">\n\t\t\t\t{{ state ? followingLabel : followLabel }}\n\t\t\t</template>\n\t\t</NcButton>\n\t\t<NcButton\n\t\t\tv-if=\"showNotifySwitch\"\n\t\t\tvariant=\"tertiary\"\n\t\t\t:aria-pressed=\"notifyState ? 'true' : 'false'\"\n\t\t\t:aria-label=\"notifyLabel\"\n\t\t\t:title=\"notifyLabel\"\n\t\t\t:disabled=\"busy\"\n\t\t\tdata-testid=\"cn-follow-toggle-notify\"\n\t\t\t@click.stop=\"toggleNotify\">\n\t\t\t<template #icon>\n\t\t\t\t<Bell v-if=\"notifyState\" :size=\"20\" />\n\t\t\t\t<BellOffOutline v-else :size=\"20\" />\n\t\t\t</template>\n\t\t</NcButton>\n\t\t<NcPopover\n\t\t\tv-if=\"count !== null && !compact\"\n\t\t\tv-model:shown=\"open\"\n\t\t\tplacement=\"bottom-start\">\n\t\t\t<template #trigger>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\tclass=\"cn-follow-toggle__count\"\n\t\t\t\t\t:aria-label=\"followersLabel\"\n\t\t\t\t\tdata-testid=\"cn-follow-toggle-count\">\n\t\t\t\t\t{{ count }}\n\t\t\t\t</NcButton>\n\t\t\t</template>\n\t\t\t<div class=\"cn-follow-toggle__popover\" data-testid=\"cn-follow-toggle-popover\">\n\t\t\t\t<NcLoadingIcon v-if=\"loadingWatchers\" :size=\"20\" :name=\"loadingLabel\" />\n\t\t\t\t<ul v-else class=\"cn-follow-toggle__list\">\n\t\t\t\t\t<li v-for=\"watcher in watchers\" :key=\"watcher.userId\" class=\"cn-follow-toggle__row\">\n\t\t\t\t\t\t<NcAvatar :user=\"watcher.userId\"\n\t\t\t\t\t\t\t:displayName=\"watcher.displayName || watcher.userId\"\n\t\t\t\t\t\t\t:size=\"28\"\n\t\t\t\t\t\t\thideStatus />\n\t\t\t\t\t\t<span class=\"cn-follow-toggle__who\">\n\t\t\t\t\t\t\t<span class=\"cn-follow-toggle__name\">{{ watcher.displayName || watcher.userId }}</span>\n\t\t\t\t\t\t\t<span v-if=\"watcher.created\" class=\"cn-follow-toggle__since\">{{ sinceText(watcher.created) }}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<NcButton\n\t\t\t\t\t\t\tv-if=\"canRemove(watcher)\"\n\t\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t\t:aria-label=\"removeFollowerLabel(watcher)\"\n\t\t\t\t\t\t\t:title=\"removeFollowerLabel(watcher)\"\n\t\t\t\t\t\t\tdata-testid=\"cn-follow-toggle-remove\"\n\t\t\t\t\t\t\t@click=\"removeWatcher(watcher)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<Close :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</li>\n\t\t\t\t</ul>\n\t\t\t\t<div v-if=\"canManage\" class=\"cn-follow-toggle__picker\">\n\t\t\t\t\t<NcSelect\n\t\t\t\t\t\t:inputLabel=\"addColleagueLabel\"\n\t\t\t\t\t\t:modelValue=\"null\"\n\t\t\t\t\t\t:options=\"pickerOptions\"\n\t\t\t\t\t\t:loading=\"pickerLoading\"\n\t\t\t\t\t\t:filterable=\"false\"\n\t\t\t\t\t\tlabel=\"label\"\n\t\t\t\t\t\tdata-testid=\"cn-follow-toggle-picker\"\n\t\t\t\t\t\t@search=\"onPickerSearch\"\n\t\t\t\t\t\t@update:modelValue=\"addWatcher\" />\n\t\t\t\t</div>\n\t\t\t\t<p v-if=\"popoverError\"\n\t\t\t\t\tclass=\"cn-follow-toggle__error\"\n\t\t\t\t\trole=\"alert\"\n\t\t\t\t\tdata-testid=\"cn-follow-toggle-error\">\n\t\t\t\t\t{{ popoverError }}\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t</NcPopover>\n\t</span>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcButton, NcLoadingIcon, NcPopover, NcSelect } from '@nextcloud/vue'\nimport Bell from 'vue-material-design-icons/Bell.vue'\nimport BellOffOutline from 'vue-material-design-icons/BellOffOutline.vue'\nimport Close from 'vue-material-design-icons/Close.vue'\nimport Eye from 'vue-material-design-icons/Eye.vue'\nimport EyeOutline from 'vue-material-design-icons/EyeOutline.vue'\nimport { patchStoredSelf } from '../../utils/patchStoredSelf.js'\nimport { listWatchers, setWatcher, setWatching } from '../../utils/recordInteractions.js'\nimport { searchNextcloudUsers } from '../../utils/userAutocomplete.js'\n\n/**\n * CnFollowToggle — the one Follow control of a record: a Follow toggle bound to\n * `@self.watching`, a notifications switch bound to `@self.watchNotify`, the\n * follower count and a followers popover for users who may see them.\n *\n * Following and favourites are one feature (OpenRegister's\n * `merge-follow-and-favourites`): a favourite is a follow with notifications\n * off, so there is no separate star. While the user follows, a bell beside the\n * toggle turns the follow's notifications on or off (`PUT .../watch` with\n * `{\"notify\": bool}`). With `notifies` false (the register sends no change\n * notifications) the bell is not offered. `compact` renders the toggle as an\n * icon only, without the bell, count or popover, for a column in a list.\n *\n * A click flips the toggle at once and sends `PUT` or `DELETE .../watch`; on\n * failure it flips back and shows the server's message, and a 404 says the\n * user can no longer see the record and emits `not-found`. The count shows\n * only when `watcherCount` is given (OpenRegister sets `@self.watcherCount` for\n * a reader with `update` only); opening the popover then fetches\n * `GET .../watchers`, never before, and a 403 closes it quietly. With\n * `canManage` (`@self.can.manage`) the popover offers an \"Add a colleague\"\n * picker and a remove action on each follower; without it, only the user's own\n * row can be removed.\n *\n * Example:\n * ```vue\n * <CnFollowToggle register=\"pipelinq\" schema=\"ticket\" :object-id=\"id\"\n * :watching=\"self.watching\" :notify=\"self.watchNotify\"\n * :watcher-count=\"self.watcherCount\" :can-manage=\"self.can && self.can.manage\" />\n * ```\n */\nexport default {\n\tname: 'CnFollowToggle',\n\n\tcomponents: { Bell, BellOffOutline, Close, Eye, EyeOutline, NcAvatar, NcButton, NcLoadingIcon, NcPopover, NcSelect },\n\n\tprops: {\n\t\t/** Register slug of the record. */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Schema slug of the record. */\n\t\tschema: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Id of the record. */\n\t\tobjectId: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Whether the current user follows the record (`@self.watching`). */\n\t\twatching: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Whether the user's follow notifies them (`@self.watchNotify`). Null\n\t\t * (an older server, or not following) reads as on.\n\t\t */\n\t\tnotify: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Icon only, without the bell, count or popover: for a column in a list. */\n\t\tcompact: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Number of followers (`@self.watcherCount`). Null hides the count and the popover. */\n\t\twatcherCount: {\n\t\t\ttype: Number,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether the user may add and remove other followers (`@self.can.manage`). */\n\t\tcanManage: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether following sends change notifications. False changes the tooltip to say it does not. */\n\t\tnotifies: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/** User id of the current user. Defaults to Nextcloud's `OC.currentUser`. */\n\t\tcurrentUser: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['change', 'error', 'not-found'],\n\n\tdata() {\n\t\treturn {\n\t\t\tstate: this.watching,\n\t\t\tnotifyState: this.notify !== false,\n\t\t\tcount: this.watcherCount,\n\t\t\tbusy: false,\n\t\t\topen: false,\n\t\t\twatchers: [],\n\t\t\tloadingWatchers: false,\n\t\t\tpopoverError: '',\n\t\t\tpickerOptions: [],\n\t\t\tpickerLoading: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tfollowLabel() {\n\t\t\treturn t('nextcloud-vue', 'Follow')\n\t\t},\n\n\t\tfollowingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Following')\n\t\t},\n\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading …')\n\t\t},\n\n\t\taddColleagueLabel() {\n\t\t\treturn t('nextcloud-vue', 'Add a colleague')\n\t\t},\n\n\t\tnotifyLabel() {\n\t\t\treturn this.notifyState\n\t\t\t\t? t('nextcloud-vue', 'Turn notifications off')\n\t\t\t\t: t('nextcloud-vue', 'Turn notifications on')\n\t\t},\n\n\t\t/**\n\t\t * Whether the bell renders: following, not compact, and the register notifies.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tshowNotifySwitch() {\n\t\t\treturn this.state && !this.compact && this.notifies\n\t\t},\n\n\t\tfollowersLabel() {\n\t\t\treturn t('nextcloud-vue', '{count} followers', { count: this.count })\n\t\t},\n\n\t\ttooltip() {\n\t\t\treturn this.notifies\n\t\t\t\t? t('nextcloud-vue', 'Follow this record to get its updates')\n\t\t\t\t: t('nextcloud-vue', 'You will see it under Following. This register sends no change notifications.')\n\t\t},\n\n\t\tme() {\n\t\t\treturn this.currentUser || (typeof OC !== 'undefined' && OC ? OC.currentUser : '') || ''\n\t\t},\n\t},\n\n\twatch: {\n\t\twatching(value) {\n\t\t\tthis.state = value\n\t\t},\n\n\t\tnotify(value) {\n\t\t\tthis.notifyState = value !== false\n\t\t},\n\n\t\twatcherCount(value) {\n\t\t\tthis.count = value\n\t\t},\n\n\t\topen(value) {\n\t\t\tif (value) {\n\t\t\t\tthis.loadWatchers()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tsinceText(created) {\n\t\t\tconst d = new Date(created)\n\t\t\treturn Number.isNaN(d.getTime())\n\t\t\t\t? ''\n\t\t\t\t: t('nextcloud-vue', 'Since {date}', { date: d.toLocaleDateString() })\n\t\t},\n\n\t\tremoveFollowerLabel(watcher) {\n\t\t\treturn t('nextcloud-vue', 'Remove {name} as a follower', { name: watcher.displayName || watcher.userId })\n\t\t},\n\n\t\tcanRemove(watcher) {\n\t\t\treturn this.canManage || (this.me !== '' && watcher.userId === this.me)\n\t\t},\n\n\t\tasync toggle() {\n\t\t\tif (this.busy) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst previous = this.state\n\t\t\tthis.state = !previous\n\t\t\tif (this.count !== null) {\n\t\t\t\tthis.count = Math.max(0, this.count + (this.state ? 1 : -1))\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\tconst result = await setWatching(this.register, this.schema, this.objectId, this.state)\n\t\t\tthis.busy = false\n\t\t\tif (!result.ok) {\n\t\t\t\tthis.state = previous\n\t\t\t\tif (this.count !== null) {\n\t\t\t\t\tthis.count = Math.max(0, this.count + (previous ? 1 : -1))\n\t\t\t\t}\n\t\t\t\tconst message = result.status === 404\n\t\t\t\t\t? t('nextcloud-vue', 'You can no longer see this record.')\n\t\t\t\t\t: (result.message || t('nextcloud-vue', 'Could not change following.'))\n\t\t\t\timport('@nextcloud/dialogs').then(({ showError }) => showError(message)).catch(() => {})\n\t\t\t\t/** @event error Emitted when a call failed; payload is the message shown. */\n\t\t\t\tthis.$emit('error', message)\n\t\t\t\tif (result.status === 404) {\n\t\t\t\t\t/** @event not-found Emitted on a 404: the record went away or access was withdrawn. */\n\t\t\t\t\tthis.$emit('not-found')\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// A new follow notifies (the server's default); an unfollow has no switch.\n\t\t\tthis.notifyState = this.state\n\t\t\tpatchStoredSelf(this.register, this.schema, this.objectId, {\n\t\t\t\twatching: this.state,\n\t\t\t\tfavourite: this.state,\n\t\t\t\twatchNotify: this.state ? true : undefined,\n\t\t\t\t...(this.count !== null ? { watcherCount: this.count } : {}),\n\t\t\t})\n\t\t\t/** @event change Emitted after a successful call. Payload: `{ watching, notify, count }`. */\n\t\t\tthis.$emit('change', { watching: this.state, notify: this.state ? this.notifyState : null, count: this.count })\n\t\t},\n\n\t\t/**\n\t\t * Turn the follow's notifications on or off, optimistically.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/one-follow-control/specs/record-follow/spec.md#requirement-one-follow-control-with-a-notifications-switch\n\t\t */\n\t\tasync toggleNotify() {\n\t\t\tif (this.busy || !this.state) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst previous = this.notifyState\n\t\t\tthis.notifyState = !previous\n\t\t\tthis.busy = true\n\t\t\tconst result = await setWatching(this.register, this.schema, this.objectId, true, { notify: this.notifyState })\n\t\t\tthis.busy = false\n\t\t\tif (!result.ok) {\n\t\t\t\tthis.notifyState = previous\n\t\t\t\tconst message = result.status === 404\n\t\t\t\t\t? t('nextcloud-vue', 'You can no longer see this record.')\n\t\t\t\t\t: (result.message || t('nextcloud-vue', 'Could not change notifications.'))\n\t\t\t\timport('@nextcloud/dialogs').then(({ showError }) => showError(message)).catch(() => {})\n\t\t\t\tthis.$emit('error', message)\n\t\t\t\tif (result.status === 404) {\n\t\t\t\t\tthis.$emit('not-found')\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tpatchStoredSelf(this.register, this.schema, this.objectId, { watchNotify: this.notifyState })\n\t\t\tthis.$emit('change', { watching: true, notify: this.notifyState, count: this.count })\n\t\t},\n\n\t\tasync loadWatchers() {\n\t\t\tthis.popoverError = ''\n\t\t\tthis.loadingWatchers = true\n\t\t\tconst result = await listWatchers(this.register, this.schema, this.objectId)\n\t\t\tthis.loadingWatchers = false\n\t\t\tif (!result.ok) {\n\t\t\t\t// No right to see the list (403) or a failure: close without a toast.\n\t\t\t\tthis.open = false\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst data = result.data || {}\n\t\t\tthis.watchers = Array.isArray(data.results) ? data.results : []\n\t\t\tthis.count = Number.isFinite(data.total) ? data.total : this.watchers.length\n\t\t},\n\n\t\tasync onPickerSearch(query) {\n\t\t\tthis.pickerLoading = true\n\t\t\tconst found = await searchNextcloudUsers(query || '', { limit: 10 })\n\t\t\tconst already = new Set(this.watchers.map((w) => w.userId))\n\t\t\tthis.pickerOptions = found.filter((u) => !already.has(u.id)).map((u) => ({ ...u, label: u.label || u.displayName || u.id }))\n\t\t\tthis.pickerLoading = false\n\t\t},\n\n\t\tasync addWatcher(option) {\n\t\t\tif (!option || !option.id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.popoverError = ''\n\t\t\tconst result = await setWatcher(this.register, this.schema, this.objectId, option.id, true)\n\t\t\tif (!result.ok) {\n\t\t\t\tthis.popoverError = result.message || t('nextcloud-vue', 'Could not add this colleague.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tawait this.loadWatchers()\n\t\t},\n\n\t\tasync removeWatcher(watcher) {\n\t\t\tthis.popoverError = ''\n\t\t\tconst result = await setWatcher(this.register, this.schema, this.objectId, watcher.userId, false)\n\t\t\tif (!result.ok) {\n\t\t\t\tthis.popoverError = result.message || t('nextcloud-vue', 'Could not remove this follower.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (watcher.userId === this.me) {\n\t\t\t\tthis.state = false\n\t\t\t\tthis.$emit('change', { watching: false, count: this.count })\n\t\t\t}\n\t\t\tawait this.loadWatchers()\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-follow-toggle {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n}\n\n.cn-follow-toggle__on {\n\tcolor: var(--color-primary-element);\n}\n\n.cn-follow-toggle__popover {\n\tmin-width: 260px;\n\tpadding: 12px;\n}\n\n.cn-follow-toggle__list {\n\tlist-style: none;\n\tmargin: 0 0 8px;\n\tpadding: 0;\n}\n\n.cn-follow-toggle__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 4px 0;\n}\n\n.cn-follow-toggle__who {\n\tdisplay: flex;\n\tflex-direction: column;\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n.cn-follow-toggle__since {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: var(--font-size-small, 13px);\n}\n\n.cn-follow-toggle__error {\n\tmargin: 8px 0 0;\n\tcolor: var(--color-error-text);\n}\n</style>\n"],"names":["Bell","t"],"mappings":"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"}
|
|
@@ -1,28 +1,24 @@
|
|
|
1
|
-
import { resolveComponent, openBlock, createElementBlock, createVNode, withModifiers, withCtx, createBlock, createTextVNode, toDisplayString,
|
|
1
|
+
import { resolveComponent, openBlock, createElementBlock, normalizeClass, createVNode, withModifiers, withCtx, createBlock, Fragment, createTextVNode, toDisplayString, createCommentVNode, createElementVNode, renderList } from 'vue';
|
|
2
2
|
|
|
3
3
|
const _hoisted_1 = {
|
|
4
|
-
class: "cn-follow-toggle",
|
|
5
|
-
"data-testid": "cn-follow-toggle"
|
|
6
|
-
};
|
|
7
|
-
const _hoisted_2 = {
|
|
8
4
|
class: "cn-follow-toggle__popover",
|
|
9
5
|
"data-testid": "cn-follow-toggle-popover"
|
|
10
6
|
};
|
|
11
|
-
const
|
|
7
|
+
const _hoisted_2 = {
|
|
12
8
|
key: 1,
|
|
13
9
|
class: "cn-follow-toggle__list"
|
|
14
10
|
};
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
11
|
+
const _hoisted_3 = { class: "cn-follow-toggle__who" };
|
|
12
|
+
const _hoisted_4 = { class: "cn-follow-toggle__name" };
|
|
13
|
+
const _hoisted_5 = {
|
|
18
14
|
key: 0,
|
|
19
15
|
class: "cn-follow-toggle__since"
|
|
20
16
|
};
|
|
21
|
-
const
|
|
17
|
+
const _hoisted_6 = {
|
|
22
18
|
key: 2,
|
|
23
19
|
class: "cn-follow-toggle__picker"
|
|
24
20
|
};
|
|
25
|
-
const
|
|
21
|
+
const _hoisted_7 = {
|
|
26
22
|
key: 3,
|
|
27
23
|
class: "cn-follow-toggle__error",
|
|
28
24
|
role: "alert",
|
|
@@ -30,19 +26,25 @@ const _hoisted_8 = {
|
|
|
30
26
|
};
|
|
31
27
|
|
|
32
28
|
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
33
|
-
const
|
|
34
|
-
const
|
|
29
|
+
const _component_Eye = resolveComponent("Eye");
|
|
30
|
+
const _component_EyeOutline = resolveComponent("EyeOutline");
|
|
35
31
|
const _component_NcButton = resolveComponent("NcButton");
|
|
32
|
+
const _component_Bell = resolveComponent("Bell");
|
|
33
|
+
const _component_BellOffOutline = resolveComponent("BellOffOutline");
|
|
36
34
|
const _component_NcLoadingIcon = resolveComponent("NcLoadingIcon");
|
|
37
35
|
const _component_NcAvatar = resolveComponent("NcAvatar");
|
|
38
36
|
const _component_Close = resolveComponent("Close");
|
|
39
37
|
const _component_NcSelect = resolveComponent("NcSelect");
|
|
40
38
|
const _component_NcPopover = resolveComponent("NcPopover");
|
|
41
39
|
|
|
42
|
-
return (openBlock(), createElementBlock("span",
|
|
40
|
+
return (openBlock(), createElementBlock("span", {
|
|
41
|
+
class: normalizeClass(["cn-follow-toggle", { 'cn-follow-toggle--compact': $props.compact }]),
|
|
42
|
+
"data-testid": "cn-follow-toggle"
|
|
43
|
+
}, [
|
|
43
44
|
createVNode(_component_NcButton, {
|
|
44
|
-
variant: $data.state ? 'secondary' : 'tertiary',
|
|
45
|
+
variant: $data.state && !$props.compact ? 'secondary' : 'tertiary',
|
|
45
46
|
"aria-pressed": $data.state ? 'true' : 'false',
|
|
47
|
+
"aria-label": $props.compact ? ($data.state ? $options.followingLabel : $options.followLabel) : undefined,
|
|
46
48
|
title: $options.tooltip,
|
|
47
49
|
disabled: $data.busy,
|
|
48
50
|
"data-testid": "cn-follow-toggle-button",
|
|
@@ -50,23 +52,53 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
50
52
|
}, {
|
|
51
53
|
icon: withCtx(() => [
|
|
52
54
|
($data.state)
|
|
53
|
-
? (openBlock(), createBlock(
|
|
55
|
+
? (openBlock(), createBlock(_component_Eye, {
|
|
54
56
|
key: 0,
|
|
55
|
-
size: 20
|
|
57
|
+
size: 20,
|
|
58
|
+
class: "cn-follow-toggle__on"
|
|
56
59
|
}))
|
|
57
|
-
: (openBlock(), createBlock(
|
|
60
|
+
: (openBlock(), createBlock(_component_EyeOutline, {
|
|
58
61
|
key: 1,
|
|
59
62
|
size: 20
|
|
60
63
|
}))
|
|
61
64
|
]),
|
|
62
65
|
default: withCtx(() => [
|
|
63
|
-
|
|
66
|
+
(!$props.compact)
|
|
67
|
+
? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
|
|
68
|
+
createTextVNode(toDisplayString($data.state ? $options.followingLabel : $options.followLabel), 1 /* TEXT */)
|
|
69
|
+
], 64 /* STABLE_FRAGMENT */))
|
|
70
|
+
: createCommentVNode("v-if", true)
|
|
64
71
|
]),
|
|
65
72
|
_: 1 /* STABLE */
|
|
66
|
-
}, 8 /* PROPS */, ["variant", "aria-pressed", "title", "disabled", "onClick"]),
|
|
67
|
-
($
|
|
68
|
-
? (openBlock(), createBlock(
|
|
73
|
+
}, 8 /* PROPS */, ["variant", "aria-pressed", "aria-label", "title", "disabled", "onClick"]),
|
|
74
|
+
($options.showNotifySwitch)
|
|
75
|
+
? (openBlock(), createBlock(_component_NcButton, {
|
|
69
76
|
key: 0,
|
|
77
|
+
variant: "tertiary",
|
|
78
|
+
"aria-pressed": $data.notifyState ? 'true' : 'false',
|
|
79
|
+
"aria-label": $options.notifyLabel,
|
|
80
|
+
title: $options.notifyLabel,
|
|
81
|
+
disabled: $data.busy,
|
|
82
|
+
"data-testid": "cn-follow-toggle-notify",
|
|
83
|
+
onClick: withModifiers($options.toggleNotify, ["stop"])
|
|
84
|
+
}, {
|
|
85
|
+
icon: withCtx(() => [
|
|
86
|
+
($data.notifyState)
|
|
87
|
+
? (openBlock(), createBlock(_component_Bell, {
|
|
88
|
+
key: 0,
|
|
89
|
+
size: 20
|
|
90
|
+
}))
|
|
91
|
+
: (openBlock(), createBlock(_component_BellOffOutline, {
|
|
92
|
+
key: 1,
|
|
93
|
+
size: 20
|
|
94
|
+
}))
|
|
95
|
+
]),
|
|
96
|
+
_: 1 /* STABLE */
|
|
97
|
+
}, 8 /* PROPS */, ["aria-pressed", "aria-label", "title", "disabled", "onClick"]))
|
|
98
|
+
: createCommentVNode("v-if", true),
|
|
99
|
+
($data.count !== null && !$props.compact)
|
|
100
|
+
? (openBlock(), createBlock(_component_NcPopover, {
|
|
101
|
+
key: 1,
|
|
70
102
|
shown: $data.open,
|
|
71
103
|
"onUpdate:shown": _cache[0] || (_cache[0] = $event => (($data.open) = $event)),
|
|
72
104
|
placement: "bottom-start"
|
|
@@ -85,14 +117,14 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
85
117
|
}, 8 /* PROPS */, ["aria-label"])
|
|
86
118
|
]),
|
|
87
119
|
default: withCtx(() => [
|
|
88
|
-
createElementVNode("div",
|
|
120
|
+
createElementVNode("div", _hoisted_1, [
|
|
89
121
|
($data.loadingWatchers)
|
|
90
122
|
? (openBlock(), createBlock(_component_NcLoadingIcon, {
|
|
91
123
|
key: 0,
|
|
92
124
|
size: 20,
|
|
93
125
|
name: $options.loadingLabel
|
|
94
126
|
}, null, 8 /* PROPS */, ["name"]))
|
|
95
|
-
: (openBlock(), createElementBlock("ul",
|
|
127
|
+
: (openBlock(), createElementBlock("ul", _hoisted_2, [
|
|
96
128
|
(openBlock(true), createElementBlock(Fragment, null, renderList($data.watchers, (watcher) => {
|
|
97
129
|
return (openBlock(), createElementBlock("li", {
|
|
98
130
|
key: watcher.userId,
|
|
@@ -104,10 +136,10 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
104
136
|
size: 28,
|
|
105
137
|
hideStatus: ""
|
|
106
138
|
}, null, 8 /* PROPS */, ["user", "displayName"]),
|
|
107
|
-
createElementVNode("span",
|
|
108
|
-
createElementVNode("span",
|
|
139
|
+
createElementVNode("span", _hoisted_3, [
|
|
140
|
+
createElementVNode("span", _hoisted_4, toDisplayString(watcher.displayName || watcher.userId), 1 /* TEXT */),
|
|
109
141
|
(watcher.created)
|
|
110
|
-
? (openBlock(), createElementBlock("span",
|
|
142
|
+
? (openBlock(), createElementBlock("span", _hoisted_5, toDisplayString($options.sinceText(watcher.created)), 1 /* TEXT */))
|
|
111
143
|
: createCommentVNode("v-if", true)
|
|
112
144
|
]),
|
|
113
145
|
($options.canRemove(watcher))
|
|
@@ -129,7 +161,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
129
161
|
}), 128 /* KEYED_FRAGMENT */))
|
|
130
162
|
])),
|
|
131
163
|
($props.canManage)
|
|
132
|
-
? (openBlock(), createElementBlock("div",
|
|
164
|
+
? (openBlock(), createElementBlock("div", _hoisted_6, [
|
|
133
165
|
createVNode(_component_NcSelect, {
|
|
134
166
|
inputLabel: $options.addColleagueLabel,
|
|
135
167
|
modelValue: null,
|
|
@@ -144,14 +176,14 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
144
176
|
]))
|
|
145
177
|
: createCommentVNode("v-if", true),
|
|
146
178
|
($data.popoverError)
|
|
147
|
-
? (openBlock(), createElementBlock("p",
|
|
179
|
+
? (openBlock(), createElementBlock("p", _hoisted_7, toDisplayString($data.popoverError), 1 /* TEXT */))
|
|
148
180
|
: createCommentVNode("v-if", true)
|
|
149
181
|
])
|
|
150
182
|
]),
|
|
151
183
|
_: 1 /* STABLE */
|
|
152
184
|
}, 8 /* PROPS */, ["shown"]))
|
|
153
185
|
: createCommentVNode("v-if", true)
|
|
154
|
-
]))
|
|
186
|
+
], 2 /* CLASS */))
|
|
155
187
|
}
|
|
156
188
|
|
|
157
189
|
export { render };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnFollowToggle.vue3.js","sources":["../../../../src/components/CnFollowToggle/CnFollowToggle.vue?vue&type=template&id=eec896e2&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<span class=\"cn-follow-toggle\" data-testid=\"cn-follow-toggle\">\n\t\t<NcButton\n\t\t\t:variant=\"state ? 'secondary' : 'tertiary'\"\n\t\t\t:aria-pressed=\"state ? 'true' : 'false'\"\n\t\t\t:title=\"tooltip\"\n\t\t\t:disabled=\"busy\"\n\t\t\tdata-testid=\"cn-follow-toggle-button\"\n\t\t\t@click.stop=\"toggle\">\n\t\t\t<template #icon>\n\t\t\t\t<Bell v-if=\"state\" :size=\"20\" />\n\t\t\t\t<BellOutline v-else :size=\"20\" />\n\t\t\t</template>\n\t\t\t{{ state ? followingLabel : followLabel }}\n\t\t</NcButton>\n\t\t<NcPopover\n\t\t\tv-if=\"count !== null\"\n\t\t\tv-model:shown=\"open\"\n\t\t\tplacement=\"bottom-start\">\n\t\t\t<template #trigger>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\tclass=\"cn-follow-toggle__count\"\n\t\t\t\t\t:aria-label=\"followersLabel\"\n\t\t\t\t\tdata-testid=\"cn-follow-toggle-count\">\n\t\t\t\t\t{{ count }}\n\t\t\t\t</NcButton>\n\t\t\t</template>\n\t\t\t<div class=\"cn-follow-toggle__popover\" data-testid=\"cn-follow-toggle-popover\">\n\t\t\t\t<NcLoadingIcon v-if=\"loadingWatchers\" :size=\"20\" :name=\"loadingLabel\" />\n\t\t\t\t<ul v-else class=\"cn-follow-toggle__list\">\n\t\t\t\t\t<li v-for=\"watcher in watchers\" :key=\"watcher.userId\" class=\"cn-follow-toggle__row\">\n\t\t\t\t\t\t<NcAvatar :user=\"watcher.userId\"\n\t\t\t\t\t\t\t:displayName=\"watcher.displayName || watcher.userId\"\n\t\t\t\t\t\t\t:size=\"28\"\n\t\t\t\t\t\t\thideStatus />\n\t\t\t\t\t\t<span class=\"cn-follow-toggle__who\">\n\t\t\t\t\t\t\t<span class=\"cn-follow-toggle__name\">{{ watcher.displayName || watcher.userId }}</span>\n\t\t\t\t\t\t\t<span v-if=\"watcher.created\" class=\"cn-follow-toggle__since\">{{ sinceText(watcher.created) }}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<NcButton\n\t\t\t\t\t\t\tv-if=\"canRemove(watcher)\"\n\t\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t\t:aria-label=\"removeFollowerLabel(watcher)\"\n\t\t\t\t\t\t\t:title=\"removeFollowerLabel(watcher)\"\n\t\t\t\t\t\t\tdata-testid=\"cn-follow-toggle-remove\"\n\t\t\t\t\t\t\t@click=\"removeWatcher(watcher)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<Close :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</li>\n\t\t\t\t</ul>\n\t\t\t\t<div v-if=\"canManage\" class=\"cn-follow-toggle__picker\">\n\t\t\t\t\t<NcSelect\n\t\t\t\t\t\t:inputLabel=\"addColleagueLabel\"\n\t\t\t\t\t\t:modelValue=\"null\"\n\t\t\t\t\t\t:options=\"pickerOptions\"\n\t\t\t\t\t\t:loading=\"pickerLoading\"\n\t\t\t\t\t\t:filterable=\"false\"\n\t\t\t\t\t\tlabel=\"label\"\n\t\t\t\t\t\tdata-testid=\"cn-follow-toggle-picker\"\n\t\t\t\t\t\t@search=\"onPickerSearch\"\n\t\t\t\t\t\t@update:modelValue=\"addWatcher\" />\n\t\t\t\t</div>\n\t\t\t\t<p v-if=\"popoverError\"\n\t\t\t\t\tclass=\"cn-follow-toggle__error\"\n\t\t\t\t\trole=\"alert\"\n\t\t\t\t\tdata-testid=\"cn-follow-toggle-error\">\n\t\t\t\t\t{{ popoverError }}\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t</NcPopover>\n\t</span>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcButton, NcLoadingIcon, NcPopover, NcSelect } from '@nextcloud/vue'\nimport Bell from 'vue-material-design-icons/Bell.vue'\nimport BellOutline from 'vue-material-design-icons/BellOutline.vue'\nimport Close from 'vue-material-design-icons/Close.vue'\nimport { patchStoredSelf } from '../../utils/patchStoredSelf.js'\nimport { listWatchers, setWatcher, setWatching } from '../../utils/recordInteractions.js'\nimport { searchNextcloudUsers } from '../../utils/userAutocomplete.js'\n\n/**\n * CnFollowToggle — a Follow toggle bound to a record's `@self.watching` marker,\n * with the follower count and a followers popover for users who may see them.\n *\n * A click flips the toggle at once and sends `PUT` or `DELETE .../watch`; on\n * failure it flips back and shows the server's message, and a 404 says the\n * user can no longer see the record and emits `not-found`. The count shows\n * only when `watcherCount` is given (OpenRegister sets `@self.watcherCount` for\n * a reader with `update` only); opening the popover then fetches\n * `GET .../watchers`, never before, and a 403 closes it quietly. With\n * `canManage` (`@self.can.manage`) the popover offers an \"Add a colleague\"\n * picker and a remove action on each follower; without it, only the user's own\n * row can be removed.\n *\n * Example:\n * ```vue\n * <CnFollowToggle register=\"pipelinq\" schema=\"ticket\" :object-id=\"id\"\n * :watching=\"self.watching\" :watcher-count=\"self.watcherCount\" :can-manage=\"self.can && self.can.manage\" />\n * ```\n */\nexport default {\n\tname: 'CnFollowToggle',\n\n\tcomponents: { Bell, BellOutline, Close, NcAvatar, NcButton, NcLoadingIcon, NcPopover, NcSelect },\n\n\tprops: {\n\t\t/** Register slug of the record. */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Schema slug of the record. */\n\t\tschema: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Id of the record. */\n\t\tobjectId: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Whether the current user follows the record (`@self.watching`). */\n\t\twatching: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Number of followers (`@self.watcherCount`). Null hides the count and the popover. */\n\t\twatcherCount: {\n\t\t\ttype: Number,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether the user may add and remove other followers (`@self.can.manage`). */\n\t\tcanManage: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether following sends change notifications. False changes the tooltip to say it does not. */\n\t\tnotifies: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/** User id of the current user. Defaults to Nextcloud's `OC.currentUser`. */\n\t\tcurrentUser: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['change', 'error', 'not-found'],\n\n\tdata() {\n\t\treturn {\n\t\t\tstate: this.watching,\n\t\t\tcount: this.watcherCount,\n\t\t\tbusy: false,\n\t\t\topen: false,\n\t\t\twatchers: [],\n\t\t\tloadingWatchers: false,\n\t\t\tpopoverError: '',\n\t\t\tpickerOptions: [],\n\t\t\tpickerLoading: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tfollowLabel() {\n\t\t\treturn t('nextcloud-vue', 'Follow')\n\t\t},\n\n\t\tfollowingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Following')\n\t\t},\n\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading …')\n\t\t},\n\n\t\taddColleagueLabel() {\n\t\t\treturn t('nextcloud-vue', 'Add a colleague')\n\t\t},\n\n\t\tfollowersLabel() {\n\t\t\treturn t('nextcloud-vue', '{count} followers', { count: this.count })\n\t\t},\n\n\t\ttooltip() {\n\t\t\treturn this.notifies\n\t\t\t\t? t('nextcloud-vue', 'Follow this record to get its updates')\n\t\t\t\t: t('nextcloud-vue', 'You will see it under Following. This register sends no change notifications.')\n\t\t},\n\n\t\tme() {\n\t\t\treturn this.currentUser || (typeof OC !== 'undefined' && OC ? OC.currentUser : '') || ''\n\t\t},\n\t},\n\n\twatch: {\n\t\twatching(value) {\n\t\t\tthis.state = value\n\t\t},\n\n\t\twatcherCount(value) {\n\t\t\tthis.count = value\n\t\t},\n\n\t\topen(value) {\n\t\t\tif (value) {\n\t\t\t\tthis.loadWatchers()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tsinceText(created) {\n\t\t\tconst d = new Date(created)\n\t\t\treturn Number.isNaN(d.getTime())\n\t\t\t\t? ''\n\t\t\t\t: t('nextcloud-vue', 'Since {date}', { date: d.toLocaleDateString() })\n\t\t},\n\n\t\tremoveFollowerLabel(watcher) {\n\t\t\treturn t('nextcloud-vue', 'Remove {name} as a follower', { name: watcher.displayName || watcher.userId })\n\t\t},\n\n\t\tcanRemove(watcher) {\n\t\t\treturn this.canManage || (this.me !== '' && watcher.userId === this.me)\n\t\t},\n\n\t\tasync toggle() {\n\t\t\tif (this.busy) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst previous = this.state\n\t\t\tthis.state = !previous\n\t\t\tif (this.count !== null) {\n\t\t\t\tthis.count = Math.max(0, this.count + (this.state ? 1 : -1))\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\tconst result = await setWatching(this.register, this.schema, this.objectId, this.state)\n\t\t\tthis.busy = false\n\t\t\tif (!result.ok) {\n\t\t\t\tthis.state = previous\n\t\t\t\tif (this.count !== null) {\n\t\t\t\t\tthis.count = Math.max(0, this.count + (previous ? 1 : -1))\n\t\t\t\t}\n\t\t\t\tconst message = result.status === 404\n\t\t\t\t\t? t('nextcloud-vue', 'You can no longer see this record.')\n\t\t\t\t\t: (result.message || t('nextcloud-vue', 'Could not change following.'))\n\t\t\t\timport('@nextcloud/dialogs').then(({ showError }) => showError(message)).catch(() => {})\n\t\t\t\t/** @event error Emitted when a call failed; payload is the message shown. */\n\t\t\t\tthis.$emit('error', message)\n\t\t\t\tif (result.status === 404) {\n\t\t\t\t\t/** @event not-found Emitted on a 404: the record went away or access was withdrawn. */\n\t\t\t\t\tthis.$emit('not-found')\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tpatchStoredSelf(this.register, this.schema, this.objectId, {\n\t\t\t\twatching: this.state,\n\t\t\t\t...(this.count !== null ? { watcherCount: this.count } : {}),\n\t\t\t})\n\t\t\t/** @event change Emitted after a successful call. Payload: `{ watching, count }`. */\n\t\t\tthis.$emit('change', { watching: this.state, count: this.count })\n\t\t},\n\n\t\tasync loadWatchers() {\n\t\t\tthis.popoverError = ''\n\t\t\tthis.loadingWatchers = true\n\t\t\tconst result = await listWatchers(this.register, this.schema, this.objectId)\n\t\t\tthis.loadingWatchers = false\n\t\t\tif (!result.ok) {\n\t\t\t\t// No right to see the list (403) or a failure: close without a toast.\n\t\t\t\tthis.open = false\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst data = result.data || {}\n\t\t\tthis.watchers = Array.isArray(data.results) ? data.results : []\n\t\t\tthis.count = Number.isFinite(data.total) ? data.total : this.watchers.length\n\t\t},\n\n\t\tasync onPickerSearch(query) {\n\t\t\tthis.pickerLoading = true\n\t\t\tconst found = await searchNextcloudUsers(query || '', { limit: 10 })\n\t\t\tconst already = new Set(this.watchers.map((w) => w.userId))\n\t\t\tthis.pickerOptions = found.filter((u) => !already.has(u.id)).map((u) => ({ ...u, label: u.label || u.displayName || u.id }))\n\t\t\tthis.pickerLoading = false\n\t\t},\n\n\t\tasync addWatcher(option) {\n\t\t\tif (!option || !option.id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.popoverError = ''\n\t\t\tconst result = await setWatcher(this.register, this.schema, this.objectId, option.id, true)\n\t\t\tif (!result.ok) {\n\t\t\t\tthis.popoverError = result.message || t('nextcloud-vue', 'Could not add this colleague.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tawait this.loadWatchers()\n\t\t},\n\n\t\tasync removeWatcher(watcher) {\n\t\t\tthis.popoverError = ''\n\t\t\tconst result = await setWatcher(this.register, this.schema, this.objectId, watcher.userId, false)\n\t\t\tif (!result.ok) {\n\t\t\t\tthis.popoverError = result.message || t('nextcloud-vue', 'Could not remove this follower.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (watcher.userId === this.me) {\n\t\t\t\tthis.state = false\n\t\t\t\tthis.$emit('change', { watching: false, count: this.count })\n\t\t\t}\n\t\t\tawait this.loadWatchers()\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-follow-toggle {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n}\n\n.cn-follow-toggle__popover {\n\tmin-width: 260px;\n\tpadding: 12px;\n}\n\n.cn-follow-toggle__list {\n\tlist-style: none;\n\tmargin: 0 0 8px;\n\tpadding: 0;\n}\n\n.cn-follow-toggle__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 4px 0;\n}\n\n.cn-follow-toggle__who {\n\tdisplay: flex;\n\tflex-direction: column;\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n.cn-follow-toggle__since {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: var(--font-size-small, 13px);\n}\n\n.cn-follow-toggle__error {\n\tmargin: 8px 0 0;\n\tcolor: var(--color-error-text);\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createVNode","_createBlock","_createTextVNode","_toDisplayString","_createElementVNode","_Fragment","_renderList"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnFollowToggle.vue3.js","sources":["../../../../src/components/CnFollowToggle/CnFollowToggle.vue?vue&type=template&id=eec896e2&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<span class=\"cn-follow-toggle\"\n\t\t:class=\"{ 'cn-follow-toggle--compact': compact }\"\n\t\tdata-testid=\"cn-follow-toggle\">\n\t\t<NcButton\n\t\t\t:variant=\"state && !compact ? 'secondary' : 'tertiary'\"\n\t\t\t:aria-pressed=\"state ? 'true' : 'false'\"\n\t\t\t:aria-label=\"compact ? (state ? followingLabel : followLabel) : undefined\"\n\t\t\t:title=\"tooltip\"\n\t\t\t:disabled=\"busy\"\n\t\t\tdata-testid=\"cn-follow-toggle-button\"\n\t\t\t@click.stop=\"toggle\">\n\t\t\t<template #icon>\n\t\t\t\t<Eye v-if=\"state\" :size=\"20\" class=\"cn-follow-toggle__on\" />\n\t\t\t\t<EyeOutline v-else :size=\"20\" />\n\t\t\t</template>\n\t\t\t<template v-if=\"!compact\">\n\t\t\t\t{{ state ? followingLabel : followLabel }}\n\t\t\t</template>\n\t\t</NcButton>\n\t\t<NcButton\n\t\t\tv-if=\"showNotifySwitch\"\n\t\t\tvariant=\"tertiary\"\n\t\t\t:aria-pressed=\"notifyState ? 'true' : 'false'\"\n\t\t\t:aria-label=\"notifyLabel\"\n\t\t\t:title=\"notifyLabel\"\n\t\t\t:disabled=\"busy\"\n\t\t\tdata-testid=\"cn-follow-toggle-notify\"\n\t\t\t@click.stop=\"toggleNotify\">\n\t\t\t<template #icon>\n\t\t\t\t<Bell v-if=\"notifyState\" :size=\"20\" />\n\t\t\t\t<BellOffOutline v-else :size=\"20\" />\n\t\t\t</template>\n\t\t</NcButton>\n\t\t<NcPopover\n\t\t\tv-if=\"count !== null && !compact\"\n\t\t\tv-model:shown=\"open\"\n\t\t\tplacement=\"bottom-start\">\n\t\t\t<template #trigger>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\tclass=\"cn-follow-toggle__count\"\n\t\t\t\t\t:aria-label=\"followersLabel\"\n\t\t\t\t\tdata-testid=\"cn-follow-toggle-count\">\n\t\t\t\t\t{{ count }}\n\t\t\t\t</NcButton>\n\t\t\t</template>\n\t\t\t<div class=\"cn-follow-toggle__popover\" data-testid=\"cn-follow-toggle-popover\">\n\t\t\t\t<NcLoadingIcon v-if=\"loadingWatchers\" :size=\"20\" :name=\"loadingLabel\" />\n\t\t\t\t<ul v-else class=\"cn-follow-toggle__list\">\n\t\t\t\t\t<li v-for=\"watcher in watchers\" :key=\"watcher.userId\" class=\"cn-follow-toggle__row\">\n\t\t\t\t\t\t<NcAvatar :user=\"watcher.userId\"\n\t\t\t\t\t\t\t:displayName=\"watcher.displayName || watcher.userId\"\n\t\t\t\t\t\t\t:size=\"28\"\n\t\t\t\t\t\t\thideStatus />\n\t\t\t\t\t\t<span class=\"cn-follow-toggle__who\">\n\t\t\t\t\t\t\t<span class=\"cn-follow-toggle__name\">{{ watcher.displayName || watcher.userId }}</span>\n\t\t\t\t\t\t\t<span v-if=\"watcher.created\" class=\"cn-follow-toggle__since\">{{ sinceText(watcher.created) }}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<NcButton\n\t\t\t\t\t\t\tv-if=\"canRemove(watcher)\"\n\t\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t\t:aria-label=\"removeFollowerLabel(watcher)\"\n\t\t\t\t\t\t\t:title=\"removeFollowerLabel(watcher)\"\n\t\t\t\t\t\t\tdata-testid=\"cn-follow-toggle-remove\"\n\t\t\t\t\t\t\t@click=\"removeWatcher(watcher)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<Close :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</li>\n\t\t\t\t</ul>\n\t\t\t\t<div v-if=\"canManage\" class=\"cn-follow-toggle__picker\">\n\t\t\t\t\t<NcSelect\n\t\t\t\t\t\t:inputLabel=\"addColleagueLabel\"\n\t\t\t\t\t\t:modelValue=\"null\"\n\t\t\t\t\t\t:options=\"pickerOptions\"\n\t\t\t\t\t\t:loading=\"pickerLoading\"\n\t\t\t\t\t\t:filterable=\"false\"\n\t\t\t\t\t\tlabel=\"label\"\n\t\t\t\t\t\tdata-testid=\"cn-follow-toggle-picker\"\n\t\t\t\t\t\t@search=\"onPickerSearch\"\n\t\t\t\t\t\t@update:modelValue=\"addWatcher\" />\n\t\t\t\t</div>\n\t\t\t\t<p v-if=\"popoverError\"\n\t\t\t\t\tclass=\"cn-follow-toggle__error\"\n\t\t\t\t\trole=\"alert\"\n\t\t\t\t\tdata-testid=\"cn-follow-toggle-error\">\n\t\t\t\t\t{{ popoverError }}\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t</NcPopover>\n\t</span>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcButton, NcLoadingIcon, NcPopover, NcSelect } from '@nextcloud/vue'\nimport Bell from 'vue-material-design-icons/Bell.vue'\nimport BellOffOutline from 'vue-material-design-icons/BellOffOutline.vue'\nimport Close from 'vue-material-design-icons/Close.vue'\nimport Eye from 'vue-material-design-icons/Eye.vue'\nimport EyeOutline from 'vue-material-design-icons/EyeOutline.vue'\nimport { patchStoredSelf } from '../../utils/patchStoredSelf.js'\nimport { listWatchers, setWatcher, setWatching } from '../../utils/recordInteractions.js'\nimport { searchNextcloudUsers } from '../../utils/userAutocomplete.js'\n\n/**\n * CnFollowToggle — the one Follow control of a record: a Follow toggle bound to\n * `@self.watching`, a notifications switch bound to `@self.watchNotify`, the\n * follower count and a followers popover for users who may see them.\n *\n * Following and favourites are one feature (OpenRegister's\n * `merge-follow-and-favourites`): a favourite is a follow with notifications\n * off, so there is no separate star. While the user follows, a bell beside the\n * toggle turns the follow's notifications on or off (`PUT .../watch` with\n * `{\"notify\": bool}`). With `notifies` false (the register sends no change\n * notifications) the bell is not offered. `compact` renders the toggle as an\n * icon only, without the bell, count or popover, for a column in a list.\n *\n * A click flips the toggle at once and sends `PUT` or `DELETE .../watch`; on\n * failure it flips back and shows the server's message, and a 404 says the\n * user can no longer see the record and emits `not-found`. The count shows\n * only when `watcherCount` is given (OpenRegister sets `@self.watcherCount` for\n * a reader with `update` only); opening the popover then fetches\n * `GET .../watchers`, never before, and a 403 closes it quietly. With\n * `canManage` (`@self.can.manage`) the popover offers an \"Add a colleague\"\n * picker and a remove action on each follower; without it, only the user's own\n * row can be removed.\n *\n * Example:\n * ```vue\n * <CnFollowToggle register=\"pipelinq\" schema=\"ticket\" :object-id=\"id\"\n * :watching=\"self.watching\" :notify=\"self.watchNotify\"\n * :watcher-count=\"self.watcherCount\" :can-manage=\"self.can && self.can.manage\" />\n * ```\n */\nexport default {\n\tname: 'CnFollowToggle',\n\n\tcomponents: { Bell, BellOffOutline, Close, Eye, EyeOutline, NcAvatar, NcButton, NcLoadingIcon, NcPopover, NcSelect },\n\n\tprops: {\n\t\t/** Register slug of the record. */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Schema slug of the record. */\n\t\tschema: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Id of the record. */\n\t\tobjectId: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Whether the current user follows the record (`@self.watching`). */\n\t\twatching: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Whether the user's follow notifies them (`@self.watchNotify`). Null\n\t\t * (an older server, or not following) reads as on.\n\t\t */\n\t\tnotify: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Icon only, without the bell, count or popover: for a column in a list. */\n\t\tcompact: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Number of followers (`@self.watcherCount`). Null hides the count and the popover. */\n\t\twatcherCount: {\n\t\t\ttype: Number,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether the user may add and remove other followers (`@self.can.manage`). */\n\t\tcanManage: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether following sends change notifications. False changes the tooltip to say it does not. */\n\t\tnotifies: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/** User id of the current user. Defaults to Nextcloud's `OC.currentUser`. */\n\t\tcurrentUser: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['change', 'error', 'not-found'],\n\n\tdata() {\n\t\treturn {\n\t\t\tstate: this.watching,\n\t\t\tnotifyState: this.notify !== false,\n\t\t\tcount: this.watcherCount,\n\t\t\tbusy: false,\n\t\t\topen: false,\n\t\t\twatchers: [],\n\t\t\tloadingWatchers: false,\n\t\t\tpopoverError: '',\n\t\t\tpickerOptions: [],\n\t\t\tpickerLoading: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tfollowLabel() {\n\t\t\treturn t('nextcloud-vue', 'Follow')\n\t\t},\n\n\t\tfollowingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Following')\n\t\t},\n\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading …')\n\t\t},\n\n\t\taddColleagueLabel() {\n\t\t\treturn t('nextcloud-vue', 'Add a colleague')\n\t\t},\n\n\t\tnotifyLabel() {\n\t\t\treturn this.notifyState\n\t\t\t\t? t('nextcloud-vue', 'Turn notifications off')\n\t\t\t\t: t('nextcloud-vue', 'Turn notifications on')\n\t\t},\n\n\t\t/**\n\t\t * Whether the bell renders: following, not compact, and the register notifies.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tshowNotifySwitch() {\n\t\t\treturn this.state && !this.compact && this.notifies\n\t\t},\n\n\t\tfollowersLabel() {\n\t\t\treturn t('nextcloud-vue', '{count} followers', { count: this.count })\n\t\t},\n\n\t\ttooltip() {\n\t\t\treturn this.notifies\n\t\t\t\t? t('nextcloud-vue', 'Follow this record to get its updates')\n\t\t\t\t: t('nextcloud-vue', 'You will see it under Following. This register sends no change notifications.')\n\t\t},\n\n\t\tme() {\n\t\t\treturn this.currentUser || (typeof OC !== 'undefined' && OC ? OC.currentUser : '') || ''\n\t\t},\n\t},\n\n\twatch: {\n\t\twatching(value) {\n\t\t\tthis.state = value\n\t\t},\n\n\t\tnotify(value) {\n\t\t\tthis.notifyState = value !== false\n\t\t},\n\n\t\twatcherCount(value) {\n\t\t\tthis.count = value\n\t\t},\n\n\t\topen(value) {\n\t\t\tif (value) {\n\t\t\t\tthis.loadWatchers()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tsinceText(created) {\n\t\t\tconst d = new Date(created)\n\t\t\treturn Number.isNaN(d.getTime())\n\t\t\t\t? ''\n\t\t\t\t: t('nextcloud-vue', 'Since {date}', { date: d.toLocaleDateString() })\n\t\t},\n\n\t\tremoveFollowerLabel(watcher) {\n\t\t\treturn t('nextcloud-vue', 'Remove {name} as a follower', { name: watcher.displayName || watcher.userId })\n\t\t},\n\n\t\tcanRemove(watcher) {\n\t\t\treturn this.canManage || (this.me !== '' && watcher.userId === this.me)\n\t\t},\n\n\t\tasync toggle() {\n\t\t\tif (this.busy) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst previous = this.state\n\t\t\tthis.state = !previous\n\t\t\tif (this.count !== null) {\n\t\t\t\tthis.count = Math.max(0, this.count + (this.state ? 1 : -1))\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\tconst result = await setWatching(this.register, this.schema, this.objectId, this.state)\n\t\t\tthis.busy = false\n\t\t\tif (!result.ok) {\n\t\t\t\tthis.state = previous\n\t\t\t\tif (this.count !== null) {\n\t\t\t\t\tthis.count = Math.max(0, this.count + (previous ? 1 : -1))\n\t\t\t\t}\n\t\t\t\tconst message = result.status === 404\n\t\t\t\t\t? t('nextcloud-vue', 'You can no longer see this record.')\n\t\t\t\t\t: (result.message || t('nextcloud-vue', 'Could not change following.'))\n\t\t\t\timport('@nextcloud/dialogs').then(({ showError }) => showError(message)).catch(() => {})\n\t\t\t\t/** @event error Emitted when a call failed; payload is the message shown. */\n\t\t\t\tthis.$emit('error', message)\n\t\t\t\tif (result.status === 404) {\n\t\t\t\t\t/** @event not-found Emitted on a 404: the record went away or access was withdrawn. */\n\t\t\t\t\tthis.$emit('not-found')\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// A new follow notifies (the server's default); an unfollow has no switch.\n\t\t\tthis.notifyState = this.state\n\t\t\tpatchStoredSelf(this.register, this.schema, this.objectId, {\n\t\t\t\twatching: this.state,\n\t\t\t\tfavourite: this.state,\n\t\t\t\twatchNotify: this.state ? true : undefined,\n\t\t\t\t...(this.count !== null ? { watcherCount: this.count } : {}),\n\t\t\t})\n\t\t\t/** @event change Emitted after a successful call. Payload: `{ watching, notify, count }`. */\n\t\t\tthis.$emit('change', { watching: this.state, notify: this.state ? this.notifyState : null, count: this.count })\n\t\t},\n\n\t\t/**\n\t\t * Turn the follow's notifications on or off, optimistically.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/one-follow-control/specs/record-follow/spec.md#requirement-one-follow-control-with-a-notifications-switch\n\t\t */\n\t\tasync toggleNotify() {\n\t\t\tif (this.busy || !this.state) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst previous = this.notifyState\n\t\t\tthis.notifyState = !previous\n\t\t\tthis.busy = true\n\t\t\tconst result = await setWatching(this.register, this.schema, this.objectId, true, { notify: this.notifyState })\n\t\t\tthis.busy = false\n\t\t\tif (!result.ok) {\n\t\t\t\tthis.notifyState = previous\n\t\t\t\tconst message = result.status === 404\n\t\t\t\t\t? t('nextcloud-vue', 'You can no longer see this record.')\n\t\t\t\t\t: (result.message || t('nextcloud-vue', 'Could not change notifications.'))\n\t\t\t\timport('@nextcloud/dialogs').then(({ showError }) => showError(message)).catch(() => {})\n\t\t\t\tthis.$emit('error', message)\n\t\t\t\tif (result.status === 404) {\n\t\t\t\t\tthis.$emit('not-found')\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tpatchStoredSelf(this.register, this.schema, this.objectId, { watchNotify: this.notifyState })\n\t\t\tthis.$emit('change', { watching: true, notify: this.notifyState, count: this.count })\n\t\t},\n\n\t\tasync loadWatchers() {\n\t\t\tthis.popoverError = ''\n\t\t\tthis.loadingWatchers = true\n\t\t\tconst result = await listWatchers(this.register, this.schema, this.objectId)\n\t\t\tthis.loadingWatchers = false\n\t\t\tif (!result.ok) {\n\t\t\t\t// No right to see the list (403) or a failure: close without a toast.\n\t\t\t\tthis.open = false\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst data = result.data || {}\n\t\t\tthis.watchers = Array.isArray(data.results) ? data.results : []\n\t\t\tthis.count = Number.isFinite(data.total) ? data.total : this.watchers.length\n\t\t},\n\n\t\tasync onPickerSearch(query) {\n\t\t\tthis.pickerLoading = true\n\t\t\tconst found = await searchNextcloudUsers(query || '', { limit: 10 })\n\t\t\tconst already = new Set(this.watchers.map((w) => w.userId))\n\t\t\tthis.pickerOptions = found.filter((u) => !already.has(u.id)).map((u) => ({ ...u, label: u.label || u.displayName || u.id }))\n\t\t\tthis.pickerLoading = false\n\t\t},\n\n\t\tasync addWatcher(option) {\n\t\t\tif (!option || !option.id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.popoverError = ''\n\t\t\tconst result = await setWatcher(this.register, this.schema, this.objectId, option.id, true)\n\t\t\tif (!result.ok) {\n\t\t\t\tthis.popoverError = result.message || t('nextcloud-vue', 'Could not add this colleague.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tawait this.loadWatchers()\n\t\t},\n\n\t\tasync removeWatcher(watcher) {\n\t\t\tthis.popoverError = ''\n\t\t\tconst result = await setWatcher(this.register, this.schema, this.objectId, watcher.userId, false)\n\t\t\tif (!result.ok) {\n\t\t\t\tthis.popoverError = result.message || t('nextcloud-vue', 'Could not remove this follower.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (watcher.userId === this.me) {\n\t\t\t\tthis.state = false\n\t\t\t\tthis.$emit('change', { watching: false, count: this.count })\n\t\t\t}\n\t\t\tawait this.loadWatchers()\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-follow-toggle {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n}\n\n.cn-follow-toggle__on {\n\tcolor: var(--color-primary-element);\n}\n\n.cn-follow-toggle__popover {\n\tmin-width: 260px;\n\tpadding: 12px;\n}\n\n.cn-follow-toggle__list {\n\tlist-style: none;\n\tmargin: 0 0 8px;\n\tpadding: 0;\n}\n\n.cn-follow-toggle__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 4px 0;\n}\n\n.cn-follow-toggle__who {\n\tdisplay: flex;\n\tflex-direction: column;\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n.cn-follow-toggle__since {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: var(--font-size-small, 13px);\n}\n\n.cn-follow-toggle__error {\n\tmargin: 8px 0 0;\n\tcolor: var(--color-error-text);\n}\n</style>\n"],"names":["_normalizeClass","_createVNode","_createBlock","_createElementBlock","_Fragment","_createElementVNode","_openBlock","_renderList","_toDisplayString"],"mappings":"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"}
|
|
@@ -15,7 +15,7 @@ import __sfc_main$v from '../../dialogs/CnConfirmDialog.vue.js';
|
|
|
15
15
|
import __sfc_main$w from '../../dialogs/CnQuickEditDialog.vue.js';
|
|
16
16
|
import __sfc_main from '../CnBuildiqEditButton/CnBuildiqEditButton.vue.js';
|
|
17
17
|
import __sfc_main$2 from '../CnEmptyContent/CnEmptyContent.vue.js';
|
|
18
|
-
import __sfc_main$1 from '../
|
|
18
|
+
import __sfc_main$1 from '../CnFollowToggle/CnFollowToggle.vue.js';
|
|
19
19
|
import { useLook } from '../../composables/useLook.js';
|
|
20
20
|
import { copyKindsOf } from '../../composables/useObjectCopy.js';
|
|
21
21
|
import { createRefLabelResolver } from '../../composables/useRefLabels.js';
|
|
@@ -27,6 +27,7 @@ import { resolveRegisteredHandler, buildOnSuccessRoute } from '../../utils/actio
|
|
|
27
27
|
import { reportBindingProblems } from '../../utils/diagnostics.js';
|
|
28
28
|
import { fetchFilterCounts } from '../../utils/fetchFilterCounts.js';
|
|
29
29
|
import { buildExportUrl } from '../../utils/indexExportHelpers.js';
|
|
30
|
+
import { lensUnavailableText } from '../../utils/lensAvailability.js';
|
|
30
31
|
import { openRowTarget } from '../../utils/linkNavigation.js';
|
|
31
32
|
import { resolveClaimedTeams, splitViewsIntoTabs, viewAsTab, resolveClaimTokens, viewIdOf } from '../../utils/listLenses.js';
|
|
32
33
|
import { LIST_SHORTCUTS, listPaletteCommands, shortcutFor } from '../../utils/listShortcuts.js';
|
|
@@ -100,8 +101,8 @@ const CALENDAR_PAGE_SIZE = 200;
|
|
|
100
101
|
*/
|
|
101
102
|
const RANGE_SEPARATOR = '..';
|
|
102
103
|
|
|
103
|
-
/** Key of the synthetic
|
|
104
|
-
const
|
|
104
|
+
/** Key of the synthetic follow column (`showFollowColumn`). */
|
|
105
|
+
const FOLLOW_COLUMN_KEY = '__follow';
|
|
105
106
|
|
|
106
107
|
/**
|
|
107
108
|
* Whether a schema property wants a from/to pair rather than a value list.
|
|
@@ -303,7 +304,7 @@ var script = {
|
|
|
303
304
|
|
|
304
305
|
components: {
|
|
305
306
|
CnBuildiqEditButton: __sfc_main,
|
|
306
|
-
|
|
307
|
+
CnFollowToggle: __sfc_main$1,
|
|
307
308
|
NcLoadingIcon,
|
|
308
309
|
CnEmptyContent: __sfc_main$2,
|
|
309
310
|
NcActions,
|
|
@@ -639,9 +640,10 @@ var script = {
|
|
|
639
640
|
},
|
|
640
641
|
|
|
641
642
|
/**
|
|
642
|
-
* Personal lenses appended to the quick filters: any of `
|
|
643
|
-
* (`
|
|
644
|
-
*
|
|
643
|
+
* Personal lenses appended to the quick filters: any of `watching`
|
|
644
|
+
* (`_watching`, labelled Following), `recent` (`_recent`) and `unread`
|
|
645
|
+
* (`_unread`). `favourite` is a deprecated alias of `watching`: a
|
|
646
|
+
* favourite is a follow now, and asking for both gives one tab.
|
|
645
647
|
* They combine with every other filter. While Recent is active column
|
|
646
648
|
* sorting is off, because the lens owns the order. Manifest
|
|
647
649
|
* `config.personalLenses`.
|
|
@@ -654,9 +656,20 @@ var script = {
|
|
|
654
656
|
},
|
|
655
657
|
|
|
656
658
|
/**
|
|
657
|
-
* Add a first column with a
|
|
658
|
-
* each row's `@self.
|
|
659
|
-
* Manifest `config.
|
|
659
|
+
* Add a first column with a follow toggle per row (`CnFollowToggle`,
|
|
660
|
+
* compact), bound to each row's `@self.watching`. Clicking it does not
|
|
661
|
+
* open the row. Manifest `config.showFollowColumn`.
|
|
662
|
+
*/
|
|
663
|
+
showFollowColumn: {
|
|
664
|
+
type: Boolean,
|
|
665
|
+
default: false,
|
|
666
|
+
},
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Deprecated alias of `showFollowColumn`: the star column became the
|
|
670
|
+
* follow column (OpenRegister `merge-follow-and-favourites`).
|
|
671
|
+
*
|
|
672
|
+
* @deprecated Use `showFollowColumn`.
|
|
660
673
|
*/
|
|
661
674
|
showFavouriteColumn: {
|
|
662
675
|
type: Boolean,
|
|
@@ -1151,6 +1164,34 @@ var script = {
|
|
|
1151
1164
|
default: 'No items found',
|
|
1152
1165
|
},
|
|
1153
1166
|
|
|
1167
|
+
/**
|
|
1168
|
+
* Personal-lens report of the list on screen, for a host that fetches
|
|
1169
|
+
* itself: the response's `@self.lenses` (`{ recent: { available,
|
|
1170
|
+
* reason } }`). In self-fetch mode the page reads it from the store
|
|
1171
|
+
* and this prop is ignored. When a lens reports `available: false`
|
|
1172
|
+
* the empty state explains why instead of showing `emptyText`.
|
|
1173
|
+
*
|
|
1174
|
+
* @type {object|null}
|
|
1175
|
+
* @spec openspec/changes/lens-says-why-it-is-empty/specs/personal-lens-availability/spec.md#requirement-an-unavailable-lens-explains-its-empty-page
|
|
1176
|
+
*/
|
|
1177
|
+
lenses: {
|
|
1178
|
+
type: Object,
|
|
1179
|
+
default: null,
|
|
1180
|
+
},
|
|
1181
|
+
|
|
1182
|
+
/**
|
|
1183
|
+
* App wording for an unavailable lens, keyed `<lens>.<reason>`
|
|
1184
|
+
* (`recent.audit-trail-disabled`) or `<reason>`; the specific key
|
|
1185
|
+
* wins, and both win over the library's object-neutral text.
|
|
1186
|
+
*
|
|
1187
|
+
* @type {{[key: string]: string}|null}
|
|
1188
|
+
* @spec openspec/changes/lens-says-why-it-is-empty/specs/personal-lens-availability/spec.md#requirement-an-unavailable-lens-explains-its-empty-page
|
|
1189
|
+
*/
|
|
1190
|
+
lensReasonTexts: {
|
|
1191
|
+
type: Object,
|
|
1192
|
+
default: null,
|
|
1193
|
+
},
|
|
1194
|
+
|
|
1154
1195
|
/** Accessible label for the loading spinner (NcLoadingIcon aria-label) */
|
|
1155
1196
|
loadingText: {
|
|
1156
1197
|
type: String,
|
|
@@ -2342,6 +2383,40 @@ var script = {
|
|
|
2342
2383
|
return this.emptyText ? fn(this.emptyText) : this.emptyText
|
|
2343
2384
|
},
|
|
2344
2385
|
|
|
2386
|
+
/**
|
|
2387
|
+
* The personal-lens reports of the list on screen: the store's copy
|
|
2388
|
+
* of the latest response in self-fetch mode, else the `lenses` prop.
|
|
2389
|
+
*
|
|
2390
|
+
* @return {object}
|
|
2391
|
+
* @spec openspec/changes/lens-says-why-it-is-empty/specs/personal-lens-availability/spec.md#requirement-a-list-response-carries-the-report-of-the-lenses-it-was-asked-for
|
|
2392
|
+
*/
|
|
2393
|
+
effectiveLenses() {
|
|
2394
|
+
if (this.isNamedSource) {
|
|
2395
|
+
return {}
|
|
2396
|
+
}
|
|
2397
|
+
if (this.isSelfFetchMode) {
|
|
2398
|
+
return (this.list.lenses && this.list.lenses.value) || {}
|
|
2399
|
+
}
|
|
2400
|
+
return this.lenses || {}
|
|
2401
|
+
},
|
|
2402
|
+
|
|
2403
|
+
/**
|
|
2404
|
+
* Why the list is empty when a personal lens could not answer (the
|
|
2405
|
+
* audit trail is off, nobody is logged in, the history could not be
|
|
2406
|
+
* read), or `''` to keep the generic empty text.
|
|
2407
|
+
*
|
|
2408
|
+
* @return {string}
|
|
2409
|
+
* @spec openspec/changes/lens-says-why-it-is-empty/specs/personal-lens-availability/spec.md#requirement-an-unavailable-lens-explains-its-empty-page
|
|
2410
|
+
*/
|
|
2411
|
+
lensReasonText() {
|
|
2412
|
+
const text = lensUnavailableText(this.effectiveLenses, this.lensReasonTexts);
|
|
2413
|
+
if (!text) {
|
|
2414
|
+
return ''
|
|
2415
|
+
}
|
|
2416
|
+
const fn = typeof this.cnTranslate === 'function' ? this.cnTranslate : (k) => k;
|
|
2417
|
+
return fn(text)
|
|
2418
|
+
},
|
|
2419
|
+
|
|
2345
2420
|
/**
|
|
2346
2421
|
* Whether the host manifest editor is in edit mode (unwraps the injected
|
|
2347
2422
|
* `cnEditingBody`, which may be a Vue ref or a plain boolean). Drives the
|
|
@@ -3235,12 +3310,22 @@ var script = {
|
|
|
3235
3310
|
if (this.recentLensActive) {
|
|
3236
3311
|
cols = cols.map((col) => (col && typeof col === 'object' ? { ...col, sortable: false } : col));
|
|
3237
3312
|
}
|
|
3238
|
-
if (this.
|
|
3239
|
-
cols = [{ key:
|
|
3313
|
+
if (this.followColumnOn && this.favouriteSchemaSlug !== '') {
|
|
3314
|
+
cols = [{ key: FOLLOW_COLUMN_KEY, label: '', sortable: false, width: '48px' }, ...cols];
|
|
3240
3315
|
}
|
|
3241
3316
|
return cols
|
|
3242
3317
|
},
|
|
3243
3318
|
|
|
3319
|
+
/**
|
|
3320
|
+
* Whether the follow column renders (`showFollowColumn`, or the deprecated `showFavouriteColumn`).
|
|
3321
|
+
*
|
|
3322
|
+
* @return {boolean}
|
|
3323
|
+
* @spec openspec/changes/one-follow-control/specs/record-follow/spec.md#requirement-index-pages-offer-one-following-lens-and-a-follow-column
|
|
3324
|
+
*/
|
|
3325
|
+
followColumnOn() {
|
|
3326
|
+
return this.showFollowColumn || this.showFavouriteColumn
|
|
3327
|
+
},
|
|
3328
|
+
|
|
3244
3329
|
/**
|
|
3245
3330
|
* Whether the active quick filter is the Recent lens.
|
|
3246
3331
|
*
|
|
@@ -3255,7 +3340,7 @@ var script = {
|
|
|
3255
3340
|
},
|
|
3256
3341
|
|
|
3257
3342
|
/**
|
|
3258
|
-
* Schema slug the
|
|
3343
|
+
* Schema slug the follow column calls with: the page's own slug in self-fetch
|
|
3259
3344
|
* mode, else the schema object's `slug`. Empty when neither is known.
|
|
3260
3345
|
*
|
|
3261
3346
|
* @return {string}
|