🚧 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:
195
src/components/Widgets/NextcloudUserStatus.vue
Normal file
195
src/components/Widgets/NextcloudUserStatus.vue
Normal file
@@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<div class="nextcloud-widget nextcloud-user-status-wrapper">
|
||||
<div v-if="didLoadData" class="sep">
|
||||
<!-- user statuses: list -->
|
||||
<div v-for="(status, userId) in statuses" :key="userId" class="user">
|
||||
<div>
|
||||
<!-- user status: emoji -->
|
||||
<div>
|
||||
<i>{{ status.icon }}</i>
|
||||
</div>
|
||||
<!-- user status: message -->
|
||||
<div>
|
||||
<p v-tooltip="clearAtTooltip(status.clearAt)">
|
||||
<strong>{{ status.userId }}</strong>
|
||||
<small v-if="status.clearAt"><i class="fal fa-clock"></i></small>
|
||||
<span v-else-if="status.message">•</span><em>{{ status.message }}</em>
|
||||
</p>
|
||||
</div>
|
||||
<!-- user status: status -->
|
||||
<div>
|
||||
<p>
|
||||
<small :class="'status ' + getStatusClass(status.status)">
|
||||
<i v-if="status.status === 'online' || status.status === 'dnd'"
|
||||
class="fas fa-circle" v-tooltip="tt(status.status)"></i>
|
||||
<i v-else class="far fa-circle" v-tooltip="tt(status.status)"></i>
|
||||
</small>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- user statuses: no content -->
|
||||
<div v-else class="sep"><p>{{ tt('nothing-to-show') }}</p></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WidgetMixin from '@/mixins/WidgetMixin';
|
||||
import NextcloudMixin from '@/mixins/NextcloudMixin';
|
||||
// //import { NcdStatusAll, NcdStatusSingle } from '@/utils/ncd';
|
||||
|
||||
// Nextcloud User Status API supports getting all user statuses at once
|
||||
// or a single user's status. {fetchStrategy} determines which of these methods to use.
|
||||
const fetchStrategies = {
|
||||
allAtOnce: 'AllAtOnce',
|
||||
oneByOne: 'OneByOne',
|
||||
};
|
||||
|
||||
/**
|
||||
* NextcloudUserStatus widget - Displays user statuses
|
||||
* Used endpoints
|
||||
* - capabilities: to determine if the User Status API is enabled
|
||||
* - userstatus: to fetch a single or all user statuses
|
||||
*/
|
||||
export default {
|
||||
mixins: [WidgetMixin, NextcloudMixin],
|
||||
components: {},
|
||||
computed: {
|
||||
didLoadData() {
|
||||
return !!Object.keys(this?.statuses || {}).length;
|
||||
},
|
||||
fetchStrategy() {
|
||||
if (!this.options.fetchStrategy) {
|
||||
return fetchStrategies.allAtOnce;
|
||||
}
|
||||
if (!Object.values(fetchStrategies).includes(this.options.fetchStrategy)) {
|
||||
return fetchStrategies.allAtOnce;
|
||||
}
|
||||
return this.options.fetchStrategy;
|
||||
},
|
||||
users() {
|
||||
if (!this.options.users || !Array.isArray(this.options.users)) return [];
|
||||
if (this.options.users.length > 100) return this.options.users.slice(0, 100);
|
||||
return this.options.users;
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
statuses: {},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
allowedStatuscodes() {
|
||||
return [100, 200];
|
||||
},
|
||||
async fetchData() {
|
||||
if (!this.hasValidCredentials() || !this.users.length) return;
|
||||
await this.loadCapabilities();
|
||||
if (!this.capabilities?.userStatus) {
|
||||
this.error('This Nextcloud server doesn\'t support the User Status API');
|
||||
return;
|
||||
}
|
||||
if (this.fetchStrategy === fetchStrategies.allAtOnce) {
|
||||
this.makeRequest(this.endpoint('userstatus'), this.headers)
|
||||
// //Promise.resolve(NcdStatusAll)
|
||||
.then(this.processStatuses)
|
||||
.finally(this.finishLoading);
|
||||
} else {
|
||||
const promises = [];
|
||||
this.newStatuses = {};
|
||||
this.users.forEach((user) => {
|
||||
promises.push(
|
||||
this.makeRequest(`${this.endpoint('userstatus')}/${user}`, this.headers)
|
||||
// //Promise.resolve(NcdStatusSingle)
|
||||
.then(this.processStatus),
|
||||
);
|
||||
});
|
||||
Promise.all(promises)
|
||||
.then(() => {
|
||||
this.statuses = this.newStatuses;
|
||||
delete this.newStatuses;
|
||||
})
|
||||
.finally(this.finishLoading);
|
||||
}
|
||||
},
|
||||
processStatuses(response) {
|
||||
const statuses = this.validateResponse(response);
|
||||
const newStatuses = {};
|
||||
Object.values(statuses).forEach((status) => {
|
||||
if (!this.users.includes(status.userId)) return;
|
||||
newStatuses[status.userId] = status;
|
||||
});
|
||||
this.statuses = newStatuses;
|
||||
},
|
||||
processStatus(response) {
|
||||
const raw = this.validateResponse(response);
|
||||
const status = Array.isArray(raw) && raw.length ? raw[0] : raw;
|
||||
if (status) {
|
||||
this.newStatuses[status.userId] = status;
|
||||
}
|
||||
},
|
||||
getStatusClass(status) {
|
||||
switch (status) {
|
||||
case 'online': return 'success';
|
||||
case 'away': return 'warning';
|
||||
case 'dnd': return 'danger';
|
||||
case 'offline': default: return 'disabled';
|
||||
}
|
||||
},
|
||||
/* Tooltip generators */
|
||||
clearAtTooltip(clearAtTime) {
|
||||
const content = clearAtTime ? `${this.tt('until')}`
|
||||
+ ` ${new Date(clearAtTime * 1000).toLocaleString()}` : '';
|
||||
return {
|
||||
content, html: true, trigger: 'hover focus', delay: 250, classes: 'nc-tooltip',
|
||||
};
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.overrideUpdateInterval = 60;
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/widgets/nextcloud-shared.scss';
|
||||
.nextcloud-user-status-wrapper {
|
||||
.status {
|
||||
float: right;
|
||||
i {
|
||||
position: relative;
|
||||
top: .25rem;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
div.user > div {
|
||||
display: table;
|
||||
width: 100%;
|
||||
> div:first-child {
|
||||
width: 1.75em;
|
||||
text-align: center;
|
||||
> i {
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
> div:nth-child(2) {
|
||||
p small i {
|
||||
color: #aaaaaa;
|
||||
top: 0;
|
||||
opacity: .5;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
> div {
|
||||
display: table-cell;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
div.user hr {
|
||||
margin-top: 0.3rem;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user