diff options
author | Richard Steinmetz <richard@steinmetz.cloud> | 2023-08-17 15:09:30 +0200 |
---|---|---|
committer | Joas Schilling <coding@schilljs.com> | 2023-08-22 08:36:53 +0200 |
commit | 6982597b6a6d319dacfbe3bee2edd2a39b3d6d68 (patch) | |
tree | 9a3cfc98c5ce08542e204ef37365491fa0a0404e /apps/dashboard/src/DashboardApp.vue | |
parent | 82835eaa4623180c41dad927b0ac1db1ed449362 (diff) | |
download | nextcloud-server-6982597b6a6d319dacfbe3bee2edd2a39b3d6d68.tar.gz nextcloud-server-6982597b6a6d319dacfbe3bee2edd2a39b3d6d68.zip |
feat(dashboard): implement widget item api v2
This API enables the dashboard to render all widgets from the API data
alone without having apps to provide their own bundles. This saves a lot
of traffic and execution time as a lot less javascript has to be parsed
on the frontend.
Signed-off-by: Richard Steinmetz <richard@steinmetz.cloud>
Diffstat (limited to 'apps/dashboard/src/DashboardApp.vue')
-rw-r--r-- | apps/dashboard/src/DashboardApp.vue | 106 |
1 files changed, 92 insertions, 14 deletions
diff --git a/apps/dashboard/src/DashboardApp.vue b/apps/dashboard/src/DashboardApp.vue index 993340dae36..7a7b56da266 100644 --- a/apps/dashboard/src/DashboardApp.vue +++ b/apps/dashboard/src/DashboardApp.vue @@ -14,21 +14,44 @@ v-bind="{swapThreshold: 0.30, delay: 500, delayOnTouchOnly: true, touchStartThreshold: 3}" handle=".panel--header" @end="saveLayout"> - <div v-for="panelId in layout" :key="panels[panelId].id" class="panel"> - <div class="panel--header"> - <h2> - <div aria-labelledby="panel--header--icon--description" - aria-hidden="true" - :class="panels[panelId].iconClass" - role="img" /> - {{ panels[panelId].title }} - </h2> - <span id="panel--header--icon--description" class="hidden-visually"> {{ t('dashboard', '"{title} icon"', { title: panels[panelId].title }) }} </span> + <template v-for="panelId in layout"> + <div v-if="isApiWidgetV2(panels[panelId].id)" + :key="`${panels[panelId].id}-v2`" + class="panel"> + <div class="panel--header"> + <h2> + <div aria-labelledby="panel--header--icon--description" + aria-hidden="true" + :class="apiWidgets[panels[panelId].id].icon_class" + role="img" /> + {{ apiWidgets[panels[panelId].id].title }} + </h2> + <span id="panel--header--icon--description" class="hidden-visually"> + {{ t('dashboard', '"{title} icon"', { title: apiWidgets[panels[panelId].id].title }) }} + </span> + </div> + <div class="panel--content"> + <ApiDashboardWidget :widget="apiWidgets[panels[panelId].id]" + :data="apiWidgetItems[panels[panelId].id]" + :loading="loadingItems" /> + </div> </div> - <div class="panel--content" :class="{ loading: !panels[panelId].mounted }"> - <div :ref="panels[panelId].id" :data-id="panels[panelId].id" /> + <div v-else :key="panels[panelId].id" class="panel"> + <div class="panel--header"> + <h2> + <div aria-labelledby="panel--header--icon--description" + aria-hidden="true" + :class="panels[panelId].iconClass" + role="img" /> + {{ panels[panelId].title }} + </h2> + <span id="panel--header--icon--description" class="hidden-visually"> {{ t('dashboard', '"{title} icon"', { title: panels[panelId].title }) }} </span> + </div> + <div class="panel--content" :class="{ loading: !panels[panelId].mounted }"> + <div :ref="panels[panelId].id" :data-id="panels[panelId].id" /> + </div> </div> - </div> + </template> </Draggable> <div class="footer"> @@ -94,7 +117,7 @@ </template> <script> -import { generateUrl } from '@nextcloud/router' +import { generateUrl, generateOcsUrl } from '@nextcloud/router' import { getCurrentUser } from '@nextcloud/auth' import { loadState } from '@nextcloud/initial-state' import axios from '@nextcloud/axios' @@ -105,6 +128,7 @@ import Pencil from 'vue-material-design-icons/Pencil.vue' import Vue from 'vue' import isMobile from './mixins/isMobile.js' +import ApiDashboardWidget from './components/ApiDashboardWidget.vue' const panels = loadState('dashboard', 'panels') const firstRun = loadState('dashboard', 'firstRun') @@ -123,6 +147,7 @@ const statusInfo = { export default { name: 'DashboardApp', components: { + ApiDashboardWidget, NcButton, Draggable, NcModal, @@ -150,6 +175,9 @@ export default { modal: false, appStoreUrl: generateUrl('/settings/apps/dashboard'), statuses: {}, + apiWidgets: [], + apiWidgetItems: {}, + loadingItems: true, } }, computed: { @@ -239,6 +267,23 @@ export default { }, }, + async created() { + await this.fetchApiWidgets() + + const apiWidgetIdsToFetch = Object + .values(this.apiWidgets) + .filter(widget => this.isApiWidgetV2(widget.id)) + .map(widget => widget.id) + await Promise.all(apiWidgetIdsToFetch.map(id => this.fetchApiWidgetItems([id], true))) + + for (const widget of Object.values(this.apiWidgets)) { + if (widget.reload_interval > 0) { + setInterval(async () => { + await this.fetchApiWidgetItems([widget.id], true) + }, widget.reload_interval * 1000) + } + } + }, mounted() { this.updateSkipLink() window.addEventListener('scroll', this.handleScroll) @@ -278,6 +323,11 @@ export default { }, rerenderPanels() { for (const app in this.callbacks) { + // TODO: Properly rerender v2 widgets + if (this.isApiWidgetV2(this.panels[app].id)) { + continue + } + const element = this.$refs[app] if (this.layout.indexOf(app) === -1) { continue @@ -374,6 +424,33 @@ export default { document.body.classList.remove('dashboard--scrolled') } }, + async fetchApiWidgets() { + const response = await axios.get(generateOcsUrl('/apps/dashboard/api/v1/widgets')) + this.apiWidgets = response.data.ocs.data + }, + async fetchApiWidgetItems(widgetIds, merge = false) { + try { + const url = generateOcsUrl('/apps/dashboard/api/v2/widget-items') + const params = new URLSearchParams(widgetIds.map(id => ['widgets[]', id])) + const response = await axios.get(`${url}?${params.toString()}`) + const widgetItems = response.data.ocs.data + if (merge) { + this.apiWidgetItems = Object.assign({}, this.apiWidgetItems, widgetItems) + } else { + this.apiWidgetItems = widgetItems + } + } finally { + this.loadingItems = false + } + }, + isApiWidgetV2(id) { + for (const widget of Object.values(this.apiWidgets)) { + if (widget.id === id && widget.item_api_versions.includes(2)) { + return true + } + } + return false + }, }, } </script> @@ -470,6 +547,7 @@ export default { margin-right: 16px; background-position: center; float: left; + margin-top: -6px; } } } |