@conduction/nextcloud-vue 2.56.0-beta.3 → 2.56.0-beta.5
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/CnActionsBar/CnActionsBar.vue2.js +12 -2
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +1 -2
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js +0 -3
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +1 -2
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +21 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +12 -5
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +2 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +9 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js +6 -1
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js.map +1 -1
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +116 -15
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +27 -14
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js +13 -6
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js.map +1 -1
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js +2 -0
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +29 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js +1 -2
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +3 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +17 -17
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js +4 -21
- package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js +0 -13
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js +8 -8
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js +3 -2
- package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +67 -13
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +15 -8
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/manifestActionDispatch.js +31 -11
- package/dist/esm/components/CnIndexPage/manifestActionDispatch.js.map +1 -1
- package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue2.js.map +1 -1
- package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js +1 -2
- package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js +0 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js +2 -17
- package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js +35 -11
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js +3 -2
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +23 -5
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectList/CnObjectList.vue2.js +19 -3
- package/dist/esm/components/CnObjectList/CnObjectList.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectList/CnObjectList.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +45 -15
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js +24 -6
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +0 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js +4 -2
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnRowActions/CnRowActions.vue2.js +7 -2
- package/dist/esm/components/CnRowActions/CnRowActions.vue2.js.map +1 -1
- package/dist/esm/components/CnRowActions/CnRowActions.vue3.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +49 -5
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
- package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js +5 -3
- package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +3 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +1 -0
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js +6 -0
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js +68 -53
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js +17 -0
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js +21 -19
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/shares/CnSharesTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js +3 -6
- package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js +4 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js +6 -3
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js.map +1 -1
- package/dist/esm/l10n/en.json.js +2 -0
- package/dist/esm/l10n/en.json.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/nextcloud-vue.css +78 -7
- package/dist/esm/utils/actionLink.js +5 -0
- package/dist/esm/utils/actionLink.js.map +1 -1
- package/dist/esm/utils/actionsDispatcher.js +16 -1
- package/dist/esm/utils/actionsDispatcher.js.map +1 -1
- package/dist/esm/utils/linkNavigation.js +6 -3
- package/dist/esm/utils/linkNavigation.js.map +1 -1
- package/dist/esm/utils/rowAuxClick.js +30 -5
- package/dist/esm/utils/rowAuxClick.js.map +1 -1
- package/dist/esm/utils/safeHref.js +18 -3
- package/dist/esm/utils/safeHref.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/nextcloud-vue.cjs.js +885 -393
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +78 -7
- package/l10n/en.json +2 -0
- package/l10n/nl.json +2 -0
- package/package.json +1 -1
- package/src/components/CnActionsBar/CnActionsBar.vue +14 -5
- package/src/components/CnActionsMenu/CnActionsMenu.vue +0 -3
- package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -2
- package/src/components/CnAppRoot/CnAppRoot.vue +31 -2
- package/src/components/CnBoardView/CnBoardView.vue +13 -5
- package/src/components/CnCardGrid/CnCardGrid.vue +9 -1
- package/src/components/CnContextMenu/CnContextMenu.vue +6 -1
- package/src/components/CnDataTable/CnDataTable.md +1 -1
- package/src/components/CnDataTable/CnDataTable.vue +131 -17
- package/src/components/CnDateAxisView/CnDateAxisView.vue +15 -6
- package/src/components/CnDetailPage/CnDetailPage.md +1 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +29 -1
- package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +1 -2
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +5 -3
- package/src/components/CnFilesWidget/CnFilesWidget.vue +4 -21
- package/src/components/CnFlowRunsWidget/CnFlowRunsWidget.vue +8 -21
- package/src/components/CnFlowsPage/CnFlowsPage.vue +2 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +75 -14
- package/src/components/CnIndexPage/manifestActionDispatch.js +31 -11
- package/src/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue +1 -2
- package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +0 -1
- package/src/components/CnLinkButtonWidget/CnLinkButtonWidget.vue +2 -17
- package/src/components/CnLogsPage/CnLogsPage.vue +37 -12
- package/src/components/CnObjectCard/CnObjectCard.vue +24 -6
- package/src/components/CnObjectList/CnObjectList.vue +19 -3
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +47 -15
- package/src/components/CnObjectRow/CnObjectRow.vue +25 -7
- package/src/components/CnObjectSidebar/CnFilesTab.vue +0 -1
- package/src/components/CnPageRenderer/CnPageRenderer.vue +2 -0
- package/src/components/CnRowActions/CnRowActions.vue +7 -2
- package/src/components/CnSetupWizard/CnSetupWizard.vue +57 -6
- package/src/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue +5 -3
- package/src/components/CnTabs/CnTabs.vue +7 -3
- package/src/components/CnTasksWidget/CnTasksWidget.vue +4 -1
- package/src/css/table.css +26 -0
- package/src/integrations/builtin/analytics/CnAnalyticsCard.vue +3 -2
- package/src/integrations/builtin/analytics/CnAnalyticsTab.vue +5 -6
- package/src/integrations/builtin/bookmarks/CnBookmarksCard.vue +12 -1
- package/src/integrations/builtin/bookmarks/CnBookmarksTab.vue +23 -6
- package/src/integrations/builtin/bookmarks/__tests__/CnBookmarksTab.spec.js +7 -2
- package/src/integrations/builtin/collectives/CnCollectivesCard.vue +3 -2
- package/src/integrations/builtin/collectives/CnCollectivesTab.vue +5 -6
- package/src/integrations/builtin/cospend/CnCospendCard.vue +2 -2
- package/src/integrations/builtin/cospend/CnCospendTab.vue +3 -4
- package/src/integrations/builtin/deck/CnDeckCard.vue +2 -2
- package/src/integrations/builtin/deck/CnDeckTab.vue +3 -4
- package/src/integrations/builtin/email/CnEmailCard.vue +6 -2
- package/src/integrations/builtin/email/CnEmailTab.vue +7 -4
- package/src/integrations/builtin/flow/CnFlowCard.vue +2 -2
- package/src/integrations/builtin/flow/CnFlowTab.vue +3 -4
- package/src/integrations/builtin/forms/CnFormsCard.vue +3 -2
- package/src/integrations/builtin/forms/CnFormsTab.vue +5 -6
- package/src/integrations/builtin/maps/CnMapsCard.vue +3 -2
- package/src/integrations/builtin/maps/CnMapsTab.vue +5 -6
- package/src/integrations/builtin/maps/__tests__/CnMapsTab.spec.js +9 -0
- package/src/integrations/builtin/openproject/CnOpenprojectCard.vue +3 -2
- package/src/integrations/builtin/openproject/CnOpenprojectTab.vue +5 -6
- package/src/integrations/builtin/photos/CnPhotosCard.vue +2 -2
- package/src/integrations/builtin/photos/CnPhotosTab.vue +3 -4
- package/src/integrations/builtin/polls/CnPollsCard.vue +2 -2
- package/src/integrations/builtin/polls/CnPollsTab.vue +3 -4
- package/src/integrations/builtin/shares/CnSharesTab.vue +3 -6
- package/src/integrations/builtin/talk/CnTalkCard.vue +3 -2
- package/src/integrations/builtin/talk/CnTalkTab.vue +5 -6
- package/src/integrations/builtin/time-tracker/CnTimeTrackerCard.vue +2 -2
- package/src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue +3 -4
- package/src/integrations/builtin/xwiki/CnXwikiCard.vue +4 -2
- package/src/integrations/builtin/xwiki/CnXwikiTab.vue +7 -7
- package/src/integrations/builtin/xwiki/__tests__/CnXwikiTab.spec.js +9 -0
- package/src/schemas/app-manifest-v2.schema.json +2 -1
- package/src/utils/actionLink.js +5 -0
- package/src/utils/actionsDispatcher.js +15 -0
- package/src/utils/linkNavigation.js +6 -3
- package/src/utils/rowAuxClick.js +29 -4
- package/src/utils/safeHref.js +18 -3
- package/src/utils/validateManifestV2.compiled.js +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnSharesTab.vue2.js","sources":["../../../../../src/integrations/builtin/shares/CnSharesTab.vue"],"sourcesContent":["<!--\n CnSharesTab — bespoke sidebar tab for the `shares` integration.\n\n Replaces the generic CnIntegrationTab for the `shares` leaf: renders\n a recipient list grouped by share type (user / group / public link\n / federated), with per-row permissions, expiry date, a\n password-protected indicator, and a \"manage in Files\" deep-link.\n Talks to the same OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/shares`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\SharesProvider`\n (which walks linked files and calls `OCP\\Share\\IManager::getSharesBy()`\n per file).\n\n Surface behaviour:\n - Empty state with \"Open Files\" CTA when no shares exist.\n - Loading + 503 \"currently unavailable\" + generic error states match\n CnIntegrationTab's behaviour for AD-23 graceful degradation.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link list\n which loses Shares' four primary signals — recipient, permissions,\n expiry, password-protection — that case handlers need to triage \"who\n has access to this case and how?\" at a glance.\n\n See `openregister/openspec/changes/integration-shares/` for the spec\n delta and ADR-019 (registry mechanism).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-shares-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-shares-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div class=\"cn-shares-tab__toolbar\">\n\t\t\t<NcButton variant=\"primary\" data-testid=\"cn-shares-tab-share-file\" @click=\"openCreateDialog\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ shareFileLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-shares-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"shares.length === 0\" class=\"cn-sidebar-tab__empty cn-shares-tab__empty\">\n\t\t\t<Share :size=\"32\" class=\"cn-shares-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"filesAppUrl\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener noreferrer\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<FolderOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openFilesLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<div v-else class=\"cn-shares-tab__groups\">\n\t\t\t<section\n\t\t\t\tv-for=\"group in groupedShares\"\n\t\t\t\t:key=\"group.key\"\n\t\t\t\tclass=\"cn-shares-tab__group\">\n\t\t\t\t<header class=\"cn-shares-tab__group-header\">\n\t\t\t\t\t<component :is=\"group.icon\" :size=\"18\" />\n\t\t\t\t\t<span class=\"cn-shares-tab__group-label\">{{ group.label }}</span>\n\t\t\t\t\t<span class=\"cn-shares-tab__group-count\">{{ group.rows.length }}</span>\n\t\t\t\t</header>\n\t\t\t\t<ul class=\"cn-shares-tab__list\">\n\t\t\t\t\t<NcListItem\n\t\t\t\t\t\tv-for=\"share in group.rows\"\n\t\t\t\t\t\t:key=\"shareKey(share)\"\n\t\t\t\t\t\tclass=\"cn-shares-tab__row\"\n\t\t\t\t\t\t:name=\"shareTarget(share)\"\n\t\t\t\t\t\t:bold=\"true\"\n\t\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<span class=\"cn-shares-tab__avatar\" :aria-hidden=\"true\">\n\t\t\t\t\t\t\t\t<component :is=\"iconFor(shareType(share))\" :size=\"20\" />\n\t\t\t\t\t\t\t\t<LockOutline\n\t\t\t\t\t\t\t\t\tv-if=\"isPasswordProtected(share)\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-shares-tab__avatar-lock\"\n\t\t\t\t\t\t\t\t\t:size=\"12\"\n\t\t\t\t\t\t\t\t\t:title=\"passwordProtectedLabel\"\n\t\t\t\t\t\t\t\t\t:aria-label=\"passwordProtectedLabel\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t\t<span class=\"cn-shares-tab__permissions\">\n\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\tv-for=\"badge in permissionBadges(share)\"\n\t\t\t\t\t\t\t\t\t:key=\"badge.key\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-shares-tab__badge\"\n\t\t\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t\t\t:variant=\"badge.variant\"\n\t\t\t\t\t\t\t\t\t:label=\"badge.label\" />\n\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\tv-if=\"isPasswordProtected(share)\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-shares-tab__badge\"\n\t\t\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t\t\tvariant=\"warning\"\n\t\t\t\t\t\t\t\t\t:label=\"passwordProtectedLabel\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"shareExpiryMs(share)\" #details>\n\t\t\t\t\t\t\t<span class=\"cn-shares-tab__expiry\" :title=\"expiryLabel\">\n\t\t\t\t\t\t\t\t<ClockOutline :size=\"13\" />\n\t\t\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\t\t\t:timestamp=\"shareExpiryMs(share)\"\n\t\t\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template #actions>\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-if=\"canRevoke(share)\"\n\t\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t\t@click=\"revoke(share)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<CloseCircleOutline :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ revokeLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<NcActionLink\n\t\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t\t:href=\"filesAppUrl\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener noreferrer\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<FolderOutline :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ openFilesLabel }}\n\t\t\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcListItem>\n\t\t\t\t</ul>\n\t\t\t</section>\n\t\t\t<div class=\"cn-shares-tab__footer\">\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t:href=\"filesAppUrl\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener noreferrer\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<FolderOutline :size=\"18\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ openFilesLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<CnShareCreate\n\t\t\tv-if=\"showCreate\"\n\t\t\t:files=\"shareableFiles\"\n\t\t\t:filesLoading=\"filesLoading\"\n\t\t\t:principals=\"principals\"\n\t\t\t:principalsLoading=\"principalsLoading\"\n\t\t\t@close=\"showCreate = false\"\n\t\t\t@searchPrincipals=\"searchPrincipals\"\n\t\t\t@create=\"createShare\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcActionLink, NcButton, NcDateTime, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AccountGroupOutline from 'vue-material-design-icons/AccountGroupOutline.vue'\nimport AccountOutline from 'vue-material-design-icons/AccountOutline.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport CloseCircleOutline from 'vue-material-design-icons/CloseCircleOutline.vue'\nimport Earth from 'vue-material-design-icons/Earth.vue'\nimport EmailOutline from 'vue-material-design-icons/EmailOutline.vue'\nimport FolderOutline from 'vue-material-design-icons/FolderOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport LockOutline from 'vue-material-design-icons/LockOutline.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport Share from 'vue-material-design-icons/Share.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { CnShareCreate } from '../../../components/CnShareCreate/index.js'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\n\n// NC core share-type constants (OCP\\Share\\IShare::TYPE_*).\nconst SHARE_TYPE_USER = 0\nconst SHARE_TYPE_GROUP = 1\nconst SHARE_TYPE_LINK = 3\nconst SHARE_TYPE_EMAIL = 4\nconst SHARE_TYPE_REMOTE = 6\nconst SHARE_TYPE_REMOTE_GROUP = 9\n\n// Permission bitmask (OCP\\Constants::PERMISSION_*).\nconst PERMISSION_READ = 1\nconst PERMISSION_UPDATE = 2\nconst PERMISSION_CREATE = 4\nconst PERMISSION_DELETE = 8\nconst PERMISSION_SHARE = 16\n\n/**\n * CnSharesTab — bespoke recipient list for the `shares` integration.\n *\n * Renders shares grouped by type with permissions, expiry, password-\n * protected flag, and a revoke action. Reads from the OR\n * pluggable-integration endpoint and delegates revoke back to the same\n * endpoint via DELETE.\n */\nexport default {\n\tname: 'CnSharesTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tAccountOutline,\n\t\tAccountGroupOutline,\n\t\tLinkVariant,\n\t\tEmailOutline,\n\t\tEarth,\n\t\tLockOutline,\n\t\tClockOutline,\n\t\tCloseCircleOutline,\n\t\tFolderOutline,\n\t\tShare,\n\t\tPlus,\n\t\tCnStatusBadge,\n\t\tCnShareCreate,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'shares'`). */\n\t\tintegrationId: { type: String, default: 'shares' },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No shares on this object yet') },\n\t\t/** Pre-translated label for the \"Open Files\" CTA. */\n\t\topenFilesLabel: { type: String, default: () => t('nextcloud-vue', 'Manage in Files') },\n\t\t/** Pre-translated banner when the share subsystem is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC sharing is currently unavailable.') },\n\t\t/** Pre-translated revoke button label. */\n\t\trevokeLabel: { type: String, default: () => t('nextcloud-vue', 'Revoke share') },\n\t\t/* eslint-disable vue/no-unused-properties -- the row hides the revoke button when canRevoke is false rather than disabling it, so the tooltip has no anchor today */\n\t\t/** Pre-translated tooltip shown when the current user cannot revoke. */\n\t\trevokeDisabledLabel: { type: String, default: () => t('nextcloud-vue', 'Only the share owner can revoke') },\n\t\t/* eslint-enable vue/no-unused-properties */\n\t\t/** Pre-translated label for the password-protected indicator. */\n\t\tpasswordProtectedLabel: { type: String, default: () => t('nextcloud-vue', 'Password protected') },\n\t\t/** Pre-translated label for the expiry row. */\n\t\texpiryLabel: { type: String, default: () => t('nextcloud-vue', 'Expires') },\n\t\t/** URL of the NC Files app entry. */\n\t\tfilesAppUrl: { type: String, default: '/index.php/apps/files' },\n\t\t/** Pre-translated label for the \"Share file\" toolbar button. */\n\t\tshareFileLabel: { type: String, default: () => t('nextcloud-vue', 'Share file') },\n\t},\n\n\temits: ['search-principals', 'share-created', 'share-revoked'],\n\n\tdata() {\n\t\treturn {\n\t\t\tshares: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tshowCreate: false,\n\t\t\tshareableFiles: [],\n\t\t\tfilesLoading: false,\n\t\t\tprincipals: [],\n\t\t\tprincipalsLoading: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tgroupedShares() {\n\t\t\tconst groups = [\n\t\t\t\t{ key: 'user', label: t('nextcloud-vue', 'Users'), icon: 'AccountOutline', match: (s) => this.shareType(s) === SHARE_TYPE_USER },\n\t\t\t\t{ key: 'group', label: t('nextcloud-vue', 'Groups'), icon: 'AccountGroupOutline', match: (s) => this.shareType(s) === SHARE_TYPE_GROUP },\n\t\t\t\t{ key: 'link', label: t('nextcloud-vue', 'Public links'), icon: 'LinkVariant', match: (s) => this.shareType(s) === SHARE_TYPE_LINK },\n\t\t\t\t{ key: 'email', label: t('nextcloud-vue', 'Email'), icon: 'EmailOutline', match: (s) => this.shareType(s) === SHARE_TYPE_EMAIL },\n\t\t\t\t{ key: 'federated', label: t('nextcloud-vue', 'Federated'), icon: 'Earth', match: (s) => this.shareType(s) === SHARE_TYPE_REMOTE || this.shareType(s) === SHARE_TYPE_REMOTE_GROUP },\n\t\t\t]\n\t\t\treturn groups\n\t\t\t\t.map((g) => ({ ...g, rows: this.shares.filter(g.match) }))\n\t\t\t\t.filter((g) => g.rows.length > 0)\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchShares()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchShares()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchShares()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\t// Tier-2 dedicated endpoints (ShareLinksController) — distinct\n\t\t// from the generic registry `baseUrl()` above. Create/revoke flow\n\t\t// through these so the IManager-backed service owns the write\n\t\t// path (NO cache table).\n\t\tsharesUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/shares`\n\t\t},\n\n\t\tshareableFilesUrl() {\n\t\t\treturn `${this.apiBase}/integrations/shares/files/${this.register}/${this.schema}/${this.objectId}`\n\t\t},\n\n\t\tasync openCreateDialog() {\n\t\t\tthis.showCreate = true\n\t\t\tawait this.fetchShareableFiles()\n\t\t},\n\n\t\tasync fetchShareableFiles() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.filesLoading = true\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.shareableFilesUrl()), { 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.shareableFiles = data.results || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else {\n\t\t\t\t\tthis.shareableFiles = []\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnSharesTab] failed to fetch shareable files', err)\n\t\t\t\tthis.shareableFiles = []\n\t\t\t} finally {\n\t\t\t\tthis.filesLoading = false\n\t\t\t}\n\t\t},\n\n\t\tsearchPrincipals({ shareType, query }) {\n\t\t\t// Principal lookup is host-supplied: the host app wires NC's\n\t\t\t// share-search OCS endpoint. Emit upward so the host can\n\t\t\t// populate `principals`; default is a no-op empty list.\n\t\t\tthis.$emit('search-principals', { shareType, query })\n\t\t},\n\n\t\tasync createShare(payload) {\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.sharesUrl()), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.showCreate = false\n\t\t\t\t\tthis.$emit('share-created', payload)\n\t\t\t\t\tawait this.fetchShares()\n\t\t\t\t} else {\n\t\t\t\t\tconst data = await response.json().catch(() => ({}))\n\t\t\t\t\tthis.error = data.error || t('nextcloud-vue', 'Could not create share.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnSharesTab] failed to create share', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create share.')\n\t\t\t}\n\t\t},\n\n\t\tshareKey(share) {\n\t\t\treturn share.id ?? share.shareId ?? share.token ?? ''\n\t\t},\n\n\t\tshareType(share) {\n\t\t\tconst value = share.shareType ?? share.share_type ?? share.type\n\t\t\treturn Number.isFinite(Number(value)) ? Number(value) : -1\n\t\t},\n\n\t\tshareTarget(share) {\n\t\t\tconst type = this.shareType(share)\n\t\t\tif (type === SHARE_TYPE_LINK) {\n\t\t\t\t// Public links surface as a generic \"Share link\" label,\n\t\t\t\t// mirroring NC core's share list (the raw token is never\n\t\t\t\t// shown as a recipient name).\n\t\t\t\treturn share.shareWithDisplayname || share.label || t('nextcloud-vue', 'Share link')\n\t\t\t}\n\t\t\treturn share.shareWithDisplayname || share.shareWith || share.share_with || share.targetDisplayName || ''\n\t\t},\n\n\t\t// Picks the recipient/share-type icon component for a row,\n\t\t// mirroring NC core's share list (user / group / link / email /\n\t\t// federated).\n\t\ticonFor(type) {\n\t\t\tif (type === SHARE_TYPE_GROUP) {\n\t\t\t\treturn 'AccountGroupOutline'\n\t\t\t}\n\t\t\tif (type === SHARE_TYPE_LINK) {\n\t\t\t\treturn 'LinkVariant'\n\t\t\t}\n\t\t\tif (type === SHARE_TYPE_EMAIL) {\n\t\t\t\treturn 'EmailOutline'\n\t\t\t}\n\t\t\tif (type === SHARE_TYPE_REMOTE || type === SHARE_TYPE_REMOTE_GROUP) {\n\t\t\t\treturn 'Earth'\n\t\t\t}\n\t\t\treturn 'AccountOutline'\n\t\t},\n\n\t\tpermissionLabel(share) {\n\t\t\treturn this.permissionBadges(share).map((b) => b.label).join(' · ')\n\t\t},\n\n\t\t// Decomposes the NC permission bitmask into the badge set the real\n\t\t// share UI shows: \"Can edit\" / \"Read only\" (mutually exclusive),\n\t\t// plus \"Can reshare\" when the share permission bit is set.\n\t\tpermissionBadges(share) {\n\t\t\tconst perms = Number(share.permissions ?? share.permission ?? 0)\n\t\t\tconst canEdit = (perms & PERMISSION_UPDATE) === PERMISSION_UPDATE\n\t\t\t\t|| (perms & PERMISSION_CREATE) === PERMISSION_CREATE\n\t\t\t\t|| (perms & PERMISSION_DELETE) === PERMISSION_DELETE\n\t\t\tconst canRead = (perms & PERMISSION_READ) === PERMISSION_READ\n\t\t\tconst badges = []\n\t\t\tif (canEdit) {\n\t\t\t\tbadges.push({ key: 'edit', variant: 'success', label: t('nextcloud-vue', 'Can edit') })\n\t\t\t} else if (canRead) {\n\t\t\t\tbadges.push({ key: 'read', variant: 'default', label: t('nextcloud-vue', 'Read only') })\n\t\t\t}\n\t\t\tif ((perms & PERMISSION_SHARE) === PERMISSION_SHARE) {\n\t\t\t\tbadges.push({ key: 'reshare', variant: 'info', label: t('nextcloud-vue', 'Can reshare') })\n\t\t\t}\n\t\t\treturn badges\n\t\t},\n\n\t\t// Returns the expiry as epoch-ms for <NcDateTime>, or 0 when there\n\t\t// is no (valid) expiry so the template can `v-if` it away.\n\t\tshareExpiryMs(share) {\n\t\t\tconst value = share.expiration ?? share.expirationDate ?? share.expiry ?? null\n\t\t\tif (!value) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\tconst date = new Date(value)\n\t\t\tconst ms = date.getTime()\n\t\t\treturn Number.isNaN(ms) ? 0 : ms\n\t\t},\n\n\t\tisPasswordProtected(share) {\n\t\t\treturn Boolean(share.passwordProtected ?? share.hasPassword ?? share.password)\n\t\t},\n\n\t\tcanRevoke(share) {\n\t\t\t// The current-user-can-revoke flag is provider-supplied. When\n\t\t\t// absent we default to true so the action stays usable; the\n\t\t\t// backend rejects unauthorized revoke attempts.\n\t\t\treturn share.canRevoke !== false\n\t\t},\n\n\t\tasync fetchShares() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.shares = rows\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.shares = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.shares = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load shares.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnSharesTab] failed to fetch shares', err)\n\t\t\t\tthis.shares = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load shares.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync revoke(share) {\n\t\t\tconst id = this.shareKey(share)\n\t\t\tif (!id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(id)}`), {\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.shares = this.shares.filter((row) => this.shareKey(row) !== id)\n\t\t\t\t\tthis.$emit('share-revoked', { id, share })\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not revoke share.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnSharesTab] failed to revoke share', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not revoke share.')\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-shares-tab__banner {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 10px;\n\tmargin-bottom: 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-shares-tab__toolbar {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\tmargin-bottom: 10px;\n}\n\n.cn-shares-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-shares-tab__empty {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 16px 8px;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n}\n\n.cn-shares-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-shares-tab__group {\n\tmargin-bottom: 12px;\n}\n\n.cn-shares-tab__group-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 0;\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-shares-tab__group-label {\n\tflex: 1;\n}\n\n.cn-shares-tab__group-count {\n\tfont-weight: normal;\n\tfont-size: 0.85em;\n\tpadding: 1px 6px;\n\tborder-radius: 9px;\n\tbackground: var(--color-background-hover);\n}\n\n.cn-shares-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* Share-type icon tile, mirroring the NcAvatar slot in NC's share list. */\n.cn-shares-tab__avatar {\n\tposition: relative;\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 34px;\n\theight: 34px;\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n}\n\n.cn-shares-tab__avatar-lock {\n\tposition: absolute;\n\tright: -2px;\n\tbottom: -2px;\n\tcolor: var(--color-warning, #e9a40f);\n\tbackground: var(--color-main-background);\n\tborder-radius: 50%;\n}\n\n.cn-shares-tab__permissions {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tflex-wrap: wrap;\n\tgap: 4px;\n}\n\n.cn-shares-tab__badge {\n\twhite-space: nowrap;\n}\n\n.cn-shares-tab__expiry {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n\n.cn-shares-tab__footer {\n\tmargin-top: 12px;\n\tdisplay: flex;\n\tjustify-content: flex-end;\n}\n</style>\n"],"names":["CnStatusBadge","CnShareCreate","t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnSharesTab.vue2.js","sources":["../../../../../src/integrations/builtin/shares/CnSharesTab.vue"],"sourcesContent":["<!--\n CnSharesTab — bespoke sidebar tab for the `shares` integration.\n\n Replaces the generic CnIntegrationTab for the `shares` leaf: renders\n a recipient list grouped by share type (user / group / public link\n / federated), with per-row permissions, expiry date, a\n password-protected indicator, and a \"manage in Files\" deep-link.\n Talks to the same OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/shares`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\SharesProvider`\n (which walks linked files and calls `OCP\\Share\\IManager::getSharesBy()`\n per file).\n\n Surface behaviour:\n - Empty state with \"Open Files\" CTA when no shares exist.\n - Loading + 503 \"currently unavailable\" + generic error states match\n CnIntegrationTab's behaviour for AD-23 graceful degradation.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link list\n which loses Shares' four primary signals — recipient, permissions,\n expiry, password-protection — that case handlers need to triage \"who\n has access to this case and how?\" at a glance.\n\n See `openregister/openspec/changes/integration-shares/` for the spec\n delta and ADR-019 (registry mechanism).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-shares-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-shares-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div class=\"cn-shares-tab__toolbar\">\n\t\t\t<NcButton variant=\"primary\" data-testid=\"cn-shares-tab-share-file\" @click=\"openCreateDialog\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ shareFileLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-shares-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"shares.length === 0\" class=\"cn-sidebar-tab__empty cn-shares-tab__empty\">\n\t\t\t<Share :size=\"32\" class=\"cn-shares-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"filesAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<FolderOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openFilesLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<div v-else class=\"cn-shares-tab__groups\">\n\t\t\t<section\n\t\t\t\tv-for=\"group in groupedShares\"\n\t\t\t\t:key=\"group.key\"\n\t\t\t\tclass=\"cn-shares-tab__group\">\n\t\t\t\t<header class=\"cn-shares-tab__group-header\">\n\t\t\t\t\t<component :is=\"group.icon\" :size=\"18\" />\n\t\t\t\t\t<span class=\"cn-shares-tab__group-label\">{{ group.label }}</span>\n\t\t\t\t\t<span class=\"cn-shares-tab__group-count\">{{ group.rows.length }}</span>\n\t\t\t\t</header>\n\t\t\t\t<ul class=\"cn-shares-tab__list\">\n\t\t\t\t\t<NcListItem\n\t\t\t\t\t\tv-for=\"share in group.rows\"\n\t\t\t\t\t\t:key=\"shareKey(share)\"\n\t\t\t\t\t\tclass=\"cn-shares-tab__row\"\n\t\t\t\t\t\t:name=\"shareTarget(share)\"\n\t\t\t\t\t\t:bold=\"true\"\n\t\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<span class=\"cn-shares-tab__avatar\" :aria-hidden=\"true\">\n\t\t\t\t\t\t\t\t<component :is=\"iconFor(shareType(share))\" :size=\"20\" />\n\t\t\t\t\t\t\t\t<LockOutline\n\t\t\t\t\t\t\t\t\tv-if=\"isPasswordProtected(share)\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-shares-tab__avatar-lock\"\n\t\t\t\t\t\t\t\t\t:size=\"12\"\n\t\t\t\t\t\t\t\t\t:title=\"passwordProtectedLabel\"\n\t\t\t\t\t\t\t\t\t:aria-label=\"passwordProtectedLabel\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t\t<span class=\"cn-shares-tab__permissions\">\n\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\tv-for=\"badge in permissionBadges(share)\"\n\t\t\t\t\t\t\t\t\t:key=\"badge.key\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-shares-tab__badge\"\n\t\t\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t\t\t:variant=\"badge.variant\"\n\t\t\t\t\t\t\t\t\t:label=\"badge.label\" />\n\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\tv-if=\"isPasswordProtected(share)\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-shares-tab__badge\"\n\t\t\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t\t\tvariant=\"warning\"\n\t\t\t\t\t\t\t\t\t:label=\"passwordProtectedLabel\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"shareExpiryMs(share)\" #details>\n\t\t\t\t\t\t\t<span class=\"cn-shares-tab__expiry\" :title=\"expiryLabel\">\n\t\t\t\t\t\t\t\t<ClockOutline :size=\"13\" />\n\t\t\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\t\t\t:timestamp=\"shareExpiryMs(share)\"\n\t\t\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template #actions>\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-if=\"canRevoke(share)\"\n\t\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t\t@click=\"revoke(share)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<CloseCircleOutline :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ revokeLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<NcActionLink\n\t\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t\t:href=\"filesAppUrl\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<FolderOutline :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ openFilesLabel }}\n\t\t\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcListItem>\n\t\t\t\t</ul>\n\t\t\t</section>\n\t\t\t<div class=\"cn-shares-tab__footer\">\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t:href=\"filesAppUrl\"\n\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<FolderOutline :size=\"18\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ openFilesLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<CnShareCreate\n\t\t\tv-if=\"showCreate\"\n\t\t\t:files=\"shareableFiles\"\n\t\t\t:filesLoading=\"filesLoading\"\n\t\t\t:principals=\"principals\"\n\t\t\t:principalsLoading=\"principalsLoading\"\n\t\t\t@close=\"showCreate = false\"\n\t\t\t@searchPrincipals=\"searchPrincipals\"\n\t\t\t@create=\"createShare\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcActionLink, NcButton, NcDateTime, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AccountGroupOutline from 'vue-material-design-icons/AccountGroupOutline.vue'\nimport AccountOutline from 'vue-material-design-icons/AccountOutline.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport CloseCircleOutline from 'vue-material-design-icons/CloseCircleOutline.vue'\nimport Earth from 'vue-material-design-icons/Earth.vue'\nimport EmailOutline from 'vue-material-design-icons/EmailOutline.vue'\nimport FolderOutline from 'vue-material-design-icons/FolderOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport LockOutline from 'vue-material-design-icons/LockOutline.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport Share from 'vue-material-design-icons/Share.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { CnShareCreate } from '../../../components/CnShareCreate/index.js'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\n\n// NC core share-type constants (OCP\\Share\\IShare::TYPE_*).\nconst SHARE_TYPE_USER = 0\nconst SHARE_TYPE_GROUP = 1\nconst SHARE_TYPE_LINK = 3\nconst SHARE_TYPE_EMAIL = 4\nconst SHARE_TYPE_REMOTE = 6\nconst SHARE_TYPE_REMOTE_GROUP = 9\n\n// Permission bitmask (OCP\\Constants::PERMISSION_*).\nconst PERMISSION_READ = 1\nconst PERMISSION_UPDATE = 2\nconst PERMISSION_CREATE = 4\nconst PERMISSION_DELETE = 8\nconst PERMISSION_SHARE = 16\n\n/**\n * CnSharesTab — bespoke recipient list for the `shares` integration.\n *\n * Renders shares grouped by type with permissions, expiry, password-\n * protected flag, and a revoke action. Reads from the OR\n * pluggable-integration endpoint and delegates revoke back to the same\n * endpoint via DELETE.\n */\nexport default {\n\tname: 'CnSharesTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tAccountOutline,\n\t\tAccountGroupOutline,\n\t\tLinkVariant,\n\t\tEmailOutline,\n\t\tEarth,\n\t\tLockOutline,\n\t\tClockOutline,\n\t\tCloseCircleOutline,\n\t\tFolderOutline,\n\t\tShare,\n\t\tPlus,\n\t\tCnStatusBadge,\n\t\tCnShareCreate,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'shares'`). */\n\t\tintegrationId: { type: String, default: 'shares' },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No shares on this object yet') },\n\t\t/** Pre-translated label for the \"Open Files\" CTA. */\n\t\topenFilesLabel: { type: String, default: () => t('nextcloud-vue', 'Manage in Files') },\n\t\t/** Pre-translated banner when the share subsystem is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC sharing is currently unavailable.') },\n\t\t/** Pre-translated revoke button label. */\n\t\trevokeLabel: { type: String, default: () => t('nextcloud-vue', 'Revoke share') },\n\t\t/* eslint-disable vue/no-unused-properties -- the row hides the revoke button when canRevoke is false rather than disabling it, so the tooltip has no anchor today */\n\t\t/** Pre-translated tooltip shown when the current user cannot revoke. */\n\t\trevokeDisabledLabel: { type: String, default: () => t('nextcloud-vue', 'Only the share owner can revoke') },\n\t\t/* eslint-enable vue/no-unused-properties */\n\t\t/** Pre-translated label for the password-protected indicator. */\n\t\tpasswordProtectedLabel: { type: String, default: () => t('nextcloud-vue', 'Password protected') },\n\t\t/** Pre-translated label for the expiry row. */\n\t\texpiryLabel: { type: String, default: () => t('nextcloud-vue', 'Expires') },\n\t\t/** URL of the NC Files app entry. */\n\t\tfilesAppUrl: { type: String, default: '/index.php/apps/files' },\n\t\t/** Pre-translated label for the \"Share file\" toolbar button. */\n\t\tshareFileLabel: { type: String, default: () => t('nextcloud-vue', 'Share file') },\n\t},\n\n\temits: ['search-principals', 'share-created', 'share-revoked'],\n\n\tdata() {\n\t\treturn {\n\t\t\tshares: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tshowCreate: false,\n\t\t\tshareableFiles: [],\n\t\t\tfilesLoading: false,\n\t\t\tprincipals: [],\n\t\t\tprincipalsLoading: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tgroupedShares() {\n\t\t\tconst groups = [\n\t\t\t\t{ key: 'user', label: t('nextcloud-vue', 'Users'), icon: 'AccountOutline', match: (s) => this.shareType(s) === SHARE_TYPE_USER },\n\t\t\t\t{ key: 'group', label: t('nextcloud-vue', 'Groups'), icon: 'AccountGroupOutline', match: (s) => this.shareType(s) === SHARE_TYPE_GROUP },\n\t\t\t\t{ key: 'link', label: t('nextcloud-vue', 'Public links'), icon: 'LinkVariant', match: (s) => this.shareType(s) === SHARE_TYPE_LINK },\n\t\t\t\t{ key: 'email', label: t('nextcloud-vue', 'Email'), icon: 'EmailOutline', match: (s) => this.shareType(s) === SHARE_TYPE_EMAIL },\n\t\t\t\t{ key: 'federated', label: t('nextcloud-vue', 'Federated'), icon: 'Earth', match: (s) => this.shareType(s) === SHARE_TYPE_REMOTE || this.shareType(s) === SHARE_TYPE_REMOTE_GROUP },\n\t\t\t]\n\t\t\treturn groups\n\t\t\t\t.map((g) => ({ ...g, rows: this.shares.filter(g.match) }))\n\t\t\t\t.filter((g) => g.rows.length > 0)\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchShares()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchShares()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchShares()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\t// Tier-2 dedicated endpoints (ShareLinksController) — distinct\n\t\t// from the generic registry `baseUrl()` above. Create/revoke flow\n\t\t// through these so the IManager-backed service owns the write\n\t\t// path (NO cache table).\n\t\tsharesUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/shares`\n\t\t},\n\n\t\tshareableFilesUrl() {\n\t\t\treturn `${this.apiBase}/integrations/shares/files/${this.register}/${this.schema}/${this.objectId}`\n\t\t},\n\n\t\tasync openCreateDialog() {\n\t\t\tthis.showCreate = true\n\t\t\tawait this.fetchShareableFiles()\n\t\t},\n\n\t\tasync fetchShareableFiles() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.filesLoading = true\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.shareableFilesUrl()), { 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.shareableFiles = data.results || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else {\n\t\t\t\t\tthis.shareableFiles = []\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnSharesTab] failed to fetch shareable files', err)\n\t\t\t\tthis.shareableFiles = []\n\t\t\t} finally {\n\t\t\t\tthis.filesLoading = false\n\t\t\t}\n\t\t},\n\n\t\tsearchPrincipals({ shareType, query }) {\n\t\t\t// Principal lookup is host-supplied: the host app wires NC's\n\t\t\t// share-search OCS endpoint. Emit upward so the host can\n\t\t\t// populate `principals`; default is a no-op empty list.\n\t\t\tthis.$emit('search-principals', { shareType, query })\n\t\t},\n\n\t\tasync createShare(payload) {\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.sharesUrl()), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.showCreate = false\n\t\t\t\t\tthis.$emit('share-created', payload)\n\t\t\t\t\tawait this.fetchShares()\n\t\t\t\t} else {\n\t\t\t\t\tconst data = await response.json().catch(() => ({}))\n\t\t\t\t\tthis.error = data.error || t('nextcloud-vue', 'Could not create share.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnSharesTab] failed to create share', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create share.')\n\t\t\t}\n\t\t},\n\n\t\tshareKey(share) {\n\t\t\treturn share.id ?? share.shareId ?? share.token ?? ''\n\t\t},\n\n\t\tshareType(share) {\n\t\t\tconst value = share.shareType ?? share.share_type ?? share.type\n\t\t\treturn Number.isFinite(Number(value)) ? Number(value) : -1\n\t\t},\n\n\t\tshareTarget(share) {\n\t\t\tconst type = this.shareType(share)\n\t\t\tif (type === SHARE_TYPE_LINK) {\n\t\t\t\t// Public links surface as a generic \"Share link\" label,\n\t\t\t\t// mirroring NC core's share list (the raw token is never\n\t\t\t\t// shown as a recipient name).\n\t\t\t\treturn share.shareWithDisplayname || share.label || t('nextcloud-vue', 'Share link')\n\t\t\t}\n\t\t\treturn share.shareWithDisplayname || share.shareWith || share.share_with || share.targetDisplayName || ''\n\t\t},\n\n\t\t// Picks the recipient/share-type icon component for a row,\n\t\t// mirroring NC core's share list (user / group / link / email /\n\t\t// federated).\n\t\ticonFor(type) {\n\t\t\tif (type === SHARE_TYPE_GROUP) {\n\t\t\t\treturn 'AccountGroupOutline'\n\t\t\t}\n\t\t\tif (type === SHARE_TYPE_LINK) {\n\t\t\t\treturn 'LinkVariant'\n\t\t\t}\n\t\t\tif (type === SHARE_TYPE_EMAIL) {\n\t\t\t\treturn 'EmailOutline'\n\t\t\t}\n\t\t\tif (type === SHARE_TYPE_REMOTE || type === SHARE_TYPE_REMOTE_GROUP) {\n\t\t\t\treturn 'Earth'\n\t\t\t}\n\t\t\treturn 'AccountOutline'\n\t\t},\n\n\t\tpermissionLabel(share) {\n\t\t\treturn this.permissionBadges(share).map((b) => b.label).join(' · ')\n\t\t},\n\n\t\t// Decomposes the NC permission bitmask into the badge set the real\n\t\t// share UI shows: \"Can edit\" / \"Read only\" (mutually exclusive),\n\t\t// plus \"Can reshare\" when the share permission bit is set.\n\t\tpermissionBadges(share) {\n\t\t\tconst perms = Number(share.permissions ?? share.permission ?? 0)\n\t\t\tconst canEdit = (perms & PERMISSION_UPDATE) === PERMISSION_UPDATE\n\t\t\t\t|| (perms & PERMISSION_CREATE) === PERMISSION_CREATE\n\t\t\t\t|| (perms & PERMISSION_DELETE) === PERMISSION_DELETE\n\t\t\tconst canRead = (perms & PERMISSION_READ) === PERMISSION_READ\n\t\t\tconst badges = []\n\t\t\tif (canEdit) {\n\t\t\t\tbadges.push({ key: 'edit', variant: 'success', label: t('nextcloud-vue', 'Can edit') })\n\t\t\t} else if (canRead) {\n\t\t\t\tbadges.push({ key: 'read', variant: 'default', label: t('nextcloud-vue', 'Read only') })\n\t\t\t}\n\t\t\tif ((perms & PERMISSION_SHARE) === PERMISSION_SHARE) {\n\t\t\t\tbadges.push({ key: 'reshare', variant: 'info', label: t('nextcloud-vue', 'Can reshare') })\n\t\t\t}\n\t\t\treturn badges\n\t\t},\n\n\t\t// Returns the expiry as epoch-ms for <NcDateTime>, or 0 when there\n\t\t// is no (valid) expiry so the template can `v-if` it away.\n\t\tshareExpiryMs(share) {\n\t\t\tconst value = share.expiration ?? share.expirationDate ?? share.expiry ?? null\n\t\t\tif (!value) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\tconst date = new Date(value)\n\t\t\tconst ms = date.getTime()\n\t\t\treturn Number.isNaN(ms) ? 0 : ms\n\t\t},\n\n\t\tisPasswordProtected(share) {\n\t\t\treturn Boolean(share.passwordProtected ?? share.hasPassword ?? share.password)\n\t\t},\n\n\t\tcanRevoke(share) {\n\t\t\t// The current-user-can-revoke flag is provider-supplied. When\n\t\t\t// absent we default to true so the action stays usable; the\n\t\t\t// backend rejects unauthorized revoke attempts.\n\t\t\treturn share.canRevoke !== false\n\t\t},\n\n\t\tasync fetchShares() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.shares = rows\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.shares = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.shares = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load shares.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnSharesTab] failed to fetch shares', err)\n\t\t\t\tthis.shares = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load shares.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync revoke(share) {\n\t\t\tconst id = this.shareKey(share)\n\t\t\tif (!id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(id)}`), {\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.shares = this.shares.filter((row) => this.shareKey(row) !== id)\n\t\t\t\t\tthis.$emit('share-revoked', { id, share })\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not revoke share.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnSharesTab] failed to revoke share', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not revoke share.')\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-shares-tab__banner {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 10px;\n\tmargin-bottom: 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-shares-tab__toolbar {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\tmargin-bottom: 10px;\n}\n\n.cn-shares-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-shares-tab__empty {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 16px 8px;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n}\n\n.cn-shares-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-shares-tab__group {\n\tmargin-bottom: 12px;\n}\n\n.cn-shares-tab__group-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 0;\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-shares-tab__group-label {\n\tflex: 1;\n}\n\n.cn-shares-tab__group-count {\n\tfont-weight: normal;\n\tfont-size: 0.85em;\n\tpadding: 1px 6px;\n\tborder-radius: 9px;\n\tbackground: var(--color-background-hover);\n}\n\n.cn-shares-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* Share-type icon tile, mirroring the NcAvatar slot in NC's share list. */\n.cn-shares-tab__avatar {\n\tposition: relative;\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 34px;\n\theight: 34px;\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n}\n\n.cn-shares-tab__avatar-lock {\n\tposition: absolute;\n\tright: -2px;\n\tbottom: -2px;\n\tcolor: var(--color-warning, #e9a40f);\n\tbackground: var(--color-main-background);\n\tborder-radius: 50%;\n}\n\n.cn-shares-tab__permissions {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tflex-wrap: wrap;\n\tgap: 4px;\n}\n\n.cn-shares-tab__badge {\n\twhite-space: nowrap;\n}\n\n.cn-shares-tab__expiry {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n\n.cn-shares-tab__footer {\n\tmargin-top: 12px;\n\tdisplay: flex;\n\tjustify-content: flex-end;\n}\n</style>\n"],"names":["CnStatusBadge","CnShareCreate","t"],"mappings":"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"}
|
|
@@ -85,8 +85,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
85
85
|
createVNode(_component_NcButton, {
|
|
86
86
|
variant: "primary",
|
|
87
87
|
href: $props.filesAppUrl,
|
|
88
|
-
target: "_blank"
|
|
89
|
-
rel: "noopener noreferrer"
|
|
88
|
+
target: "_blank"
|
|
90
89
|
}, {
|
|
91
90
|
icon: withCtx(() => [
|
|
92
91
|
createVNode(_component_FolderOutline, { size: 20 })
|
|
@@ -172,8 +171,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
172
171
|
createVNode(_component_NcActionLink, {
|
|
173
172
|
closeAfterClick: true,
|
|
174
173
|
href: $props.filesAppUrl,
|
|
175
|
-
target: "_blank"
|
|
176
|
-
rel: "noopener noreferrer"
|
|
174
|
+
target: "_blank"
|
|
177
175
|
}, {
|
|
178
176
|
icon: withCtx(() => [
|
|
179
177
|
createVNode(_component_FolderOutline, { size: 20 })
|
|
@@ -213,8 +211,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
213
211
|
createVNode(_component_NcButton, {
|
|
214
212
|
variant: "tertiary",
|
|
215
213
|
href: $props.filesAppUrl,
|
|
216
|
-
target: "_blank"
|
|
217
|
-
rel: "noopener noreferrer"
|
|
214
|
+
target: "_blank"
|
|
218
215
|
}, {
|
|
219
216
|
icon: withCtx(() => [
|
|
220
217
|
createVNode(_component_FolderOutline, { size: 18 })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnSharesTab.vue3.js","sources":["../../../../../src/integrations/builtin/shares/CnSharesTab.vue?vue&type=template&id=56e70e2e&lang.js"],"sourcesContent":["<!--\n CnSharesTab — bespoke sidebar tab for the `shares` integration.\n\n Replaces the generic CnIntegrationTab for the `shares` leaf: renders\n a recipient list grouped by share type (user / group / public link\n / federated), with per-row permissions, expiry date, a\n password-protected indicator, and a \"manage in Files\" deep-link.\n Talks to the same OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/shares`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\SharesProvider`\n (which walks linked files and calls `OCP\\Share\\IManager::getSharesBy()`\n per file).\n\n Surface behaviour:\n - Empty state with \"Open Files\" CTA when no shares exist.\n - Loading + 503 \"currently unavailable\" + generic error states match\n CnIntegrationTab's behaviour for AD-23 graceful degradation.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link list\n which loses Shares' four primary signals — recipient, permissions,\n expiry, password-protection — that case handlers need to triage \"who\n has access to this case and how?\" at a glance.\n\n See `openregister/openspec/changes/integration-shares/` for the spec\n delta and ADR-019 (registry mechanism).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-shares-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-shares-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div class=\"cn-shares-tab__toolbar\">\n\t\t\t<NcButton variant=\"primary\" data-testid=\"cn-shares-tab-share-file\" @click=\"openCreateDialog\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ shareFileLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-shares-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"shares.length === 0\" class=\"cn-sidebar-tab__empty cn-shares-tab__empty\">\n\t\t\t<Share :size=\"32\" class=\"cn-shares-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"filesAppUrl\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener noreferrer\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<FolderOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openFilesLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<div v-else class=\"cn-shares-tab__groups\">\n\t\t\t<section\n\t\t\t\tv-for=\"group in groupedShares\"\n\t\t\t\t:key=\"group.key\"\n\t\t\t\tclass=\"cn-shares-tab__group\">\n\t\t\t\t<header class=\"cn-shares-tab__group-header\">\n\t\t\t\t\t<component :is=\"group.icon\" :size=\"18\" />\n\t\t\t\t\t<span class=\"cn-shares-tab__group-label\">{{ group.label }}</span>\n\t\t\t\t\t<span class=\"cn-shares-tab__group-count\">{{ group.rows.length }}</span>\n\t\t\t\t</header>\n\t\t\t\t<ul class=\"cn-shares-tab__list\">\n\t\t\t\t\t<NcListItem\n\t\t\t\t\t\tv-for=\"share in group.rows\"\n\t\t\t\t\t\t:key=\"shareKey(share)\"\n\t\t\t\t\t\tclass=\"cn-shares-tab__row\"\n\t\t\t\t\t\t:name=\"shareTarget(share)\"\n\t\t\t\t\t\t:bold=\"true\"\n\t\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<span class=\"cn-shares-tab__avatar\" :aria-hidden=\"true\">\n\t\t\t\t\t\t\t\t<component :is=\"iconFor(shareType(share))\" :size=\"20\" />\n\t\t\t\t\t\t\t\t<LockOutline\n\t\t\t\t\t\t\t\t\tv-if=\"isPasswordProtected(share)\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-shares-tab__avatar-lock\"\n\t\t\t\t\t\t\t\t\t:size=\"12\"\n\t\t\t\t\t\t\t\t\t:title=\"passwordProtectedLabel\"\n\t\t\t\t\t\t\t\t\t:aria-label=\"passwordProtectedLabel\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t\t<span class=\"cn-shares-tab__permissions\">\n\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\tv-for=\"badge in permissionBadges(share)\"\n\t\t\t\t\t\t\t\t\t:key=\"badge.key\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-shares-tab__badge\"\n\t\t\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t\t\t:variant=\"badge.variant\"\n\t\t\t\t\t\t\t\t\t:label=\"badge.label\" />\n\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\tv-if=\"isPasswordProtected(share)\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-shares-tab__badge\"\n\t\t\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t\t\tvariant=\"warning\"\n\t\t\t\t\t\t\t\t\t:label=\"passwordProtectedLabel\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"shareExpiryMs(share)\" #details>\n\t\t\t\t\t\t\t<span class=\"cn-shares-tab__expiry\" :title=\"expiryLabel\">\n\t\t\t\t\t\t\t\t<ClockOutline :size=\"13\" />\n\t\t\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\t\t\t:timestamp=\"shareExpiryMs(share)\"\n\t\t\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template #actions>\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-if=\"canRevoke(share)\"\n\t\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t\t@click=\"revoke(share)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<CloseCircleOutline :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ revokeLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<NcActionLink\n\t\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t\t:href=\"filesAppUrl\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener noreferrer\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<FolderOutline :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ openFilesLabel }}\n\t\t\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcListItem>\n\t\t\t\t</ul>\n\t\t\t</section>\n\t\t\t<div class=\"cn-shares-tab__footer\">\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t:href=\"filesAppUrl\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener noreferrer\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<FolderOutline :size=\"18\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ openFilesLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<CnShareCreate\n\t\t\tv-if=\"showCreate\"\n\t\t\t:files=\"shareableFiles\"\n\t\t\t:filesLoading=\"filesLoading\"\n\t\t\t:principals=\"principals\"\n\t\t\t:principalsLoading=\"principalsLoading\"\n\t\t\t@close=\"showCreate = false\"\n\t\t\t@searchPrincipals=\"searchPrincipals\"\n\t\t\t@create=\"createShare\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcActionLink, NcButton, NcDateTime, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AccountGroupOutline from 'vue-material-design-icons/AccountGroupOutline.vue'\nimport AccountOutline from 'vue-material-design-icons/AccountOutline.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport CloseCircleOutline from 'vue-material-design-icons/CloseCircleOutline.vue'\nimport Earth from 'vue-material-design-icons/Earth.vue'\nimport EmailOutline from 'vue-material-design-icons/EmailOutline.vue'\nimport FolderOutline from 'vue-material-design-icons/FolderOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport LockOutline from 'vue-material-design-icons/LockOutline.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport Share from 'vue-material-design-icons/Share.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { CnShareCreate } from '../../../components/CnShareCreate/index.js'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\n\n// NC core share-type constants (OCP\\Share\\IShare::TYPE_*).\nconst SHARE_TYPE_USER = 0\nconst SHARE_TYPE_GROUP = 1\nconst SHARE_TYPE_LINK = 3\nconst SHARE_TYPE_EMAIL = 4\nconst SHARE_TYPE_REMOTE = 6\nconst SHARE_TYPE_REMOTE_GROUP = 9\n\n// Permission bitmask (OCP\\Constants::PERMISSION_*).\nconst PERMISSION_READ = 1\nconst PERMISSION_UPDATE = 2\nconst PERMISSION_CREATE = 4\nconst PERMISSION_DELETE = 8\nconst PERMISSION_SHARE = 16\n\n/**\n * CnSharesTab — bespoke recipient list for the `shares` integration.\n *\n * Renders shares grouped by type with permissions, expiry, password-\n * protected flag, and a revoke action. Reads from the OR\n * pluggable-integration endpoint and delegates revoke back to the same\n * endpoint via DELETE.\n */\nexport default {\n\tname: 'CnSharesTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tAccountOutline,\n\t\tAccountGroupOutline,\n\t\tLinkVariant,\n\t\tEmailOutline,\n\t\tEarth,\n\t\tLockOutline,\n\t\tClockOutline,\n\t\tCloseCircleOutline,\n\t\tFolderOutline,\n\t\tShare,\n\t\tPlus,\n\t\tCnStatusBadge,\n\t\tCnShareCreate,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'shares'`). */\n\t\tintegrationId: { type: String, default: 'shares' },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No shares on this object yet') },\n\t\t/** Pre-translated label for the \"Open Files\" CTA. */\n\t\topenFilesLabel: { type: String, default: () => t('nextcloud-vue', 'Manage in Files') },\n\t\t/** Pre-translated banner when the share subsystem is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC sharing is currently unavailable.') },\n\t\t/** Pre-translated revoke button label. */\n\t\trevokeLabel: { type: String, default: () => t('nextcloud-vue', 'Revoke share') },\n\t\t/* eslint-disable vue/no-unused-properties -- the row hides the revoke button when canRevoke is false rather than disabling it, so the tooltip has no anchor today */\n\t\t/** Pre-translated tooltip shown when the current user cannot revoke. */\n\t\trevokeDisabledLabel: { type: String, default: () => t('nextcloud-vue', 'Only the share owner can revoke') },\n\t\t/* eslint-enable vue/no-unused-properties */\n\t\t/** Pre-translated label for the password-protected indicator. */\n\t\tpasswordProtectedLabel: { type: String, default: () => t('nextcloud-vue', 'Password protected') },\n\t\t/** Pre-translated label for the expiry row. */\n\t\texpiryLabel: { type: String, default: () => t('nextcloud-vue', 'Expires') },\n\t\t/** URL of the NC Files app entry. */\n\t\tfilesAppUrl: { type: String, default: '/index.php/apps/files' },\n\t\t/** Pre-translated label for the \"Share file\" toolbar button. */\n\t\tshareFileLabel: { type: String, default: () => t('nextcloud-vue', 'Share file') },\n\t},\n\n\temits: ['search-principals', 'share-created', 'share-revoked'],\n\n\tdata() {\n\t\treturn {\n\t\t\tshares: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tshowCreate: false,\n\t\t\tshareableFiles: [],\n\t\t\tfilesLoading: false,\n\t\t\tprincipals: [],\n\t\t\tprincipalsLoading: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tgroupedShares() {\n\t\t\tconst groups = [\n\t\t\t\t{ key: 'user', label: t('nextcloud-vue', 'Users'), icon: 'AccountOutline', match: (s) => this.shareType(s) === SHARE_TYPE_USER },\n\t\t\t\t{ key: 'group', label: t('nextcloud-vue', 'Groups'), icon: 'AccountGroupOutline', match: (s) => this.shareType(s) === SHARE_TYPE_GROUP },\n\t\t\t\t{ key: 'link', label: t('nextcloud-vue', 'Public links'), icon: 'LinkVariant', match: (s) => this.shareType(s) === SHARE_TYPE_LINK },\n\t\t\t\t{ key: 'email', label: t('nextcloud-vue', 'Email'), icon: 'EmailOutline', match: (s) => this.shareType(s) === SHARE_TYPE_EMAIL },\n\t\t\t\t{ key: 'federated', label: t('nextcloud-vue', 'Federated'), icon: 'Earth', match: (s) => this.shareType(s) === SHARE_TYPE_REMOTE || this.shareType(s) === SHARE_TYPE_REMOTE_GROUP },\n\t\t\t]\n\t\t\treturn groups\n\t\t\t\t.map((g) => ({ ...g, rows: this.shares.filter(g.match) }))\n\t\t\t\t.filter((g) => g.rows.length > 0)\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchShares()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchShares()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchShares()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\t// Tier-2 dedicated endpoints (ShareLinksController) — distinct\n\t\t// from the generic registry `baseUrl()` above. Create/revoke flow\n\t\t// through these so the IManager-backed service owns the write\n\t\t// path (NO cache table).\n\t\tsharesUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/shares`\n\t\t},\n\n\t\tshareableFilesUrl() {\n\t\t\treturn `${this.apiBase}/integrations/shares/files/${this.register}/${this.schema}/${this.objectId}`\n\t\t},\n\n\t\tasync openCreateDialog() {\n\t\t\tthis.showCreate = true\n\t\t\tawait this.fetchShareableFiles()\n\t\t},\n\n\t\tasync fetchShareableFiles() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.filesLoading = true\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.shareableFilesUrl()), { 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.shareableFiles = data.results || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else {\n\t\t\t\t\tthis.shareableFiles = []\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnSharesTab] failed to fetch shareable files', err)\n\t\t\t\tthis.shareableFiles = []\n\t\t\t} finally {\n\t\t\t\tthis.filesLoading = false\n\t\t\t}\n\t\t},\n\n\t\tsearchPrincipals({ shareType, query }) {\n\t\t\t// Principal lookup is host-supplied: the host app wires NC's\n\t\t\t// share-search OCS endpoint. Emit upward so the host can\n\t\t\t// populate `principals`; default is a no-op empty list.\n\t\t\tthis.$emit('search-principals', { shareType, query })\n\t\t},\n\n\t\tasync createShare(payload) {\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.sharesUrl()), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.showCreate = false\n\t\t\t\t\tthis.$emit('share-created', payload)\n\t\t\t\t\tawait this.fetchShares()\n\t\t\t\t} else {\n\t\t\t\t\tconst data = await response.json().catch(() => ({}))\n\t\t\t\t\tthis.error = data.error || t('nextcloud-vue', 'Could not create share.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnSharesTab] failed to create share', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create share.')\n\t\t\t}\n\t\t},\n\n\t\tshareKey(share) {\n\t\t\treturn share.id ?? share.shareId ?? share.token ?? ''\n\t\t},\n\n\t\tshareType(share) {\n\t\t\tconst value = share.shareType ?? share.share_type ?? share.type\n\t\t\treturn Number.isFinite(Number(value)) ? Number(value) : -1\n\t\t},\n\n\t\tshareTarget(share) {\n\t\t\tconst type = this.shareType(share)\n\t\t\tif (type === SHARE_TYPE_LINK) {\n\t\t\t\t// Public links surface as a generic \"Share link\" label,\n\t\t\t\t// mirroring NC core's share list (the raw token is never\n\t\t\t\t// shown as a recipient name).\n\t\t\t\treturn share.shareWithDisplayname || share.label || t('nextcloud-vue', 'Share link')\n\t\t\t}\n\t\t\treturn share.shareWithDisplayname || share.shareWith || share.share_with || share.targetDisplayName || ''\n\t\t},\n\n\t\t// Picks the recipient/share-type icon component for a row,\n\t\t// mirroring NC core's share list (user / group / link / email /\n\t\t// federated).\n\t\ticonFor(type) {\n\t\t\tif (type === SHARE_TYPE_GROUP) {\n\t\t\t\treturn 'AccountGroupOutline'\n\t\t\t}\n\t\t\tif (type === SHARE_TYPE_LINK) {\n\t\t\t\treturn 'LinkVariant'\n\t\t\t}\n\t\t\tif (type === SHARE_TYPE_EMAIL) {\n\t\t\t\treturn 'EmailOutline'\n\t\t\t}\n\t\t\tif (type === SHARE_TYPE_REMOTE || type === SHARE_TYPE_REMOTE_GROUP) {\n\t\t\t\treturn 'Earth'\n\t\t\t}\n\t\t\treturn 'AccountOutline'\n\t\t},\n\n\t\tpermissionLabel(share) {\n\t\t\treturn this.permissionBadges(share).map((b) => b.label).join(' · ')\n\t\t},\n\n\t\t// Decomposes the NC permission bitmask into the badge set the real\n\t\t// share UI shows: \"Can edit\" / \"Read only\" (mutually exclusive),\n\t\t// plus \"Can reshare\" when the share permission bit is set.\n\t\tpermissionBadges(share) {\n\t\t\tconst perms = Number(share.permissions ?? share.permission ?? 0)\n\t\t\tconst canEdit = (perms & PERMISSION_UPDATE) === PERMISSION_UPDATE\n\t\t\t\t|| (perms & PERMISSION_CREATE) === PERMISSION_CREATE\n\t\t\t\t|| (perms & PERMISSION_DELETE) === PERMISSION_DELETE\n\t\t\tconst canRead = (perms & PERMISSION_READ) === PERMISSION_READ\n\t\t\tconst badges = []\n\t\t\tif (canEdit) {\n\t\t\t\tbadges.push({ key: 'edit', variant: 'success', label: t('nextcloud-vue', 'Can edit') })\n\t\t\t} else if (canRead) {\n\t\t\t\tbadges.push({ key: 'read', variant: 'default', label: t('nextcloud-vue', 'Read only') })\n\t\t\t}\n\t\t\tif ((perms & PERMISSION_SHARE) === PERMISSION_SHARE) {\n\t\t\t\tbadges.push({ key: 'reshare', variant: 'info', label: t('nextcloud-vue', 'Can reshare') })\n\t\t\t}\n\t\t\treturn badges\n\t\t},\n\n\t\t// Returns the expiry as epoch-ms for <NcDateTime>, or 0 when there\n\t\t// is no (valid) expiry so the template can `v-if` it away.\n\t\tshareExpiryMs(share) {\n\t\t\tconst value = share.expiration ?? share.expirationDate ?? share.expiry ?? null\n\t\t\tif (!value) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\tconst date = new Date(value)\n\t\t\tconst ms = date.getTime()\n\t\t\treturn Number.isNaN(ms) ? 0 : ms\n\t\t},\n\n\t\tisPasswordProtected(share) {\n\t\t\treturn Boolean(share.passwordProtected ?? share.hasPassword ?? share.password)\n\t\t},\n\n\t\tcanRevoke(share) {\n\t\t\t// The current-user-can-revoke flag is provider-supplied. When\n\t\t\t// absent we default to true so the action stays usable; the\n\t\t\t// backend rejects unauthorized revoke attempts.\n\t\t\treturn share.canRevoke !== false\n\t\t},\n\n\t\tasync fetchShares() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.shares = rows\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.shares = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.shares = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load shares.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnSharesTab] failed to fetch shares', err)\n\t\t\t\tthis.shares = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load shares.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync revoke(share) {\n\t\t\tconst id = this.shareKey(share)\n\t\t\tif (!id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(id)}`), {\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.shares = this.shares.filter((row) => this.shareKey(row) !== id)\n\t\t\t\t\tthis.$emit('share-revoked', { id, share })\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not revoke share.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnSharesTab] failed to revoke share', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not revoke share.')\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-shares-tab__banner {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 10px;\n\tmargin-bottom: 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-shares-tab__toolbar {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\tmargin-bottom: 10px;\n}\n\n.cn-shares-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-shares-tab__empty {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 16px 8px;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n}\n\n.cn-shares-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-shares-tab__group {\n\tmargin-bottom: 12px;\n}\n\n.cn-shares-tab__group-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 0;\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-shares-tab__group-label {\n\tflex: 1;\n}\n\n.cn-shares-tab__group-count {\n\tfont-weight: normal;\n\tfont-size: 0.85em;\n\tpadding: 1px 6px;\n\tborder-radius: 9px;\n\tbackground: var(--color-background-hover);\n}\n\n.cn-shares-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* Share-type icon tile, mirroring the NcAvatar slot in NC's share list. */\n.cn-shares-tab__avatar {\n\tposition: relative;\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 34px;\n\theight: 34px;\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n}\n\n.cn-shares-tab__avatar-lock {\n\tposition: absolute;\n\tright: -2px;\n\tbottom: -2px;\n\tcolor: var(--color-warning, #e9a40f);\n\tbackground: var(--color-main-background);\n\tborder-radius: 50%;\n}\n\n.cn-shares-tab__permissions {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tflex-wrap: wrap;\n\tgap: 4px;\n}\n\n.cn-shares-tab__badge {\n\twhite-space: nowrap;\n}\n\n.cn-shares-tab__expiry {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n\n.cn-shares-tab__footer {\n\tmargin-top: 12px;\n\tdisplay: flex;\n\tjustify-content: flex-end;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_createVNode","_createTextVNode","_createBlock","_toDisplayString","_resolveDynamicComponent","_Fragment","_renderList"],"mappings":";;AAqDM,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,8BAAiB,GAAA;;;AACV,EAAA,KAAA,EAAA,uBAAA;AAAA,EAAA,IAAA,EAAA,OAAA;;AAKZ,MAAA,UAAA,GAAA,EAAA,KAAA,EAAA,wBAAA,GAAA;;;AAUK,EAAA,KAAA,EAAA,sBAAA;AAAA,EAAA,IAAA,EAAA,OAAA;;;;AAGoB,EAAA,KAAA,EAAA,4CAAA;;;;AAclB,EAAA,KAAK,EAAC,uBAAuB;;AAK/B,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,6BAAG,GAAA;AAEV,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,4BAAO,GAAA;AACb,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,4BAAmB,GAAA;2BAEvB,EAAC,qBAAkB,GAAA;;AASnB,EAAA,KAAG,EAAA,uBAAA;AAAA,EAAA,aAAA,EAAA,IAAA;;AAWH,MAAA,WAAA,GAAA,EAAA,KAAK,EAAC,4BAAW,GAAA;;;;;;;;;;;;;;;;;;;;;;KA9DlB,KAAO,CAAA,QAAA;AAAf,SAAAA,SAAA,EAAA,EAAAC,kBAAA,CAGI,OAHJ,UAGI,EAAA;qBAFwB,CAAA,6BAAA,EAAA,EAAR,IAAI,EAAC,EAAG,EAAA,CAAA;6BAClB,8BAAF,KAAE,CAAA,QAAA,CAAA,EAAA,CAAA,YAAA;;;AAGZ,IAAAC,kBAAA,CAOM,OAPN,UAOM,EAAA;MANLC,WAKW,CAAA,mBAAA,EAAA;AALA,QAAA,OAAM,EAAC,SAAS;AAAC,QAAA,aAAW,EAAC,0BAAA;AAAA,QAAA,OAAA,EAAA,QAAA,CAAA,gBAAA;;AACjC,QAAA,IAAA,UACL,MAAmB;UAAnBA,WAAmB,CAAA,eAAA,EAAA,EAAZ,IAAK,EAAC,EAAE,EAAA,CAAA;;yBACL,MACX;AADW,UAAAC,eAAA,CAAA,GACX,mBAAG,MAAc,CAAA,cAAA,CAAA,EAAA,CAAA,YAAA;;;;;KAIE,KAAO,CAAA,OAAA;sBAAzBC,WAA6B,CAAA,wBAAA,EAAA,EAAA,GAAA,EAAA,CAAA,EAAA,CAAA;SACtB,KAAA,CAAA,KAAA;wBAARJ,kBAEI,CAAA,KAAA,EAFJ,UAEI,EAAAK,eAAA,CADF,KAAK,CAAA,KAAA,CAAA,EAAA,CAAA,YAAA;AAEM,UAAA,CAAA,KAAA,CAAA,MAAO,CAAC,MAAM,KAAA,CAAA;6CAatB,iBAAA,EAAA;cAZHH,WAAqB,CAAA,gBAAA,EAAA;AAAhB,gBAAA,IAAI,EAAE,EAAE;AAAE,gBAAA,KAAK,EAAC,2BAAA;;iCACD,2BAAjB,MAAU,CAAA,UAAA,CAAA,EAAA,CAAA,YAAA;yBAUL,CAAA,mBAAA,EAAA;uBARH,EAAC,SAAK;oBACR,EAAE,MAAW,CAAA,WAAA;AAChB,gBAAA,MAAI,EAAC,QAAQ;mBACZ,EAAC,qBAAqB;;AACd,gBAAA,IAAI,gBACA;6BAAA,CAAA,wBAAA,EAAA,EAAA,IAAA,EAAA,EAAA,EAAA,CAAA;;iCACJ,MACN;AADM,kBAAAC,eAAA,CAAA,GACN,mBAAA,MAAY,CAAA,cAAA,CAAA,EAAA,CAAA,YAAA;;;;;AAGZ,aAAAJ,SAAA,EAAA,EAAAC,kBAAA,QAAA;gCACFA,8CACS,QAAA,CAAA,aAAA,EAAA,CAAL,KAAK,KAAA;qCADTA;kBAED,GAAE,EAAE,KAAM,CAAA,GAAG;AACZ,kBAAA,KAAE,EAAC,sBAAsB;;qCAKnB,oBAAA,EAAA;AAHJ,qBAAAD,SAAA,EAAA,EAAAK,WAAA,CAAqCE,wBAAxB,KAAI,CAAC,IAAI,CAAG,EAAA,EAAA,IAAI,EAAE,EAAE,EAAA,CAAA;AACjC,oBAAAL,kBAAA,CAAe,MAAf,EAAA,UAAe,EAAAI,eAAA,CAAA,KAAA,CAAA,KAAA,CAAA,EAAA,CAAA,YAAA;uCACY,kBAAA,EAAAA,eAAA,CAAA,KAAA,CAAA,IAAA,CAAA,MAAA,CAAA,EAAA,CAAA,YAAA;;;AAG3B,qBAAAN,SAAA,CAAA,IAAA,CAAA,EAAAC,kBAAA,CAgEHO,QA/De,EAAA,IAAA,EAAAC,UAAA,CAAA,KAAA,CAAA,IAAA,GAAR,KAAK,KAAA;2CADTJ,WAgEH,CAAA,qBAAA,EAAA;wBA9DG,GAAC,EAAE,QAAQ,CAAA,QAAA,CAAC,KAAK,CAAA;6BACf,EAAC,oBAAoB;4BACrB,EAAE,QAAO,CAAA,WAAA,CAAA,KAAA,CAAA;AACX,wBAAA,IAAE,EAAE,IAAI;AACR,wBAAA,mBAAiB,EAAE,IAAI;;AACnB,wBAAA,IAAA,UACN,MAQO;AARP,0BAAAH,kBAAA,CAQO,QARP,WAQO,EAAA;0CAPNG,WAA4C,CAAAE,uBAAA,CAA5B,gBAAO,CAAC,QAAA,CAAA,SAAS,CAAC,KAAK,CAAA,CAAA,CAAA,EAAA,EAAK,IAAA,EAAA,EAAA,EAAA,CAAA;AAErC,4BAAA,CAAA,QAAA,CAAA,mBAAmB,CAAA,KAAA,CAAA;8CAD1BF,WAKK,CAAA,sBAAA,EAAA;;AAHJ,kCAAA,KAAK,EAAC,4BAAG;AACR,kCAAA,IAAI,EAAE,EAAE;AACR,kCAAA,KAAK,EAAE,MAAc,CAAA,sBAAA;AACrB,kCAAA,YAAG,EAAA,MAAA,CAAA,sBAAA;;;;;AAGI,wBAAA,OAAI,gBAeP;6CAAA,mBAAA,EAAA;AAbN,6BAAAL,SAAA,CAAA,IAAA,CAAA,EAAAC,kBAAA,CAMeO,QALE,EAAA,IAAA,EAAAC,UAAA,CAAA,QAAA,CAAA,gBAAK,CAAA,KAAA,IAAd,KAAK,KAAA;mDADbJ,WAMe,CAAA,wBAAA,EAAA;gCAJb,GAAG,EAAE,KAAK,CAAC,GAAE;AACd,gCAAA,KAAK,EAAC,sBAAsB;AAC5B,gCAAA,IAAI,EAAC,OAAI;gCACP,OAAM,EAAE,KAAK,CAAA,OAAA;gCACd,KAAK,EAAE,KAAK,CAAC,KAAA;;;AAER,4BAAA,CAAA,QAAA,CAAA,mBAAe,CAAA,KAAA,CAAA;8CADtBA,WAKe,CAAA,wBAAA,EAAA;;AAHd,kCAAA,KAAK,EAAC,sBAAW;AACjB,kCAAA,IAAI,EAAC,OAAO;AACZ,kCAAA,OAAO,EAAC,SAAS;AAChB,kCAAA,KAAK,EAAC,MAAO,CAAA,sBAAA;;;;;AAWN,wBAAA,OAAK,UACf,MAQC;AAPM,0BAAA,CAAA,QAAA,CAAA,SAAK,CAAA,KAAA,CAAA;4CADZA,WAQC,CAAA,yBAAA,EAAA;;AANC,gCAAA,eAAe,EAAE,IAAI;gCACrB,OAAI,EAAA,MAAA,KAAG,QAAI,CAAA,MAAA,CAAA,KAAA,CAAA,CAAA;;AACD,gCAAA,IAAI,UACd,MAAM;kCAANF,WAAM,CAAA,6BAAA,EAAA,EAAA,IAAA,EAAA,EAAA,EAAA,CAAA;;iDACI,MACX;AADW,kCAAAC,eAAA,CAAA,GACX,mBAAG,MAAG,CAAA,WAAA,CAAA,EAAA,CAAA,YAAA;;;;;0BAEPD,WASe,CAAA,uBAAA,EAAA;;AAPb,4BAAA,IAAI,EAAE,MAAA,CAAA,WAAA;AACP,4BAAA,MAAM,EAAC,QAAI;AACX,4BAAA,GAAG,EAAC,qBAAa;;AACN,4BAAA,IAAI,UACd,MAAiB;8BAAjBA,WAAiB,CAAA,wBAAA,EAAA,EAAD,IAAC,EAAA,EAAA,EAAA,CAAA;;6CACP,MACX;AADW,8BAAAC,eAAA,CAAA,GACX,mBAAG,MAAc,CAAA,cAAA,CAAA,EAAA,CAAA,YAAA;;;;;;;AA1BH,wBAAA,CAAA,QAAA,CAAA,aAAK,CAAA,KAAA,CAAA;;oCAAA,SAAA;0CACpB,MAKO;gCALPF,kBAKO,CAAA,MAAA,EAAA;AALD,kCAAA,KAAK,EAAC,uBAAE;AAAA,kCAAA,KAAA,EAAA,MAAA,CAAA,WAAA;;kCACbC,WAAQ,CAAA,uBAAA,EAAA,EAAA,IAAA,EAAA,EAAA,EAAA,CAAA;kCACRA,WAEI,CAAA,qBAAA,EAAA;oCADJ,SAAA,EAAA,QAAA,CAAA,aAAA,CAAA,KAAA,CAAA;AACC,oCAAA,YAAG,EAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"CnSharesTab.vue3.js","sources":["../../../../../src/integrations/builtin/shares/CnSharesTab.vue?vue&type=template&id=56e70e2e&lang.js"],"sourcesContent":["<!--\n CnSharesTab — bespoke sidebar tab for the `shares` integration.\n\n Replaces the generic CnIntegrationTab for the `shares` leaf: renders\n a recipient list grouped by share type (user / group / public link\n / federated), with per-row permissions, expiry date, a\n password-protected indicator, and a \"manage in Files\" deep-link.\n Talks to the same OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/shares`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\SharesProvider`\n (which walks linked files and calls `OCP\\Share\\IManager::getSharesBy()`\n per file).\n\n Surface behaviour:\n - Empty state with \"Open Files\" CTA when no shares exist.\n - Loading + 503 \"currently unavailable\" + generic error states match\n CnIntegrationTab's behaviour for AD-23 graceful degradation.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link list\n which loses Shares' four primary signals — recipient, permissions,\n expiry, password-protection — that case handlers need to triage \"who\n has access to this case and how?\" at a glance.\n\n See `openregister/openspec/changes/integration-shares/` for the spec\n delta and ADR-019 (registry mechanism).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-shares-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-shares-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div class=\"cn-shares-tab__toolbar\">\n\t\t\t<NcButton variant=\"primary\" data-testid=\"cn-shares-tab-share-file\" @click=\"openCreateDialog\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ shareFileLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-shares-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"shares.length === 0\" class=\"cn-sidebar-tab__empty cn-shares-tab__empty\">\n\t\t\t<Share :size=\"32\" class=\"cn-shares-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"filesAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<FolderOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openFilesLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<div v-else class=\"cn-shares-tab__groups\">\n\t\t\t<section\n\t\t\t\tv-for=\"group in groupedShares\"\n\t\t\t\t:key=\"group.key\"\n\t\t\t\tclass=\"cn-shares-tab__group\">\n\t\t\t\t<header class=\"cn-shares-tab__group-header\">\n\t\t\t\t\t<component :is=\"group.icon\" :size=\"18\" />\n\t\t\t\t\t<span class=\"cn-shares-tab__group-label\">{{ group.label }}</span>\n\t\t\t\t\t<span class=\"cn-shares-tab__group-count\">{{ group.rows.length }}</span>\n\t\t\t\t</header>\n\t\t\t\t<ul class=\"cn-shares-tab__list\">\n\t\t\t\t\t<NcListItem\n\t\t\t\t\t\tv-for=\"share in group.rows\"\n\t\t\t\t\t\t:key=\"shareKey(share)\"\n\t\t\t\t\t\tclass=\"cn-shares-tab__row\"\n\t\t\t\t\t\t:name=\"shareTarget(share)\"\n\t\t\t\t\t\t:bold=\"true\"\n\t\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<span class=\"cn-shares-tab__avatar\" :aria-hidden=\"true\">\n\t\t\t\t\t\t\t\t<component :is=\"iconFor(shareType(share))\" :size=\"20\" />\n\t\t\t\t\t\t\t\t<LockOutline\n\t\t\t\t\t\t\t\t\tv-if=\"isPasswordProtected(share)\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-shares-tab__avatar-lock\"\n\t\t\t\t\t\t\t\t\t:size=\"12\"\n\t\t\t\t\t\t\t\t\t:title=\"passwordProtectedLabel\"\n\t\t\t\t\t\t\t\t\t:aria-label=\"passwordProtectedLabel\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t\t<span class=\"cn-shares-tab__permissions\">\n\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\tv-for=\"badge in permissionBadges(share)\"\n\t\t\t\t\t\t\t\t\t:key=\"badge.key\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-shares-tab__badge\"\n\t\t\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t\t\t:variant=\"badge.variant\"\n\t\t\t\t\t\t\t\t\t:label=\"badge.label\" />\n\t\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\t\tv-if=\"isPasswordProtected(share)\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-shares-tab__badge\"\n\t\t\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t\t\tvariant=\"warning\"\n\t\t\t\t\t\t\t\t\t:label=\"passwordProtectedLabel\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"shareExpiryMs(share)\" #details>\n\t\t\t\t\t\t\t<span class=\"cn-shares-tab__expiry\" :title=\"expiryLabel\">\n\t\t\t\t\t\t\t\t<ClockOutline :size=\"13\" />\n\t\t\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\t\t\t:timestamp=\"shareExpiryMs(share)\"\n\t\t\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template #actions>\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-if=\"canRevoke(share)\"\n\t\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t\t@click=\"revoke(share)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<CloseCircleOutline :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ revokeLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<NcActionLink\n\t\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t\t:href=\"filesAppUrl\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<FolderOutline :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ openFilesLabel }}\n\t\t\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcListItem>\n\t\t\t\t</ul>\n\t\t\t</section>\n\t\t\t<div class=\"cn-shares-tab__footer\">\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t:href=\"filesAppUrl\"\n\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<FolderOutline :size=\"18\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ openFilesLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<CnShareCreate\n\t\t\tv-if=\"showCreate\"\n\t\t\t:files=\"shareableFiles\"\n\t\t\t:filesLoading=\"filesLoading\"\n\t\t\t:principals=\"principals\"\n\t\t\t:principalsLoading=\"principalsLoading\"\n\t\t\t@close=\"showCreate = false\"\n\t\t\t@searchPrincipals=\"searchPrincipals\"\n\t\t\t@create=\"createShare\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcActionLink, NcButton, NcDateTime, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AccountGroupOutline from 'vue-material-design-icons/AccountGroupOutline.vue'\nimport AccountOutline from 'vue-material-design-icons/AccountOutline.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport CloseCircleOutline from 'vue-material-design-icons/CloseCircleOutline.vue'\nimport Earth from 'vue-material-design-icons/Earth.vue'\nimport EmailOutline from 'vue-material-design-icons/EmailOutline.vue'\nimport FolderOutline from 'vue-material-design-icons/FolderOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport LockOutline from 'vue-material-design-icons/LockOutline.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport Share from 'vue-material-design-icons/Share.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { CnShareCreate } from '../../../components/CnShareCreate/index.js'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\n\n// NC core share-type constants (OCP\\Share\\IShare::TYPE_*).\nconst SHARE_TYPE_USER = 0\nconst SHARE_TYPE_GROUP = 1\nconst SHARE_TYPE_LINK = 3\nconst SHARE_TYPE_EMAIL = 4\nconst SHARE_TYPE_REMOTE = 6\nconst SHARE_TYPE_REMOTE_GROUP = 9\n\n// Permission bitmask (OCP\\Constants::PERMISSION_*).\nconst PERMISSION_READ = 1\nconst PERMISSION_UPDATE = 2\nconst PERMISSION_CREATE = 4\nconst PERMISSION_DELETE = 8\nconst PERMISSION_SHARE = 16\n\n/**\n * CnSharesTab — bespoke recipient list for the `shares` integration.\n *\n * Renders shares grouped by type with permissions, expiry, password-\n * protected flag, and a revoke action. Reads from the OR\n * pluggable-integration endpoint and delegates revoke back to the same\n * endpoint via DELETE.\n */\nexport default {\n\tname: 'CnSharesTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tAccountOutline,\n\t\tAccountGroupOutline,\n\t\tLinkVariant,\n\t\tEmailOutline,\n\t\tEarth,\n\t\tLockOutline,\n\t\tClockOutline,\n\t\tCloseCircleOutline,\n\t\tFolderOutline,\n\t\tShare,\n\t\tPlus,\n\t\tCnStatusBadge,\n\t\tCnShareCreate,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'shares'`). */\n\t\tintegrationId: { type: String, default: 'shares' },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No shares on this object yet') },\n\t\t/** Pre-translated label for the \"Open Files\" CTA. */\n\t\topenFilesLabel: { type: String, default: () => t('nextcloud-vue', 'Manage in Files') },\n\t\t/** Pre-translated banner when the share subsystem is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC sharing is currently unavailable.') },\n\t\t/** Pre-translated revoke button label. */\n\t\trevokeLabel: { type: String, default: () => t('nextcloud-vue', 'Revoke share') },\n\t\t/* eslint-disable vue/no-unused-properties -- the row hides the revoke button when canRevoke is false rather than disabling it, so the tooltip has no anchor today */\n\t\t/** Pre-translated tooltip shown when the current user cannot revoke. */\n\t\trevokeDisabledLabel: { type: String, default: () => t('nextcloud-vue', 'Only the share owner can revoke') },\n\t\t/* eslint-enable vue/no-unused-properties */\n\t\t/** Pre-translated label for the password-protected indicator. */\n\t\tpasswordProtectedLabel: { type: String, default: () => t('nextcloud-vue', 'Password protected') },\n\t\t/** Pre-translated label for the expiry row. */\n\t\texpiryLabel: { type: String, default: () => t('nextcloud-vue', 'Expires') },\n\t\t/** URL of the NC Files app entry. */\n\t\tfilesAppUrl: { type: String, default: '/index.php/apps/files' },\n\t\t/** Pre-translated label for the \"Share file\" toolbar button. */\n\t\tshareFileLabel: { type: String, default: () => t('nextcloud-vue', 'Share file') },\n\t},\n\n\temits: ['search-principals', 'share-created', 'share-revoked'],\n\n\tdata() {\n\t\treturn {\n\t\t\tshares: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tshowCreate: false,\n\t\t\tshareableFiles: [],\n\t\t\tfilesLoading: false,\n\t\t\tprincipals: [],\n\t\t\tprincipalsLoading: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tgroupedShares() {\n\t\t\tconst groups = [\n\t\t\t\t{ key: 'user', label: t('nextcloud-vue', 'Users'), icon: 'AccountOutline', match: (s) => this.shareType(s) === SHARE_TYPE_USER },\n\t\t\t\t{ key: 'group', label: t('nextcloud-vue', 'Groups'), icon: 'AccountGroupOutline', match: (s) => this.shareType(s) === SHARE_TYPE_GROUP },\n\t\t\t\t{ key: 'link', label: t('nextcloud-vue', 'Public links'), icon: 'LinkVariant', match: (s) => this.shareType(s) === SHARE_TYPE_LINK },\n\t\t\t\t{ key: 'email', label: t('nextcloud-vue', 'Email'), icon: 'EmailOutline', match: (s) => this.shareType(s) === SHARE_TYPE_EMAIL },\n\t\t\t\t{ key: 'federated', label: t('nextcloud-vue', 'Federated'), icon: 'Earth', match: (s) => this.shareType(s) === SHARE_TYPE_REMOTE || this.shareType(s) === SHARE_TYPE_REMOTE_GROUP },\n\t\t\t]\n\t\t\treturn groups\n\t\t\t\t.map((g) => ({ ...g, rows: this.shares.filter(g.match) }))\n\t\t\t\t.filter((g) => g.rows.length > 0)\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchShares()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchShares()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchShares()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\t// Tier-2 dedicated endpoints (ShareLinksController) — distinct\n\t\t// from the generic registry `baseUrl()` above. Create/revoke flow\n\t\t// through these so the IManager-backed service owns the write\n\t\t// path (NO cache table).\n\t\tsharesUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/shares`\n\t\t},\n\n\t\tshareableFilesUrl() {\n\t\t\treturn `${this.apiBase}/integrations/shares/files/${this.register}/${this.schema}/${this.objectId}`\n\t\t},\n\n\t\tasync openCreateDialog() {\n\t\t\tthis.showCreate = true\n\t\t\tawait this.fetchShareableFiles()\n\t\t},\n\n\t\tasync fetchShareableFiles() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.filesLoading = true\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.shareableFilesUrl()), { 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.shareableFiles = data.results || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else {\n\t\t\t\t\tthis.shareableFiles = []\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnSharesTab] failed to fetch shareable files', err)\n\t\t\t\tthis.shareableFiles = []\n\t\t\t} finally {\n\t\t\t\tthis.filesLoading = false\n\t\t\t}\n\t\t},\n\n\t\tsearchPrincipals({ shareType, query }) {\n\t\t\t// Principal lookup is host-supplied: the host app wires NC's\n\t\t\t// share-search OCS endpoint. Emit upward so the host can\n\t\t\t// populate `principals`; default is a no-op empty list.\n\t\t\tthis.$emit('search-principals', { shareType, query })\n\t\t},\n\n\t\tasync createShare(payload) {\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.sharesUrl()), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.showCreate = false\n\t\t\t\t\tthis.$emit('share-created', payload)\n\t\t\t\t\tawait this.fetchShares()\n\t\t\t\t} else {\n\t\t\t\t\tconst data = await response.json().catch(() => ({}))\n\t\t\t\t\tthis.error = data.error || t('nextcloud-vue', 'Could not create share.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnSharesTab] failed to create share', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create share.')\n\t\t\t}\n\t\t},\n\n\t\tshareKey(share) {\n\t\t\treturn share.id ?? share.shareId ?? share.token ?? ''\n\t\t},\n\n\t\tshareType(share) {\n\t\t\tconst value = share.shareType ?? share.share_type ?? share.type\n\t\t\treturn Number.isFinite(Number(value)) ? Number(value) : -1\n\t\t},\n\n\t\tshareTarget(share) {\n\t\t\tconst type = this.shareType(share)\n\t\t\tif (type === SHARE_TYPE_LINK) {\n\t\t\t\t// Public links surface as a generic \"Share link\" label,\n\t\t\t\t// mirroring NC core's share list (the raw token is never\n\t\t\t\t// shown as a recipient name).\n\t\t\t\treturn share.shareWithDisplayname || share.label || t('nextcloud-vue', 'Share link')\n\t\t\t}\n\t\t\treturn share.shareWithDisplayname || share.shareWith || share.share_with || share.targetDisplayName || ''\n\t\t},\n\n\t\t// Picks the recipient/share-type icon component for a row,\n\t\t// mirroring NC core's share list (user / group / link / email /\n\t\t// federated).\n\t\ticonFor(type) {\n\t\t\tif (type === SHARE_TYPE_GROUP) {\n\t\t\t\treturn 'AccountGroupOutline'\n\t\t\t}\n\t\t\tif (type === SHARE_TYPE_LINK) {\n\t\t\t\treturn 'LinkVariant'\n\t\t\t}\n\t\t\tif (type === SHARE_TYPE_EMAIL) {\n\t\t\t\treturn 'EmailOutline'\n\t\t\t}\n\t\t\tif (type === SHARE_TYPE_REMOTE || type === SHARE_TYPE_REMOTE_GROUP) {\n\t\t\t\treturn 'Earth'\n\t\t\t}\n\t\t\treturn 'AccountOutline'\n\t\t},\n\n\t\tpermissionLabel(share) {\n\t\t\treturn this.permissionBadges(share).map((b) => b.label).join(' · ')\n\t\t},\n\n\t\t// Decomposes the NC permission bitmask into the badge set the real\n\t\t// share UI shows: \"Can edit\" / \"Read only\" (mutually exclusive),\n\t\t// plus \"Can reshare\" when the share permission bit is set.\n\t\tpermissionBadges(share) {\n\t\t\tconst perms = Number(share.permissions ?? share.permission ?? 0)\n\t\t\tconst canEdit = (perms & PERMISSION_UPDATE) === PERMISSION_UPDATE\n\t\t\t\t|| (perms & PERMISSION_CREATE) === PERMISSION_CREATE\n\t\t\t\t|| (perms & PERMISSION_DELETE) === PERMISSION_DELETE\n\t\t\tconst canRead = (perms & PERMISSION_READ) === PERMISSION_READ\n\t\t\tconst badges = []\n\t\t\tif (canEdit) {\n\t\t\t\tbadges.push({ key: 'edit', variant: 'success', label: t('nextcloud-vue', 'Can edit') })\n\t\t\t} else if (canRead) {\n\t\t\t\tbadges.push({ key: 'read', variant: 'default', label: t('nextcloud-vue', 'Read only') })\n\t\t\t}\n\t\t\tif ((perms & PERMISSION_SHARE) === PERMISSION_SHARE) {\n\t\t\t\tbadges.push({ key: 'reshare', variant: 'info', label: t('nextcloud-vue', 'Can reshare') })\n\t\t\t}\n\t\t\treturn badges\n\t\t},\n\n\t\t// Returns the expiry as epoch-ms for <NcDateTime>, or 0 when there\n\t\t// is no (valid) expiry so the template can `v-if` it away.\n\t\tshareExpiryMs(share) {\n\t\t\tconst value = share.expiration ?? share.expirationDate ?? share.expiry ?? null\n\t\t\tif (!value) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\tconst date = new Date(value)\n\t\t\tconst ms = date.getTime()\n\t\t\treturn Number.isNaN(ms) ? 0 : ms\n\t\t},\n\n\t\tisPasswordProtected(share) {\n\t\t\treturn Boolean(share.passwordProtected ?? share.hasPassword ?? share.password)\n\t\t},\n\n\t\tcanRevoke(share) {\n\t\t\t// The current-user-can-revoke flag is provider-supplied. When\n\t\t\t// absent we default to true so the action stays usable; the\n\t\t\t// backend rejects unauthorized revoke attempts.\n\t\t\treturn share.canRevoke !== false\n\t\t},\n\n\t\tasync fetchShares() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.shares = rows\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.shares = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.shares = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load shares.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnSharesTab] failed to fetch shares', err)\n\t\t\t\tthis.shares = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load shares.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync revoke(share) {\n\t\t\tconst id = this.shareKey(share)\n\t\t\tif (!id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(id)}`), {\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.shares = this.shares.filter((row) => this.shareKey(row) !== id)\n\t\t\t\t\tthis.$emit('share-revoked', { id, share })\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not revoke share.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnSharesTab] failed to revoke share', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not revoke share.')\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-shares-tab__banner {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 10px;\n\tmargin-bottom: 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-shares-tab__toolbar {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\tmargin-bottom: 10px;\n}\n\n.cn-shares-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-shares-tab__empty {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 16px 8px;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n}\n\n.cn-shares-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-shares-tab__group {\n\tmargin-bottom: 12px;\n}\n\n.cn-shares-tab__group-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 0;\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-shares-tab__group-label {\n\tflex: 1;\n}\n\n.cn-shares-tab__group-count {\n\tfont-weight: normal;\n\tfont-size: 0.85em;\n\tpadding: 1px 6px;\n\tborder-radius: 9px;\n\tbackground: var(--color-background-hover);\n}\n\n.cn-shares-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* Share-type icon tile, mirroring the NcAvatar slot in NC's share list. */\n.cn-shares-tab__avatar {\n\tposition: relative;\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 34px;\n\theight: 34px;\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n}\n\n.cn-shares-tab__avatar-lock {\n\tposition: absolute;\n\tright: -2px;\n\tbottom: -2px;\n\tcolor: var(--color-warning, #e9a40f);\n\tbackground: var(--color-main-background);\n\tborder-radius: 50%;\n}\n\n.cn-shares-tab__permissions {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tflex-wrap: wrap;\n\tgap: 4px;\n}\n\n.cn-shares-tab__badge {\n\twhite-space: nowrap;\n}\n\n.cn-shares-tab__expiry {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n\n.cn-shares-tab__footer {\n\tmargin-top: 12px;\n\tdisplay: flex;\n\tjustify-content: flex-end;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_createVNode","_createTextVNode","_createBlock","_toDisplayString","_resolveDynamicComponent","_renderList","_Fragment"],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ import { NcAvatar, NcCounterBubble, NcLoadingIcon } from '@nextcloud/vue';
|
|
|
3
3
|
import ChatOutline from 'vue-material-design-icons/ChatOutline.vue';
|
|
4
4
|
import __sfc_main from '../../../components/CnDetailCard/CnDetailCard.vue.js';
|
|
5
5
|
import { stripMarker } from '../../utils/marker.js';
|
|
6
|
+
import { safeHref } from '../../../utils/safeHref.js';
|
|
6
7
|
import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
|
|
7
8
|
|
|
8
9
|
const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'];
|
|
@@ -143,8 +144,9 @@ var script = {
|
|
|
143
144
|
return stripMarker(raw) || String(this.roomKey(room))
|
|
144
145
|
},
|
|
145
146
|
|
|
147
|
+
// A provider URL is used only when it is a safe scheme; else the app link.
|
|
146
148
|
roomUrl(room) {
|
|
147
|
-
if (room.url) {
|
|
149
|
+
if (room.url && safeHref(room.url) !== '#') {
|
|
148
150
|
return room.url
|
|
149
151
|
}
|
|
150
152
|
const token = room.token ?? room.id ?? '';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnTalkCard.vue2.js","sources":["../../../../../src/integrations/builtin/talk/CnTalkCard.vue"],"sourcesContent":["<!--\n CnTalkCard — bespoke surface-aware widget for the `talk` integration.\n\n Replaces the generic CnIntegrationCard for the `talk` leaf. Branches on\n `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N unread across M\n conversations\" (per Talk integration-talk spec — unread is the\n single glanceable signal); secondary line names the most recent\n conversation.\n - detail-page : compact list of linked\n conversations with last-message preview + unread badge per row.\n - single-entity : chip with conversation name + an\n unread dot when applicable (referenceType: 'talk').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnTalkTab; for `single-entity` the optional `value` prop addresses a\n single conversation by token (matching CnIntegrationCard's fetchSingle\n contract).\n\n See `openregister/openspec/changes/integration-talk/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback).\n-->\n<template>\n\t<CnDetailCard :title=\"cardTitle\" :icon=\"cardIcon\" :collapsible=\"collapsible\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- single-entity surface: chip -->\n\t\t<template v-else-if=\"surface === 'single-entity'\">\n\t\t\t<span v-if=\"entity\" class=\"cn-talk-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<ChatOutline :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"chipUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ roomTitle(entity) }}</a>\n\t\t\t\t<span v-if=\"hasUnread(entity)\" class=\"cn-talk-card__chip-badge\" :aria-label=\"unreadAriaLabel(entity)\">\n\t\t\t\t\t{{ formatUnread(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-talk-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + most-recent -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div v-if=\"degraded\" class=\"cn-talk-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"rooms.length === 0\" class=\"cn-talk-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-talk-card__headline\">\n\t\t\t\t<div class=\"cn-talk-card__headline-line\">\n\t\t\t\t\t<strong>{{ unreadHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-talk-card__headline-recent\">\n\t\t\t\t\t<ChatOutline :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"roomUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ roomTitle(mostRecent) }}</a>\n\t\t\t\t\t<span v-if=\"roomPreview(mostRecent)\" class=\"cn-talk-card__headline-preview\">\n\t\t\t\t\t\t· {{ roomPreview(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: compact list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-talk-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"rooms.length === 0\" class=\"cn-talk-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-talk-card__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"room in displayedRooms\"\n\t\t\t\t\t:key=\"roomKey(room)\"\n\t\t\t\t\tclass=\"cn-talk-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-talk-card__row--unread': hasUnread(room) }\">\n\t\t\t\t\t<div class=\"cn-talk-card__row-icon\">\n\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\t:displayName=\"roomTitle(room)\"\n\t\t\t\t\t\t\t:size=\"32\"\n\t\t\t\t\t\t\t:isNoUser=\"true\"\n\t\t\t\t\t\t\thideStatus />\n\t\t\t\t\t\t<NcCounterBubble\n\t\t\t\t\t\t\tv-if=\"hasUnread(room)\"\n\t\t\t\t\t\t\tclass=\"cn-talk-card__badge\"\n\t\t\t\t\t\t\ttype=\"highlighted\"\n\t\t\t\t\t\t\t:aria-label=\"unreadAriaLabel(room)\">\n\t\t\t\t\t\t\t{{ formatUnread(room) }}\n\t\t\t\t\t\t</NcCounterBubble>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"cn-talk-card__row-main\">\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"roomUrl(room)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-talk-card__title\">{{ roomTitle(room) }}</a>\n\t\t\t\t\t\t<span v-if=\"roomPreview(room)\" class=\"cn-talk-card__subtitle\">{{ roomPreview(room) }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcCounterBubble, NcLoadingIcon } from '@nextcloud/vue'\nimport ChatOutline from 'vue-material-design-icons/ChatOutline.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\nconst COMPACT_LIMIT = 5\n\n/**\n * CnTalkCard — bespoke surface-aware widget for the `talk` integration.\n *\n * Renders Talk-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnTalkCard',\n\n\tcomponents: { CnDetailCard, NcAvatar, NcCounterBubble, NcLoadingIcon, ChatOutline },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'talk'`). */\n\t\tintegrationId: { type: String, default: 'talk' },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, required: true },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, required: true },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** Rendering surface (AD-19). */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (s) => VALID_SURFACES.includes(s),\n\t\t},\n\n\t\t/** Optional single-entity reference (room token). */\n\t\tvalue: { type: String, default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Chat') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => ChatOutline },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Whether the card body is collapsible. */\n\t\tcollapsible: { type: Boolean, default: true },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No conversations linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Talk is currently unavailable.') },\n\t\t/** URL of the NC Talk app entry. */\n\t\ttalkAppUrl: { type: String, default: '/index.php/apps/spreed' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trooms: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardTitle() {\n\t\t\treturn this.title || this.integrationId\n\t\t},\n\n\t\tcardIcon() {\n\t\t\treturn this.icon\n\t\t},\n\n\t\tdisplayedRooms() {\n\t\t\tif (this.surface === 'user-dashboard' || this.surface === 'app-dashboard') {\n\t\t\t\treturn this.rooms.slice(0, COMPACT_LIMIT)\n\t\t\t}\n\t\t\treturn this.rooms\n\t\t},\n\n\t\ttotalUnread() {\n\t\t\treturn this.rooms.reduce((sum, room) => {\n\t\t\t\tconst count = Number(room.unreadMessages ?? room.unread ?? 0)\n\t\t\t\treturn sum + (Number.isFinite(count) && count > 0 ? count : 0)\n\t\t\t}, 0)\n\t\t},\n\n\t\tunreadHeadline() {\n\t\t\tconst unread = this.totalUnread\n\t\t\tconst total = this.rooms.length\n\t\t\tif (unread === 0) {\n\t\t\t\t// \"M conversations\" via translatePlural.\n\t\t\t\treturn n('nextcloud-vue', '{count} conversation', '{count} conversations', total, { count: total })\n\t\t\t}\n\t\t\t// Pre-format the two plural fragments, then compose so we don't\n\t\t\t// trip the @nextcloud/l10n single-placeholder constraint.\n\t\t\tconst unreadFragment = n('nextcloud-vue', '{count} unread message', '{count} unread messages', unread, { count: unread })\n\t\t\tconst acrossFragment = n('nextcloud-vue', 'across {count} conversation', 'across {count} conversations', total, { count: total })\n\t\t\treturn `${unreadFragment} ${acrossFragment}`\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.rooms.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Sort defensive copy by lastActivity desc.\n\t\t\tconst sorted = [...this.rooms].sort((a, b) => {\n\t\t\t\tconst ta = Number(a.lastActivity ?? 0)\n\t\t\t\tconst tb = Number(b.lastActivity ?? 0)\n\t\t\t\treturn tb - ta\n\t\t\t})\n\t\t\treturn sorted[0]\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.fetch()\n\t\t} },\n\n\t\tsurface() {\n\t\t\tthis.fetch()\n\t\t},\n\n\t\tvalue() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\troomKey(room) {\n\t\t\treturn room.id ?? room.token ?? room.reference ?? ''\n\t\t},\n\n\t\troomTitle(room) {\n\t\t\tconst raw = room.title ?? room.displayName ?? room.name ?? this.roomKey(room)\n\t\t\treturn stripMarker(raw) || String(this.roomKey(room))\n\t\t},\n\n\t\troomUrl(room) {\n\t\t\tif (room.url) {\n\t\t\t\treturn room.url\n\t\t\t}\n\t\t\tconst token = room.token ?? room.id ?? ''\n\t\t\treturn token ? `/index.php/call/${token}` : this.talkAppUrl\n\t\t},\n\n\t\troomPreview(room) {\n\t\t\tconst msg = room.lastMessage\n\t\t\tif (msg && typeof msg === 'object') {\n\t\t\t\treturn msg.message ?? msg.text ?? ''\n\t\t\t}\n\t\t\treturn room.lastMessageText ?? room.preview ?? ''\n\t\t},\n\n\t\thasUnread(room) {\n\t\t\tconst count = Number(room.unreadMessages ?? room.unread ?? 0)\n\t\t\treturn Number.isFinite(count) && count > 0\n\t\t},\n\n\t\tformatUnread(room) {\n\t\t\tconst count = Number(room.unreadMessages ?? room.unread ?? 0)\n\t\t\tif (!Number.isFinite(count) || count <= 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn count > 99 ? '99+' : String(count)\n\t\t},\n\n\t\tunreadAriaLabel(room) {\n\t\t\tconst count = Number(room.unreadMessages ?? room.unread ?? 0)\n\t\t\treturn t('nextcloud-vue', '{n} unread', { n: count })\n\t\t},\n\n\t\tchipSubtitle(room) {\n\t\t\tconst preview = this.roomPreview(room)\n\t\t\treturn preview || this.roomTitle(room)\n\t\t},\n\n\t\tchipUrl(room) {\n\t\t\treturn this.roomUrl(room)\n\t\t},\n\n\t\tfetch() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchList()\n\t\t},\n\n\t\tasync fetchList() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { 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.rooms = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.rooms = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rooms = []\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnTalkCard] failed to fetch rooms', err)\n\t\t\t\tthis.rooms = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync fetchSingle() {\n\t\t\tif (!this.value || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.entity = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.entity = await response.json()\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnTalkCard] failed to fetch single conversation', err)\n\t\t\t\tthis.entity = null\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-talk-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-talk-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-talk-card__headline-line {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-talk-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-talk-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-talk-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-talk-card__headline-preview {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-talk-card__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-hover);\n\tfont-size: 0.9em;\n\tmax-width: 100%;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n/* stylelint-disable-next-line no-descending-specificity */\n.cn-talk-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-talk-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-talk-card__chip-badge {\n\tdisplay: inline-block;\n\tmin-width: 16px;\n\theight: 16px;\n\tpadding: 0 5px;\n\tborder-radius: 8px;\n\tbackground: var(--color-primary-element, #4376FC);\n\tcolor: var(--color-primary-element-text, #ffffff);\n\tfont-size: 0.7em;\n\tfont-weight: bold;\n\tline-height: 16px;\n\ttext-align: center;\n}\n\n.cn-talk-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-talk-card__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 6px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-talk-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-talk-card__row-icon {\n\tposition: relative;\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-talk-card__badge {\n\tposition: absolute;\n\ttop: -4px;\n\tright: -8px;\n\tmin-width: 16px;\n\theight: 16px;\n\tpadding: 0 5px;\n\tborder-radius: 8px;\n\tbackground: var(--color-primary-element, #4376FC);\n\tcolor: var(--color-primary-element-text, #ffffff);\n\tfont-size: 0.7em;\n\tfont-weight: bold;\n\tline-height: 16px;\n\ttext-align: center;\n}\n\n.cn-talk-card__row-main {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-talk-card__title {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-talk-card__row--unread .cn-talk-card__title {\n\tfont-weight: 600;\n}\n\na.cn-talk-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-talk-card__subtitle {\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["CnDetailCard","t","n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnTalkCard.vue2.js","sources":["../../../../../src/integrations/builtin/talk/CnTalkCard.vue"],"sourcesContent":["<!--\n CnTalkCard — bespoke surface-aware widget for the `talk` integration.\n\n Replaces the generic CnIntegrationCard for the `talk` leaf. Branches on\n `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N unread across M\n conversations\" (per Talk integration-talk spec — unread is the\n single glanceable signal); secondary line names the most recent\n conversation.\n - detail-page : compact list of linked\n conversations with last-message preview + unread badge per row.\n - single-entity : chip with conversation name + an\n unread dot when applicable (referenceType: 'talk').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnTalkTab; for `single-entity` the optional `value` prop addresses a\n single conversation by token (matching CnIntegrationCard's fetchSingle\n contract).\n\n See `openregister/openspec/changes/integration-talk/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback).\n-->\n<template>\n\t<CnDetailCard :title=\"cardTitle\" :icon=\"cardIcon\" :collapsible=\"collapsible\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- single-entity surface: chip -->\n\t\t<template v-else-if=\"surface === 'single-entity'\">\n\t\t\t<span v-if=\"entity\" class=\"cn-talk-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<ChatOutline :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"chipUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ roomTitle(entity) }}</a>\n\t\t\t\t<span v-if=\"hasUnread(entity)\" class=\"cn-talk-card__chip-badge\" :aria-label=\"unreadAriaLabel(entity)\">\n\t\t\t\t\t{{ formatUnread(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-talk-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + most-recent -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div v-if=\"degraded\" class=\"cn-talk-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"rooms.length === 0\" class=\"cn-talk-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-talk-card__headline\">\n\t\t\t\t<div class=\"cn-talk-card__headline-line\">\n\t\t\t\t\t<strong>{{ unreadHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-talk-card__headline-recent\">\n\t\t\t\t\t<ChatOutline :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"roomUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ roomTitle(mostRecent) }}</a>\n\t\t\t\t\t<span v-if=\"roomPreview(mostRecent)\" class=\"cn-talk-card__headline-preview\">\n\t\t\t\t\t\t· {{ roomPreview(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: compact list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-talk-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"rooms.length === 0\" class=\"cn-talk-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-talk-card__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"room in displayedRooms\"\n\t\t\t\t\t:key=\"roomKey(room)\"\n\t\t\t\t\tclass=\"cn-talk-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-talk-card__row--unread': hasUnread(room) }\">\n\t\t\t\t\t<div class=\"cn-talk-card__row-icon\">\n\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\t:displayName=\"roomTitle(room)\"\n\t\t\t\t\t\t\t:size=\"32\"\n\t\t\t\t\t\t\t:isNoUser=\"true\"\n\t\t\t\t\t\t\thideStatus />\n\t\t\t\t\t\t<NcCounterBubble\n\t\t\t\t\t\t\tv-if=\"hasUnread(room)\"\n\t\t\t\t\t\t\tclass=\"cn-talk-card__badge\"\n\t\t\t\t\t\t\ttype=\"highlighted\"\n\t\t\t\t\t\t\t:aria-label=\"unreadAriaLabel(room)\">\n\t\t\t\t\t\t\t{{ formatUnread(room) }}\n\t\t\t\t\t\t</NcCounterBubble>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"cn-talk-card__row-main\">\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"roomUrl(room)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-talk-card__title\">{{ roomTitle(room) }}</a>\n\t\t\t\t\t\t<span v-if=\"roomPreview(room)\" class=\"cn-talk-card__subtitle\">{{ roomPreview(room) }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcCounterBubble, NcLoadingIcon } from '@nextcloud/vue'\nimport ChatOutline from 'vue-material-design-icons/ChatOutline.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\nconst COMPACT_LIMIT = 5\n\n/**\n * CnTalkCard — bespoke surface-aware widget for the `talk` integration.\n *\n * Renders Talk-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnTalkCard',\n\n\tcomponents: { CnDetailCard, NcAvatar, NcCounterBubble, NcLoadingIcon, ChatOutline },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'talk'`). */\n\t\tintegrationId: { type: String, default: 'talk' },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, required: true },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, required: true },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** Rendering surface (AD-19). */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (s) => VALID_SURFACES.includes(s),\n\t\t},\n\n\t\t/** Optional single-entity reference (room token). */\n\t\tvalue: { type: String, default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Chat') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => ChatOutline },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Whether the card body is collapsible. */\n\t\tcollapsible: { type: Boolean, default: true },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No conversations linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Talk is currently unavailable.') },\n\t\t/** URL of the NC Talk app entry. */\n\t\ttalkAppUrl: { type: String, default: '/index.php/apps/spreed' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trooms: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardTitle() {\n\t\t\treturn this.title || this.integrationId\n\t\t},\n\n\t\tcardIcon() {\n\t\t\treturn this.icon\n\t\t},\n\n\t\tdisplayedRooms() {\n\t\t\tif (this.surface === 'user-dashboard' || this.surface === 'app-dashboard') {\n\t\t\t\treturn this.rooms.slice(0, COMPACT_LIMIT)\n\t\t\t}\n\t\t\treturn this.rooms\n\t\t},\n\n\t\ttotalUnread() {\n\t\t\treturn this.rooms.reduce((sum, room) => {\n\t\t\t\tconst count = Number(room.unreadMessages ?? room.unread ?? 0)\n\t\t\t\treturn sum + (Number.isFinite(count) && count > 0 ? count : 0)\n\t\t\t}, 0)\n\t\t},\n\n\t\tunreadHeadline() {\n\t\t\tconst unread = this.totalUnread\n\t\t\tconst total = this.rooms.length\n\t\t\tif (unread === 0) {\n\t\t\t\t// \"M conversations\" via translatePlural.\n\t\t\t\treturn n('nextcloud-vue', '{count} conversation', '{count} conversations', total, { count: total })\n\t\t\t}\n\t\t\t// Pre-format the two plural fragments, then compose so we don't\n\t\t\t// trip the @nextcloud/l10n single-placeholder constraint.\n\t\t\tconst unreadFragment = n('nextcloud-vue', '{count} unread message', '{count} unread messages', unread, { count: unread })\n\t\t\tconst acrossFragment = n('nextcloud-vue', 'across {count} conversation', 'across {count} conversations', total, { count: total })\n\t\t\treturn `${unreadFragment} ${acrossFragment}`\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.rooms.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Sort defensive copy by lastActivity desc.\n\t\t\tconst sorted = [...this.rooms].sort((a, b) => {\n\t\t\t\tconst ta = Number(a.lastActivity ?? 0)\n\t\t\t\tconst tb = Number(b.lastActivity ?? 0)\n\t\t\t\treturn tb - ta\n\t\t\t})\n\t\t\treturn sorted[0]\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.fetch()\n\t\t} },\n\n\t\tsurface() {\n\t\t\tthis.fetch()\n\t\t},\n\n\t\tvalue() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\troomKey(room) {\n\t\t\treturn room.id ?? room.token ?? room.reference ?? ''\n\t\t},\n\n\t\troomTitle(room) {\n\t\t\tconst raw = room.title ?? room.displayName ?? room.name ?? this.roomKey(room)\n\t\t\treturn stripMarker(raw) || String(this.roomKey(room))\n\t\t},\n\n\t\t// A provider URL is used only when it is a safe scheme; else the app link.\n\t\troomUrl(room) {\n\t\t\tif (room.url && safeHref(room.url) !== '#') {\n\t\t\t\treturn room.url\n\t\t\t}\n\t\t\tconst token = room.token ?? room.id ?? ''\n\t\t\treturn token ? `/index.php/call/${token}` : this.talkAppUrl\n\t\t},\n\n\t\troomPreview(room) {\n\t\t\tconst msg = room.lastMessage\n\t\t\tif (msg && typeof msg === 'object') {\n\t\t\t\treturn msg.message ?? msg.text ?? ''\n\t\t\t}\n\t\t\treturn room.lastMessageText ?? room.preview ?? ''\n\t\t},\n\n\t\thasUnread(room) {\n\t\t\tconst count = Number(room.unreadMessages ?? room.unread ?? 0)\n\t\t\treturn Number.isFinite(count) && count > 0\n\t\t},\n\n\t\tformatUnread(room) {\n\t\t\tconst count = Number(room.unreadMessages ?? room.unread ?? 0)\n\t\t\tif (!Number.isFinite(count) || count <= 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn count > 99 ? '99+' : String(count)\n\t\t},\n\n\t\tunreadAriaLabel(room) {\n\t\t\tconst count = Number(room.unreadMessages ?? room.unread ?? 0)\n\t\t\treturn t('nextcloud-vue', '{n} unread', { n: count })\n\t\t},\n\n\t\tchipSubtitle(room) {\n\t\t\tconst preview = this.roomPreview(room)\n\t\t\treturn preview || this.roomTitle(room)\n\t\t},\n\n\t\tchipUrl(room) {\n\t\t\treturn this.roomUrl(room)\n\t\t},\n\n\t\tfetch() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchList()\n\t\t},\n\n\t\tasync fetchList() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { 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.rooms = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.rooms = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rooms = []\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnTalkCard] failed to fetch rooms', err)\n\t\t\t\tthis.rooms = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync fetchSingle() {\n\t\t\tif (!this.value || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.entity = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.entity = await response.json()\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnTalkCard] failed to fetch single conversation', err)\n\t\t\t\tthis.entity = null\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-talk-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-talk-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-talk-card__headline-line {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-talk-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-talk-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-talk-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-talk-card__headline-preview {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-talk-card__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-hover);\n\tfont-size: 0.9em;\n\tmax-width: 100%;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n/* stylelint-disable-next-line no-descending-specificity */\n.cn-talk-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-talk-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-talk-card__chip-badge {\n\tdisplay: inline-block;\n\tmin-width: 16px;\n\theight: 16px;\n\tpadding: 0 5px;\n\tborder-radius: 8px;\n\tbackground: var(--color-primary-element, #4376FC);\n\tcolor: var(--color-primary-element-text, #ffffff);\n\tfont-size: 0.7em;\n\tfont-weight: bold;\n\tline-height: 16px;\n\ttext-align: center;\n}\n\n.cn-talk-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-talk-card__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 6px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-talk-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-talk-card__row-icon {\n\tposition: relative;\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-talk-card__badge {\n\tposition: absolute;\n\ttop: -4px;\n\tright: -8px;\n\tmin-width: 16px;\n\theight: 16px;\n\tpadding: 0 5px;\n\tborder-radius: 8px;\n\tbackground: var(--color-primary-element, #4376FC);\n\tcolor: var(--color-primary-element-text, #ffffff);\n\tfont-size: 0.7em;\n\tfont-weight: bold;\n\tline-height: 16px;\n\ttext-align: center;\n}\n\n.cn-talk-card__row-main {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-talk-card__title {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-talk-card__row--unread .cn-talk-card__title {\n\tfont-weight: 600;\n}\n\na.cn-talk-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-talk-card__subtitle {\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["CnDetailCard","t","n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnTalkCard.vue3.js","sources":["../../../../../src/integrations/builtin/talk/CnTalkCard.vue?vue&type=template&id=5f0c0447&lang.js"],"sourcesContent":["<!--\n CnTalkCard — bespoke surface-aware widget for the `talk` integration.\n\n Replaces the generic CnIntegrationCard for the `talk` leaf. Branches on\n `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N unread across M\n conversations\" (per Talk integration-talk spec — unread is the\n single glanceable signal); secondary line names the most recent\n conversation.\n - detail-page : compact list of linked\n conversations with last-message preview + unread badge per row.\n - single-entity : chip with conversation name + an\n unread dot when applicable (referenceType: 'talk').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnTalkTab; for `single-entity` the optional `value` prop addresses a\n single conversation by token (matching CnIntegrationCard's fetchSingle\n contract).\n\n See `openregister/openspec/changes/integration-talk/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback).\n-->\n<template>\n\t<CnDetailCard :title=\"cardTitle\" :icon=\"cardIcon\" :collapsible=\"collapsible\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- single-entity surface: chip -->\n\t\t<template v-else-if=\"surface === 'single-entity'\">\n\t\t\t<span v-if=\"entity\" class=\"cn-talk-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<ChatOutline :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"chipUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ roomTitle(entity) }}</a>\n\t\t\t\t<span v-if=\"hasUnread(entity)\" class=\"cn-talk-card__chip-badge\" :aria-label=\"unreadAriaLabel(entity)\">\n\t\t\t\t\t{{ formatUnread(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-talk-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + most-recent -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div v-if=\"degraded\" class=\"cn-talk-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"rooms.length === 0\" class=\"cn-talk-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-talk-card__headline\">\n\t\t\t\t<div class=\"cn-talk-card__headline-line\">\n\t\t\t\t\t<strong>{{ unreadHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-talk-card__headline-recent\">\n\t\t\t\t\t<ChatOutline :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"roomUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ roomTitle(mostRecent) }}</a>\n\t\t\t\t\t<span v-if=\"roomPreview(mostRecent)\" class=\"cn-talk-card__headline-preview\">\n\t\t\t\t\t\t· {{ roomPreview(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: compact list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-talk-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"rooms.length === 0\" class=\"cn-talk-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-talk-card__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"room in displayedRooms\"\n\t\t\t\t\t:key=\"roomKey(room)\"\n\t\t\t\t\tclass=\"cn-talk-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-talk-card__row--unread': hasUnread(room) }\">\n\t\t\t\t\t<div class=\"cn-talk-card__row-icon\">\n\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\t:displayName=\"roomTitle(room)\"\n\t\t\t\t\t\t\t:size=\"32\"\n\t\t\t\t\t\t\t:isNoUser=\"true\"\n\t\t\t\t\t\t\thideStatus />\n\t\t\t\t\t\t<NcCounterBubble\n\t\t\t\t\t\t\tv-if=\"hasUnread(room)\"\n\t\t\t\t\t\t\tclass=\"cn-talk-card__badge\"\n\t\t\t\t\t\t\ttype=\"highlighted\"\n\t\t\t\t\t\t\t:aria-label=\"unreadAriaLabel(room)\">\n\t\t\t\t\t\t\t{{ formatUnread(room) }}\n\t\t\t\t\t\t</NcCounterBubble>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"cn-talk-card__row-main\">\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"roomUrl(room)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-talk-card__title\">{{ roomTitle(room) }}</a>\n\t\t\t\t\t\t<span v-if=\"roomPreview(room)\" class=\"cn-talk-card__subtitle\">{{ roomPreview(room) }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcCounterBubble, NcLoadingIcon } from '@nextcloud/vue'\nimport ChatOutline from 'vue-material-design-icons/ChatOutline.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\nconst COMPACT_LIMIT = 5\n\n/**\n * CnTalkCard — bespoke surface-aware widget for the `talk` integration.\n *\n * Renders Talk-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnTalkCard',\n\n\tcomponents: { CnDetailCard, NcAvatar, NcCounterBubble, NcLoadingIcon, ChatOutline },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'talk'`). */\n\t\tintegrationId: { type: String, default: 'talk' },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, required: true },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, required: true },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** Rendering surface (AD-19). */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (s) => VALID_SURFACES.includes(s),\n\t\t},\n\n\t\t/** Optional single-entity reference (room token). */\n\t\tvalue: { type: String, default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Chat') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => ChatOutline },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Whether the card body is collapsible. */\n\t\tcollapsible: { type: Boolean, default: true },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No conversations linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Talk is currently unavailable.') },\n\t\t/** URL of the NC Talk app entry. */\n\t\ttalkAppUrl: { type: String, default: '/index.php/apps/spreed' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trooms: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardTitle() {\n\t\t\treturn this.title || this.integrationId\n\t\t},\n\n\t\tcardIcon() {\n\t\t\treturn this.icon\n\t\t},\n\n\t\tdisplayedRooms() {\n\t\t\tif (this.surface === 'user-dashboard' || this.surface === 'app-dashboard') {\n\t\t\t\treturn this.rooms.slice(0, COMPACT_LIMIT)\n\t\t\t}\n\t\t\treturn this.rooms\n\t\t},\n\n\t\ttotalUnread() {\n\t\t\treturn this.rooms.reduce((sum, room) => {\n\t\t\t\tconst count = Number(room.unreadMessages ?? room.unread ?? 0)\n\t\t\t\treturn sum + (Number.isFinite(count) && count > 0 ? count : 0)\n\t\t\t}, 0)\n\t\t},\n\n\t\tunreadHeadline() {\n\t\t\tconst unread = this.totalUnread\n\t\t\tconst total = this.rooms.length\n\t\t\tif (unread === 0) {\n\t\t\t\t// \"M conversations\" via translatePlural.\n\t\t\t\treturn n('nextcloud-vue', '{count} conversation', '{count} conversations', total, { count: total })\n\t\t\t}\n\t\t\t// Pre-format the two plural fragments, then compose so we don't\n\t\t\t// trip the @nextcloud/l10n single-placeholder constraint.\n\t\t\tconst unreadFragment = n('nextcloud-vue', '{count} unread message', '{count} unread messages', unread, { count: unread })\n\t\t\tconst acrossFragment = n('nextcloud-vue', 'across {count} conversation', 'across {count} conversations', total, { count: total })\n\t\t\treturn `${unreadFragment} ${acrossFragment}`\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.rooms.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Sort defensive copy by lastActivity desc.\n\t\t\tconst sorted = [...this.rooms].sort((a, b) => {\n\t\t\t\tconst ta = Number(a.lastActivity ?? 0)\n\t\t\t\tconst tb = Number(b.lastActivity ?? 0)\n\t\t\t\treturn tb - ta\n\t\t\t})\n\t\t\treturn sorted[0]\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.fetch()\n\t\t} },\n\n\t\tsurface() {\n\t\t\tthis.fetch()\n\t\t},\n\n\t\tvalue() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\troomKey(room) {\n\t\t\treturn room.id ?? room.token ?? room.reference ?? ''\n\t\t},\n\n\t\troomTitle(room) {\n\t\t\tconst raw = room.title ?? room.displayName ?? room.name ?? this.roomKey(room)\n\t\t\treturn stripMarker(raw) || String(this.roomKey(room))\n\t\t},\n\n\t\troomUrl(room) {\n\t\t\tif (room.url) {\n\t\t\t\treturn room.url\n\t\t\t}\n\t\t\tconst token = room.token ?? room.id ?? ''\n\t\t\treturn token ? `/index.php/call/${token}` : this.talkAppUrl\n\t\t},\n\n\t\troomPreview(room) {\n\t\t\tconst msg = room.lastMessage\n\t\t\tif (msg && typeof msg === 'object') {\n\t\t\t\treturn msg.message ?? msg.text ?? ''\n\t\t\t}\n\t\t\treturn room.lastMessageText ?? room.preview ?? ''\n\t\t},\n\n\t\thasUnread(room) {\n\t\t\tconst count = Number(room.unreadMessages ?? room.unread ?? 0)\n\t\t\treturn Number.isFinite(count) && count > 0\n\t\t},\n\n\t\tformatUnread(room) {\n\t\t\tconst count = Number(room.unreadMessages ?? room.unread ?? 0)\n\t\t\tif (!Number.isFinite(count) || count <= 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn count > 99 ? '99+' : String(count)\n\t\t},\n\n\t\tunreadAriaLabel(room) {\n\t\t\tconst count = Number(room.unreadMessages ?? room.unread ?? 0)\n\t\t\treturn t('nextcloud-vue', '{n} unread', { n: count })\n\t\t},\n\n\t\tchipSubtitle(room) {\n\t\t\tconst preview = this.roomPreview(room)\n\t\t\treturn preview || this.roomTitle(room)\n\t\t},\n\n\t\tchipUrl(room) {\n\t\t\treturn this.roomUrl(room)\n\t\t},\n\n\t\tfetch() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchList()\n\t\t},\n\n\t\tasync fetchList() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { 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.rooms = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.rooms = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rooms = []\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnTalkCard] failed to fetch rooms', err)\n\t\t\t\tthis.rooms = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync fetchSingle() {\n\t\t\tif (!this.value || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.entity = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.entity = await response.json()\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnTalkCard] failed to fetch single conversation', err)\n\t\t\t\tthis.entity = null\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-talk-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-talk-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-talk-card__headline-line {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-talk-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-talk-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-talk-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-talk-card__headline-preview {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-talk-card__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-hover);\n\tfont-size: 0.9em;\n\tmax-width: 100%;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n/* stylelint-disable-next-line no-descending-specificity */\n.cn-talk-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-talk-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-talk-card__chip-badge {\n\tdisplay: inline-block;\n\tmin-width: 16px;\n\theight: 16px;\n\tpadding: 0 5px;\n\tborder-radius: 8px;\n\tbackground: var(--color-primary-element, #4376FC);\n\tcolor: var(--color-primary-element-text, #ffffff);\n\tfont-size: 0.7em;\n\tfont-weight: bold;\n\tline-height: 16px;\n\ttext-align: center;\n}\n\n.cn-talk-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-talk-card__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 6px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-talk-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-talk-card__row-icon {\n\tposition: relative;\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-talk-card__badge {\n\tposition: absolute;\n\ttop: -4px;\n\tright: -8px;\n\tmin-width: 16px;\n\theight: 16px;\n\tpadding: 0 5px;\n\tborder-radius: 8px;\n\tbackground: var(--color-primary-element, #4376FC);\n\tcolor: var(--color-primary-element-text, #ffffff);\n\tfont-size: 0.7em;\n\tfont-weight: bold;\n\tline-height: 16px;\n\ttext-align: center;\n}\n\n.cn-talk-card__row-main {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-talk-card__title {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-talk-card__row--unread .cn-talk-card__title {\n\tfont-weight: 600;\n}\n\na.cn-talk-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-talk-card__subtitle {\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["_createElementBlock","_Fragment","_createVNode","_createElementVNode","_toDisplayString","_openBlock"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnTalkCard.vue3.js","sources":["../../../../../src/integrations/builtin/talk/CnTalkCard.vue?vue&type=template&id=5f0c0447&lang.js"],"sourcesContent":["<!--\n CnTalkCard — bespoke surface-aware widget for the `talk` integration.\n\n Replaces the generic CnIntegrationCard for the `talk` leaf. Branches on\n `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N unread across M\n conversations\" (per Talk integration-talk spec — unread is the\n single glanceable signal); secondary line names the most recent\n conversation.\n - detail-page : compact list of linked\n conversations with last-message preview + unread badge per row.\n - single-entity : chip with conversation name + an\n unread dot when applicable (referenceType: 'talk').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnTalkTab; for `single-entity` the optional `value` prop addresses a\n single conversation by token (matching CnIntegrationCard's fetchSingle\n contract).\n\n See `openregister/openspec/changes/integration-talk/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback).\n-->\n<template>\n\t<CnDetailCard :title=\"cardTitle\" :icon=\"cardIcon\" :collapsible=\"collapsible\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- single-entity surface: chip -->\n\t\t<template v-else-if=\"surface === 'single-entity'\">\n\t\t\t<span v-if=\"entity\" class=\"cn-talk-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<ChatOutline :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"chipUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ roomTitle(entity) }}</a>\n\t\t\t\t<span v-if=\"hasUnread(entity)\" class=\"cn-talk-card__chip-badge\" :aria-label=\"unreadAriaLabel(entity)\">\n\t\t\t\t\t{{ formatUnread(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-talk-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + most-recent -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div v-if=\"degraded\" class=\"cn-talk-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"rooms.length === 0\" class=\"cn-talk-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-talk-card__headline\">\n\t\t\t\t<div class=\"cn-talk-card__headline-line\">\n\t\t\t\t\t<strong>{{ unreadHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-talk-card__headline-recent\">\n\t\t\t\t\t<ChatOutline :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"roomUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ roomTitle(mostRecent) }}</a>\n\t\t\t\t\t<span v-if=\"roomPreview(mostRecent)\" class=\"cn-talk-card__headline-preview\">\n\t\t\t\t\t\t· {{ roomPreview(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: compact list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-talk-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"rooms.length === 0\" class=\"cn-talk-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-talk-card__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"room in displayedRooms\"\n\t\t\t\t\t:key=\"roomKey(room)\"\n\t\t\t\t\tclass=\"cn-talk-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-talk-card__row--unread': hasUnread(room) }\">\n\t\t\t\t\t<div class=\"cn-talk-card__row-icon\">\n\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\t:displayName=\"roomTitle(room)\"\n\t\t\t\t\t\t\t:size=\"32\"\n\t\t\t\t\t\t\t:isNoUser=\"true\"\n\t\t\t\t\t\t\thideStatus />\n\t\t\t\t\t\t<NcCounterBubble\n\t\t\t\t\t\t\tv-if=\"hasUnread(room)\"\n\t\t\t\t\t\t\tclass=\"cn-talk-card__badge\"\n\t\t\t\t\t\t\ttype=\"highlighted\"\n\t\t\t\t\t\t\t:aria-label=\"unreadAriaLabel(room)\">\n\t\t\t\t\t\t\t{{ formatUnread(room) }}\n\t\t\t\t\t\t</NcCounterBubble>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"cn-talk-card__row-main\">\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"roomUrl(room)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-talk-card__title\">{{ roomTitle(room) }}</a>\n\t\t\t\t\t\t<span v-if=\"roomPreview(room)\" class=\"cn-talk-card__subtitle\">{{ roomPreview(room) }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcCounterBubble, NcLoadingIcon } from '@nextcloud/vue'\nimport ChatOutline from 'vue-material-design-icons/ChatOutline.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\nconst COMPACT_LIMIT = 5\n\n/**\n * CnTalkCard — bespoke surface-aware widget for the `talk` integration.\n *\n * Renders Talk-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnTalkCard',\n\n\tcomponents: { CnDetailCard, NcAvatar, NcCounterBubble, NcLoadingIcon, ChatOutline },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'talk'`). */\n\t\tintegrationId: { type: String, default: 'talk' },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, required: true },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, required: true },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** Rendering surface (AD-19). */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (s) => VALID_SURFACES.includes(s),\n\t\t},\n\n\t\t/** Optional single-entity reference (room token). */\n\t\tvalue: { type: String, default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Chat') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => ChatOutline },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Whether the card body is collapsible. */\n\t\tcollapsible: { type: Boolean, default: true },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No conversations linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Talk is currently unavailable.') },\n\t\t/** URL of the NC Talk app entry. */\n\t\ttalkAppUrl: { type: String, default: '/index.php/apps/spreed' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trooms: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardTitle() {\n\t\t\treturn this.title || this.integrationId\n\t\t},\n\n\t\tcardIcon() {\n\t\t\treturn this.icon\n\t\t},\n\n\t\tdisplayedRooms() {\n\t\t\tif (this.surface === 'user-dashboard' || this.surface === 'app-dashboard') {\n\t\t\t\treturn this.rooms.slice(0, COMPACT_LIMIT)\n\t\t\t}\n\t\t\treturn this.rooms\n\t\t},\n\n\t\ttotalUnread() {\n\t\t\treturn this.rooms.reduce((sum, room) => {\n\t\t\t\tconst count = Number(room.unreadMessages ?? room.unread ?? 0)\n\t\t\t\treturn sum + (Number.isFinite(count) && count > 0 ? count : 0)\n\t\t\t}, 0)\n\t\t},\n\n\t\tunreadHeadline() {\n\t\t\tconst unread = this.totalUnread\n\t\t\tconst total = this.rooms.length\n\t\t\tif (unread === 0) {\n\t\t\t\t// \"M conversations\" via translatePlural.\n\t\t\t\treturn n('nextcloud-vue', '{count} conversation', '{count} conversations', total, { count: total })\n\t\t\t}\n\t\t\t// Pre-format the two plural fragments, then compose so we don't\n\t\t\t// trip the @nextcloud/l10n single-placeholder constraint.\n\t\t\tconst unreadFragment = n('nextcloud-vue', '{count} unread message', '{count} unread messages', unread, { count: unread })\n\t\t\tconst acrossFragment = n('nextcloud-vue', 'across {count} conversation', 'across {count} conversations', total, { count: total })\n\t\t\treturn `${unreadFragment} ${acrossFragment}`\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.rooms.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Sort defensive copy by lastActivity desc.\n\t\t\tconst sorted = [...this.rooms].sort((a, b) => {\n\t\t\t\tconst ta = Number(a.lastActivity ?? 0)\n\t\t\t\tconst tb = Number(b.lastActivity ?? 0)\n\t\t\t\treturn tb - ta\n\t\t\t})\n\t\t\treturn sorted[0]\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.fetch()\n\t\t} },\n\n\t\tsurface() {\n\t\t\tthis.fetch()\n\t\t},\n\n\t\tvalue() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\troomKey(room) {\n\t\t\treturn room.id ?? room.token ?? room.reference ?? ''\n\t\t},\n\n\t\troomTitle(room) {\n\t\t\tconst raw = room.title ?? room.displayName ?? room.name ?? this.roomKey(room)\n\t\t\treturn stripMarker(raw) || String(this.roomKey(room))\n\t\t},\n\n\t\t// A provider URL is used only when it is a safe scheme; else the app link.\n\t\troomUrl(room) {\n\t\t\tif (room.url && safeHref(room.url) !== '#') {\n\t\t\t\treturn room.url\n\t\t\t}\n\t\t\tconst token = room.token ?? room.id ?? ''\n\t\t\treturn token ? `/index.php/call/${token}` : this.talkAppUrl\n\t\t},\n\n\t\troomPreview(room) {\n\t\t\tconst msg = room.lastMessage\n\t\t\tif (msg && typeof msg === 'object') {\n\t\t\t\treturn msg.message ?? msg.text ?? ''\n\t\t\t}\n\t\t\treturn room.lastMessageText ?? room.preview ?? ''\n\t\t},\n\n\t\thasUnread(room) {\n\t\t\tconst count = Number(room.unreadMessages ?? room.unread ?? 0)\n\t\t\treturn Number.isFinite(count) && count > 0\n\t\t},\n\n\t\tformatUnread(room) {\n\t\t\tconst count = Number(room.unreadMessages ?? room.unread ?? 0)\n\t\t\tif (!Number.isFinite(count) || count <= 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn count > 99 ? '99+' : String(count)\n\t\t},\n\n\t\tunreadAriaLabel(room) {\n\t\t\tconst count = Number(room.unreadMessages ?? room.unread ?? 0)\n\t\t\treturn t('nextcloud-vue', '{n} unread', { n: count })\n\t\t},\n\n\t\tchipSubtitle(room) {\n\t\t\tconst preview = this.roomPreview(room)\n\t\t\treturn preview || this.roomTitle(room)\n\t\t},\n\n\t\tchipUrl(room) {\n\t\t\treturn this.roomUrl(room)\n\t\t},\n\n\t\tfetch() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchList()\n\t\t},\n\n\t\tasync fetchList() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { 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.rooms = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.rooms = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rooms = []\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnTalkCard] failed to fetch rooms', err)\n\t\t\t\tthis.rooms = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync fetchSingle() {\n\t\t\tif (!this.value || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.entity = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.entity = await response.json()\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnTalkCard] failed to fetch single conversation', err)\n\t\t\t\tthis.entity = null\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-talk-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-talk-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-talk-card__headline-line {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-talk-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-talk-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-talk-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-talk-card__headline-preview {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-talk-card__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-hover);\n\tfont-size: 0.9em;\n\tmax-width: 100%;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n/* stylelint-disable-next-line no-descending-specificity */\n.cn-talk-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-talk-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-talk-card__chip-badge {\n\tdisplay: inline-block;\n\tmin-width: 16px;\n\theight: 16px;\n\tpadding: 0 5px;\n\tborder-radius: 8px;\n\tbackground: var(--color-primary-element, #4376FC);\n\tcolor: var(--color-primary-element-text, #ffffff);\n\tfont-size: 0.7em;\n\tfont-weight: bold;\n\tline-height: 16px;\n\ttext-align: center;\n}\n\n.cn-talk-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-talk-card__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 6px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-talk-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-talk-card__row-icon {\n\tposition: relative;\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-talk-card__badge {\n\tposition: absolute;\n\ttop: -4px;\n\tright: -8px;\n\tmin-width: 16px;\n\theight: 16px;\n\tpadding: 0 5px;\n\tborder-radius: 8px;\n\tbackground: var(--color-primary-element, #4376FC);\n\tcolor: var(--color-primary-element-text, #ffffff);\n\tfont-size: 0.7em;\n\tfont-weight: bold;\n\tline-height: 16px;\n\ttext-align: center;\n}\n\n.cn-talk-card__row-main {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-talk-card__title {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-talk-card__row--unread .cn-talk-card__title {\n\tfont-weight: 600;\n}\n\na.cn-talk-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-talk-card__subtitle {\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["_createElementBlock","_Fragment","_createVNode","_createElementVNode","_toDisplayString","_openBlock"],"mappings":"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"}
|