fix dashboard widget

This commit is contained in:
Jonas Heinrich 2020-11-19 10:01:55 +01:00
parent 02de2c652d
commit c7d21c74d8
5 changed files with 62 additions and 182 deletions

View File

@ -14,10 +14,15 @@ class RadioWidget implements IWidget {
/** @var IL10N */ /** @var IL10N */
private $l10n; private $l10n;
/** @var IURLGenerator */
private $urlGenerator;
public function __construct( public function __construct(
IL10N $l10n IL10N $l10n,
IURLGenerator $urlGenerator
) { ) {
$this->l10n = $l10n; $this->l10n = $l10n;
$this->urlGenerator = $urlGenerator;
} }
/** /**
@ -52,15 +57,14 @@ class RadioWidget implements IWidget {
* @inheritDoc * @inheritDoc
*/ */
public function getUrl(): ?string { public function getUrl(): ?string {
return \OC::$server->getURLGenerator()->linkToRoute('radio.page.index'); return $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('radio.page.index'));
// return $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('radio.page.index'));
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function load(): void { public function load(): void {
Util::addScript(Application::APP_ID, 'dashboard'); Util::addScript(Application::APP_ID, 'radio-dashboard');
Util::addStyle(Application::APP_ID, 'dashboard'); Util::addStyle(Application::APP_ID, 'dashboard');
} }
} }

View File

