summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJulien Veyssier <eneiluj@posteo.net>2020-12-14 14:36:10 +0100
committerCarl Schwan <carl@carlschwan.eu>2022-09-14 14:46:44 +0200
commit0915e4e1013c12a30372c713c2fe057acc119991 (patch)
tree0afc173bba929a3b62dc37c6340ee1cb83346f41 /src
parent3d439139d2b0a7561402bd9dd8a76e72ad3cbfc6 (diff)
add dashboard widget, only handling internal 'Follow' subtypes for the moment
Signed-off-by: Julien Veyssier <eneiluj@posteo.net>
Diffstat (limited to 'src')
-rw-r--r--src/dashboard.js32
-rw-r--r--src/views/Dashboard.vue200
2 files changed, 232 insertions, 0 deletions
diff --git a/src/dashboard.js b/src/dashboard.js
new file mode 100644
index 00000000..48132a91
--- /dev/null
+++ b/src/dashboard.js
@@ -0,0 +1,32 @@
+/* jshint esversion: 6 */
+
+/**
+ * Nextcloud - social
+ *
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Julien Veyssier <eneiluj@posteo.net>
+ * @copyright Julien Veyssier 2020
+ */
+
+import Vue from 'vue'
+import { translate, translatePlural } from '@nextcloud/l10n'
+import Dashboard from './views/Dashboard'
+
+Vue.prototype.t = translate
+Vue.prototype.n = translatePlural
+Vue.prototype.OC = window.OC
+Vue.prototype.OCA = window.OCA
+
+document.addEventListener('DOMContentLoaded', function() {
+
+ OCA.Dashboard.register('social_notifications', (el, { widget }) => {
+ const View = Vue.extend(Dashboard)
+ new View({
+ propsData: { title: widget.title },
+ }).$mount(el)
+ })
+
+})
diff --git a/src/views/Dashboard.vue b/src/views/Dashboard.vue
new file mode 100644
index 00000000..bb5cbf8a
--- /dev/null
+++ b/src/views/Dashboard.vue
@@ -0,0 +1,200 @@
+<template>
+ <DashboardWidget :items="items"
+ :show-more-url="showMoreUrl"
+ :show-more-text="title"
+ :loading="state === 'loading'">
+ <template #empty-content>
+ <EmptyContent
+ v-if="emptyContentMessage"
+ :icon="emptyContentIcon">
+ <template #desc>
+ {{ emptyContentMessage }}
+ <div v-if="state === 'error'" class="connect-button">
+ <a class="button" :href="appUrl">
+ {{ t('social', 'Go to Social app') }}
+ </a>
+ </div>
+ </template>
+ </EmptyContent>
+ </template>
+ </DashboardWidget>
+</template>
+
+<script>
+import axios from '@nextcloud/axios'
+import { generateUrl } from '@nextcloud/router'
+import { showError, showSuccess } from '@nextcloud/dialogs'
+import moment from '@nextcloud/moment'
+import { DashboardWidget } from '@nextcloud/vue-dashboard'
+import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'
+
+export default {
+ name: 'Dashboard',
+
+ components: {
+ DashboardWidget,
+ EmptyContent,
+ },
+
+ props: {
+ title: {
+ type: String,
+ required: true,
+ },
+ },
+
+ data() {
+ return {
+ notifications: [],
+ showMoreUrl: generateUrl('/apps/social/timeline/notifications'),
+ showMoreText: t('social', 'Social notifications'),
+ loop: null,
+ state: 'loading',
+ appUrl: generateUrl('/apps/social'),
+ }
+ },
+
+ computed: {
+ items() {
+ return this.notifications.map((n) => {
+ return {
+ id: n.id,
+ targetUrl: this.getNotificationTarget(n),
+ avatarUrl: this.getAvatarUrl(n),
+ avatarUsername: this.getActorName(n),
+ overlayIconUrl: this.getNotificationTypeImage(n),
+ mainText: this.getMainText(n),
+ subText: this.getSubline(n),
+ }
+ })
+ },
+ lastTimestamp() {
+ return this.notifications.length
+ ? this.notifications[0].publishedTime
+ : 0
+ },
+ emptyContentMessage() {
+ if (this.state === 'error') {
+ return t('social', 'Error getting Social notifications')
+ } else if (this.state === 'ok') {
+ return t('social', 'No Social notifications!')
+ }
+ return ''
+ },
+ emptyContentIcon() {
+ if (this.state === 'error') {
+ return 'icon-close'
+ } else if (this.state === 'ok') {
+ return 'icon-checkmark'
+ }
+ return 'icon-checkmark'
+ },
+ },
+
+ beforeMount() {
+ this.fetchNotifications()
+ this.loop = setInterval(() => this.fetchNotifications(), 10000)
+ },
+
+ methods: {
+ fetchNotifications() {
+ const req = {
+ params: {
+ limit: 10,
+ },
+ }
+ const url = generateUrl('/apps/social/api/v1/stream/notifications')
+ // TODO check why 'since' param is in fact 'until'
+ /* if (this.lastDate) {
+ req.params.since = this.lastTimestamp,
+ } */
+ axios.get(url, req).then((response) => {
+ if (response.data?.result) {
+ this.processNotifications(response.data.result)
+ this.state = 'ok'
+ } else {
+ this.state = 'error'
+ }
+ }).catch((error) => {
+ clearInterval(this.loop)
+ if (error.response?.status && error.response.status >= 400) {
+ showError(t('social', 'Failed to get Social notifications'))
+ this.state = 'error'
+ } else {
+ // there was an error in notif processing
+ console.error(error)
+ }
+ })
+ },
+ processNotifications(newNotifications) {
+ if (this.lastTimestamp !== 0) {
+ // just add those which are more recent than our most recent one
+ let i = 0
+ while (i < newNotifications.length && this.lastTimestamp < newNotifications[i].publishedTime) {
+ i++
+ }
+ if (i > 0) {
+ const toAdd = this.filter(newNotifications.slice(0, i))
+ this.notifications = toAdd.concat(this.notifications)
+ }
+ } else {
+ // first time, we don't check the date
+ this.notifications = this.filter(newNotifications)
+ }
+ },
+ filter(notifications) {
+ return notifications
+ // TODO check if we need to filter
+ /* return notifications.filter((n) => {
+ return (n.type === 'something' || n.subtype === 'somethingElse')
+ }) */
+ },
+ getMainText(n) {
+ if (n.subtype === 'Follow') {
+ return t('social', '{account} is following you', { account: this.getActorName(n) })
+ }
+ },
+ getAvatarUrl(n) {
+ return undefined
+ // TODO get external and internal avatars
+ /* return this.getActorAccountName(n)
+ ? generateUrl('???')
+ : undefined */
+ },
+ getActorName(n) {
+ return n.actor_info && n.actor_info.type === 'Person' && n.actor_info.preferredUsername
+ ? n.actor_info.preferredUsername
+ : ''
+ },
+ getActorAccountName(n) {
+ return n.actor_info && n.actor_info.type === 'Person' && n.actor_info.account
+ ? n.actor_info.account
+ : ''
+ },
+ getNotificationTarget(n) {
+ if (n.subtype === 'Follow') {
+ return generateUrl('/apps/social/@' + this.getActorAccountName(n) + '/')
+ }
+ return this.showMoreUrl
+ },
+ getSubline(n) {
+ if (n.subtype === 'Follow') {
+ return this.getActorAccountName(n)
+ }
+ return ''
+ },
+ getNotificationTypeImage(n) {
+ if (n.subtype === 'Follow') {
+ return generateUrl('/svg/social/add_user')
+ }
+ return ''
+ },
+ },
+}
+</script>
+
+<style scoped lang="scss">
+::v-deep .connect-button {
+ margin-top: 10px;
+}
+</style>