🚧 Refactor + more widgets

* ♻️ segment into smaller widgets, improve mixin
* ♻️ change NextcloudInfo to NextcloudUser
  * a small widget showing branding and uesr info, including quota
*  add NextcloudNotifications widget
  * show and delete Nextcloud notifications
*  add NextcloudUserStatus widget
  * display user statuses of selected users
*  add NextcloudStats widget (admin only)
  * display Nextcloud usage statistics (users, files, shares)
*  add NextcloudSystem widget (admin only)
  * visualise cpu load and memory utilisation, show server versions
*  add NextcloudPhpOpcache widget (admin only)
  * show statistics about php opcache performance
*  add a misc helper for formatting nunbers
* 🌐 add translations to widget templates
* 🌐 add translation entries for en
* 🍱 add scss styles file, shared by all widgets
This commit is contained in:
Marcell Fülöp
2022-06-19 12:06:43 +00:00
parent a43988f3cd
commit 821af62426
12 changed files with 1558 additions and 442 deletions

View File

@@ -0,0 +1,192 @@
<template>
<div class="nextcloud-widget nextcloud-status-wrapper">
<div v-if="notifications.length" class="sep">
<!-- group actions: delete all -->
<p v-if="canDeleteNotification('delete-all')">
<span class="action group-action" @click="deleteNotifications">{{ tt('delete-all') }}</span>
</p>
<hr/>
<!-- notifications list -->
<div v-for="(notification, idx) in notifications" :key="idx" class="notification">
<div><img :src="notificationIcon(notification.icon)" /></div>
<div>
<p>
<small class="date" v-tooltip="dateTooltip(notification)">
{{ getTimeAgo(Date.parse(notification.datetime)) }}
</small> <span v-tooltip="subjectTooltip(notification)">{{ notification.subject }} </span>
<!-- notifications item: action links -->
<span v-if="notification.actions.length">
<span v-for="(action, idx) in notification.actions" :key="idx">
<a :href="action.link" class="action" target="_blank">{{ action.label }}</a>
</span>
</span>
<span v-if="canDeleteNotification('delete')">
<a @click="deleteNotification(notification.notification_id)"
class="action secondary">{{ tt('delete-notification') }}</a>
</span>
</p>
</div>
<hr/>
</div>
</div>
<!-- empty notifications list -->
<div v-else class="sep">
<p>{{ tt('no-notifications') }}</p>
</div>
</div>
</template>
<script>
import WidgetMixin from '@/mixins/WidgetMixin';
import NextcloudMixin from '@/mixins/NextcloudMixin';
// //import { NcdNotif } from '@/utils/ncd';
/**
* NextcloudNotifications widget - Displays the user's notifications
* Used endpoints
* - capabilities: to determine if the User Notification API is enabled
* - notifications: to fetch list of notifications, delete all or a single notification
*/
export default {
mixins: [WidgetMixin, NextcloudMixin],
components: {},
data() {
return {
notifications: [],
};
},
methods: {
allowedStatuscodes() {
return [100, 200];
},
async fetchData() {
if (!this.hasValidCredentials()) return;
await this.loadCapabilities();
if (!this.capabilities?.notifications?.enabled) {
this.error('This Nextcloud server doesn\'t support the Notifications API');
return;
}
this.makeRequest(this.endpoint('notifications'), this.headers)
// //Promise.resolve(NcdNotif)
.then(this.processNotifications)
.finally(this.finishLoading);
},
processNotifications(response) {
const notifications = this.validateResponse(response);
this.notifications = [];
notifications.forEach((notification) => {
this.notifications.push(notification);
});
},
/* Transform icon URL to SVG Color API request URL
* @see https://docs.nextcloud.com/server/latest/developer_manual/html_css_design/icons.html */
notificationIcon(url) {
const color = this.getValueFromCss('widget-text-color').replace('#', '');
return url.replace('core/img', 'svg/core')
.replace(/extra-apps\/([^/]+)\/img/, 'svg/$1')
.replace(/apps\/([^/]+)\/img/, 'svg/$1')
.replace('.svg', `?color=${color}`);
},
/* Notification actions */
canDeleteNotification(deleteTarget) {
const capNotif = this.capabilities?.notifications?.features;
return Array.isArray(capNotif) && capNotif.includes(deleteTarget);
},
deleteNotifications() {
this.makeRequest(this.endpoint('notifications'), this.headers, 'DELETE')
// //Promise.resolve()
.then(() => {
this.notifications = [];
});
},
deleteNotification(id) {
this.makeRequest(`${this.endpoint('notifications')}/${id}`, this.headers, 'DELETE')
// //Promise.resolve()
.then(this.fetchData);
},
/* Tooltip generators */
subjectTooltip(notification) {
const content = notification.message;
return {
content, trigger: 'hover focus', delay: 250, classes: 'nc-tooltip',
};
},
dateTooltip(notification) {
const content = new Date(Date.parse(notification.datetime)).toLocaleString();
return {
content, trigger: 'hover focus', delay: 250, classes: 'nc-tooltip',
};
},
},
created() {
this.overrideUpdateInterval = 60;
},
};
</script>
<style scoped lang="scss">
@import '@/styles/widgets/nextcloud-shared.scss';
.nextcloud-status-wrapper {
div p small i {
position: relative;
top: .25rem;
}
small.date {
background: var(--widget-text-color);
color: var(--widget-accent-color);
border-radius: 4px;
padding: .15rem .3rem;
margin: .25rem .25rem .25rem 0;
display: inline-block;
font-weight: bold;
opacity: .66;
}
span.group-action {
float: right;
}
span.action, span a.action {
cursor: pointer;
margin: .1rem 0 .1rem .5rem;
padding: .15rem;
border-radius: 4px;
white-space: nowrap;
}
span.action:hover, span a.action:hover {
background: var(--widget-text-color);
color: var(--widget-accent-color);
text-decoration: underline;
}
.secondary {
opacity: .5;
font-size: 75%;
margin-left: .2rem;
}
div.notification {
display: table;
width: 100%;
> div:first-child {
float: right;
}
> div:nth-child(2) {
float: left;
width: 93%;
}
> div {
display: table-cell;
text-align: left;
> img {
float: right;
width: 16px;
height: 16px;
position: relative;
top: 1rem;
opacity: .75;
}
}
}
div hr {
margin-top: 0.3rem;
margin-bottom: 0;
}
}
</style>