aboutsummaryrefslogtreecommitdiffstats
path: root/apps/dashboard/src/DashboardApp.vue
diff options
context:
space:
mode:
authorRichard Steinmetz <richard@steinmetz.cloud>2023-08-17 15:09:30 +0200
committerJoas Schilling <coding@schilljs.com>2023-08-22 08:36:53 +0200
commit6982597b6a6d319dacfbe3bee2edd2a39b3d6d68 (patch)
tree9a3cfc98c5ce08542e204ef37365491fa0a0404e /apps/dashboard/src/DashboardApp.vue
parent82835eaa4623180c41dad927b0ac1db1ed449362 (diff)
downloadnextcloud-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.vue106
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;
}
}
}