@ -9,11 +9,6 @@
:icon="emptyContentIcon"> :icon="emptyContentIcon">
<template #desc> <template #desc>
{{ emptyContentMessage }} {{ emptyContentMessage }}
<div v-if="state === 'no-token' || state === 'error'" class="connect-button">
<a class="button" :href="settingsUrl">
{{ t('integration_twitter', 'Connect to Twitter') }}
</a>
</div>
</template> </template>
</EmptyContent> </EmptyContent>
</template> </template>
@ -22,9 +17,8 @@
<script> <script>
import axios from '@nextcloud/axios' import axios from '@nextcloud/axios'
import { generateUrl, imagePath } from '@nextcloud/router' import { generateUrl } from '@nextcloud/router'
import { showError } from '@nextcloud/dialogs' import { showError } from '@nextcloud/dialogs'
import moment from '@nextcloud/moment'
import { DashboardWidget } from '@nextcloud/vue-dashboard' import { DashboardWidget } from '@nextcloud/vue-dashboard'
import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent' import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'
@ -32,90 +26,49 @@ export default {
name: 'Dashboard', name: 'Dashboard',
components: { components: {
DashboardWidget, EmptyContent, DashboardWidget,
EmptyContent,
}, },
props: { props: {
title: { title: {
type: String, type: String,
required: true, required: false,
default: 'radio',
}, },
}, },
data() { data() {
return { return {
notifications: [], notifications: [],
showMoreUrl: 'https://twitter.com/notifications', showMoreUrl: generateUrl('/apps/radio/api/favorites'),
// lastDate could be computed but we want to keep the value when first notification is removed
// to avoid getting it again on next request
lastDate: null,
loop: null,
state: 'loading', state: 'loading',
settingsUrl: generateUrl('/settings/user/connected-accounts#twitter_prefs'),
darkThemeColor: OCA.Accessibility.theme === 'dark' ? 'ffffff' : '000000', darkThemeColor: OCA.Accessibility.theme === 'dark' ? 'ffffff' : '000000',
} }
}, },
computed: { computed: {
items() { items() {
// get rid of follow request counts return this.notifications.map((n) => {
let items = this.notifications.filter((n) => {
return (!['follow_request'].includes(n.type))
})
// if we have follow requests, show them in first
if (this.followRequestItem && this.followRequestItem.number > 0) {
items.unshift(this.followRequestItem)
}
// then filter out unnecessary private messages
const privMsgAuthorIds = []
items = items.filter((n) => {
if (n.type !== 'message') {
return true
} else if (privMsgAuthorIds.includes(n.sender_screen_name)) {
return false
} else {
privMsgAuthorIds.push(n.sender_screen_name)
return true
}
})
return items.map((n) => {
return { return {
id: n.id, id: n.id,
targetUrl: this.getNotificationTarget(n), targetUrl: generateUrl('/apps/radio/api/favorites'),
avatarUrl: this.getUserAvatarUrl(n), avatarUrl: n.favicon,
avatarUsername: this.getAvatarText(n), mainText: n.name,
overlayIconUrl: this.getNotificationTypeImage(n), subText: n.tags,
mainText: this.getMainText(n),
subText: this.getSubline(n),
} }
}) })
}, },
followRequestItem() {
// find the last follow request notif
return this.notifications.find((n) => {
return n.type === 'follow_request'
})
},
lastMoment() {
return moment(this.lastDate)
},
emptyContentMessage() { emptyContentMessage() {
if (this.state === 'no-token') { if (this.state === 'error') {
return t('radio', 'No Twitter account connected') return t('radio', 'Error fetching favorite stations')
} else if (this.state === 'error') {
return t('radio', 'Error connecting to Twitter')
} else if (this.state === 'ok') { } else if (this.state === 'ok') {
return t('radio', 'No Twitter notifications!') return t('radio', 'No favorites added yet!')
} }
return '' return ''
}, },
emptyContentIcon() { emptyContentIcon() {
if (this.state === 'no-token') { if (this.state === 'error') {
return 'icon-radio'
} else if (this.state === 'error') {
return 'icon-close' return 'icon-close'
} else if (this.state === 'ok') { } else if (this.state === 'ok') {
return 'icon-checkmark' return 'icon-checkmark'
@ -126,29 +79,18 @@ export default {
beforeMount() { beforeMount() {
this.fetchNotifications() this.fetchNotifications()
this.loop = setInterval(() => this.fetchNotifications(), 120000)
},
mounted() {
}, },
methods: { methods: {
fetchNotifications() { fetchNotifications() {
const req = {} const req = {}
if (this.lastDate) { axios.get(generateUrl('/apps/radio/api/favorites'), req).then((response) => {
req.params = {
since: this.lastDate,
}
}
axios.get(generateUrl('/apps/radio/favorites'), req).then((response) => {
this.processNotifications(response.data) this.processNotifications(response.data)
this.state = 'ok' this.state = 'ok'
}).catch((error) => { }).catch((error) => {
clearInterval(this.loop) clearInterval(this.loop)
if (error.response && error.response.status === 400) { if (error.response && error.response.status === 401) {
this.state = 'no-token' showError(t('radio', 'Failed to fetch favorite radio stations'))
} else if (error.response && error.response.status === 401) {
showError(t('integration_twitter', 'Failed to get Twitter notifications'))
this.state = 'error' this.state = 'error'
} else { } else {
// there was an error in notif processing // there was an error in notif processing
@ -157,89 +99,8 @@ export default {
}) })
}, },
processNotifications(newNotifications) { processNotifications(newNotifications) {
if (this.lastDate) { this.notifications = newNotifications
// just add those which are more recent than our most recent one
let i = 0
while (i < newNotifications.length && this.lastDate < newNotifications[i].timestamp) {
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)
}
// update lastDate manually (explained in data)
const nbNotif = this.notifications.length
this.lastDate = (nbNotif > 0) ? this.notifications[0].timestamp : null
},
filter(notifications) {
return notifications
},
getUserAvatarUrl(n) {
return (n.profile_image_url_https && n.sender_id_str)
? generateUrl('/apps/integration_twitter/avatar?') + encodeURIComponent('userId') + '=' + encodeURIComponent(n.sender_id_str)
: n.type === 'follow_request'
? imagePath('integration_twitter', 'twitter.png')
: ''
},
getAvatarText(n) {
if (['follow_request'].includes(n.type)) {
return '!'
}
return ''
},
getNotificationTarget(n) {
if (['retweet', 'mention'].includes(n.type)) {
return 'https://twitter.com/' + n.sender_screen_name + '/status/' + n.id_str
} else if (['message'].includes(n.type)) {
return 'https://twitter.com/messages'
} else if (['follow_request'].includes(n.type)) {
return 'https://twitter.com/follower_requests'
}
return ''
},
getMainText(n) {
if (['follow_request'].includes(n.type)) {
return t('integration_twitter', '{nb} follow requests', { nb: n.number })
} else if (['retweet', 'mention'].includes(n.type)) {
let text = n.text
while (text.startsWith('@')) {
text = text.replace(/^@[^\s]*\s?/, '')
}
return text
}
return n.text
},
getSubline(n) {
if (['follow_request'].includes(n.type)) {
return t('integration_twitter', 'System')
}
return '@' + n.sender_screen_name
},
getNotificationTypeImage(n) {
if (n.type === 'mention') {
return generateUrl('/svg/integration_twitter/arobase?color=ffffff')
} else if (n.type === 'message') {
return generateUrl('/svg/integration_twitter/message?color=ffffff')
} else if (n.type === 'retweet') {
return generateUrl('/svg/integration_twitter/retweet?color=ffffff')
} else if (n.type === 'follow_request') {
return generateUrl('/svg/integration_twitter/follow_request?color=ffffff')
}
return ''
},
getFormattedDate(n) {
return moment(n.timestamp).format('LLL')
}, },
}, },
} }
</script> </script>
<style scoped lang="scss">
::v-deep .connect-button {
margin-top: 10px;
}
</style>

View File

@ -1,19 +1,15 @@
import Vue from 'vue' import Vue from 'vue'
import { translate, translatePlural } from '@nextcloud/l10n' import router from './router'
import Dashboard from './components/Dashboard' import store from './store'
import Dashboard from './components/Dashboard.vue'
Vue.prototype.t = translate document.addEventListener('DOMContentLoaded', () => {
Vue.prototype.n = translatePlural OCA.Dashboard.register('radio', (el) => {
Vue.prototype.OC = window.OC global.Radio = new Vue({
Vue.prototype.OCA = window.OCA el,
store,
document.addEventListener('DOMContentLoaded', function() { router,
render: h => h(Dashboard),
OCA.Dashboard.register('radio', (el, { widget }) => { })
const View = Vue.extend(Dashboard)
new View({
propsData: { title: widget.title },
}).$mount(el)
}) })
}) })

View File

@ -27,8 +27,6 @@ import { translate, translatePlural } from '@nextcloud/l10n'
import App from './App' import App from './App'
import './dashboard'
import VueBlurHash from 'vue-blurhash' import VueBlurHash from 'vue-blurhash'
import 'vue-blurhash/dist/vue-blurhash.css' import 'vue-blurhash/dist/vue-blurhash.css'

View File

@ -1,3 +1,24 @@
const { merge } = require('webpack-merge')
const path = require('path')
const webpackConfig = require('@nextcloud/webpack-vue-config') const webpackConfig = require('@nextcloud/webpack-vue-config')
module.exports = webpackConfig const config = {
entry: {
dashboard: path.join(__dirname, 'src', 'dashboard.js'),
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
},
],
},
}
const mergedConfigs = merge(config, webpackConfig)
// Remove duplicate rules by the `test` key
mergedConfigs.module.rules = mergedConfigs.module.rules.filter((v, i, a) => a.findIndex(t => (t.test.toString() === v.test.toString())) === i)
module.exports = mergedConfigs