Merge pull request #1719 from nextcloud/artonge/fix/fixes

Tiny fixes all around
pull/1728/head
Louis 2023-04-11 11:42:12 +02:00 zatwierdzone przez GitHub
commit 4d056fe998
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
12 zmienionych plików z 102 dodań i 91 usunięć

Wyświetl plik

@ -60,7 +60,7 @@ class Application extends App implements IBootstrap {
$context->registerSearchProvider(UnifiedSearchProvider::class); $context->registerSearchProvider(UnifiedSearchProvider::class);
$context->registerWellKnownHandler(WebfingerHandler::class); $context->registerWellKnownHandler(WebfingerHandler::class);
$context->registerEventListener(BeforeTemplateRenderedEvent::class, ProfileSectionListener::class); $context->registerEventListener(BeforeTemplateRenderedEvent::class, ProfileSectionListener::class);
// $context->registerDashboardWidget(SocialWidget::class); $context->registerDashboardWidget(SocialWidget::class);
} }
public function boot(IBootContext $context): void { public function boot(IBootContext $context): void {

Wyświetl plik

@ -169,13 +169,13 @@ export default {
}, },
defaultVisibility: { defaultVisibility: {
type: String, type: String,
default: localStorage.getItem('social.lastPostType') || 'followers', default: undefined,
}, },
}, },
data() { data() {
return { return {
statusContent: '', statusContent: '',
visibility: this.defaultVisibility, visibility: this.defaultVisibility || localStorage.getItem('social.lastPostType') || 'followers',
loading: false, loading: false,
/** @type {Object<string, LocalAttachment>} */ /** @type {Object<string, LocalAttachment>} */
attachments: {}, attachments: {},
@ -301,14 +301,14 @@ export default {
* @param {import('../../types/Mastodon.js').Account} account * @param {import('../../types/Mastodon.js').Account} account
*/ */
prefillMessageWithMention(account) { prefillMessageWithMention(account) {
if (!this.statusIsEmpty) { if (!this.statusIsEmpty || this.$refs.composerInput === undefined) {
return return
} }
this.$refs.composerInput.innerHTML = ` this.$refs.composerInput.innerHTML = `
<span class="mention" contenteditable="false"> <span class="mention" contenteditable="false">
<a href="${account.url}" target="_blank"> <a href="${account.url}" target="_blank">
<img src="${!account.acct.includes('@') ? generateUrl(`/avatar/${account.username}/32`) : generateUrl(`apps/social/api/v1/global/actor/avatar?id=${account.acct}`)}"/> <img src="${account.avatar}"/>
@${account.acct} @${account.acct}
</a> </a>
</span>&nbsp;` </span>&nbsp;`
@ -413,6 +413,7 @@ export default {
this.loading = false this.loading = false
this.replyTo = null this.replyTo = null
this.$refs.composerInput.innerText = '' this.$refs.composerInput.innerText = ''
this.updateStatusContent()
this.attachments = {} this.attachments = {}
this.$store.dispatch('refreshTimeline') this.$store.dispatch('refreshTimeline')
} }

Wyświetl plik

@ -27,7 +27,7 @@
:disable-tooltip="true" :disable-tooltip="true"
:size="128" /> :size="128" />
<NcAvatar v-else <NcAvatar v-else
:url="avatarUrl" :url="accountInfo.avatar"
:disable-tooltip="true" :disable-tooltip="true"
:size="128" /> :size="128" />
<h2>{{ displayName }}</h2> <h2>{{ displayName }}</h2>
@ -57,6 +57,9 @@
{{ t('social', 'Website') }}: <a :href="website.value">{{ website.value }}<OpenInNew :size="15" /></a> {{ t('social', 'Website') }}: <a :href="website.value">{{ website.value }}<OpenInNew :size="15" /></a>
</p> </p>
<!-- Hack to render note safely -->
<MessageContent v-if="accountInfo.note" class="user-profile__note" :item="{content: accountInfo.note, tag: [], mentions: []}" />
<FollowButton class="user-profile__info" :account="accountInfo.acct" :uid="uid" /> <FollowButton class="user-profile__info" :account="accountInfo.acct" :uid="uid" />
<NcButton v-if="serverData.public" <NcButton v-if="serverData.public"
class="user-profile__info primary" class="user-profile__info primary"
@ -158,6 +161,10 @@ export default {
} }
&__note {
text-align: start;
}
&__sections { &__sections {
display: flex; display: flex;

Wyświetl plik

@ -43,11 +43,14 @@ export default {
} }
</script> </script>
<style scoped> <style scoped lang='scss'>
.post-avatar { .post-avatar {
position: relative;
padding: 5px 10px 10px 5px; padding: 5px 10px 10px 5px;
height: 52px; height: 52px;
width: 52px; width: 52px;
.avatardiv {
position: static;
}
} }
</style> </style>

Wyświetl plik

@ -1,5 +1,5 @@
<template> <template>
<div :class="['timeline-entry', hasHeader ? 'with-header' : '']"> <li :class="['timeline-entry', hasHeader ? 'with-header' : '']">
<div v-if="isNotification" class="notification"> <div v-if="isNotification" class="notification">
<Bell :size="22" /> <Bell :size="22" />
<span class="notification-action"> <span class="notification-action">
@ -30,15 +30,16 @@
:type="type" /> :type="type" />
</div> </div>
</template> </template>
</div> </li>
</template> </template>
<script> <script>
import Bell from 'vue-material-design-icons/Bell.vue'
import { translate } from '@nextcloud/l10n'
import TimelinePost from './TimelinePost.vue' import TimelinePost from './TimelinePost.vue'
import TimelineAvatar from './TimelineAvatar.vue' import TimelineAvatar from './TimelineAvatar.vue'
import UserEntry from './UserEntry.vue' import UserEntry from './UserEntry.vue'
import Bell from 'vue-material-design-icons/Bell.vue' import { notificationSummary } from '../services/notifications.js'
import { translate } from '@nextcloud/l10n'
export default { export default {
name: 'TimelineEntry', name: 'TimelineEntry',
@ -102,30 +103,7 @@ export default {
* @return {string} * @return {string}
*/ */
actionSummary() { actionSummary() {
switch (this.notification.type) { return notificationSummary(this.notification)
case 'mention':
return t('social', '{account} mentioned you', { account: this.notification.account.acct })
case 'status':
return t('social', '{account} posted a status', { account: this.notification.account.acct })
case 'reblog':
return t('social', '{account} boosted your post', { account: this.notification.account.acct })
case 'follow':
return t('social', '{account} started to follow you', { account: this.notification.account.acct })
case 'follow_request':
return t('social', '{account} requested to follow you', { account: this.notification.account.acct })
case 'favourite':
return t('social', '{account} liked your post', { account: this.notification.account.acct })
case 'poll':
return t('social', '{account} ended the poll', { account: this.notification.account.acct })
case 'update':
return t('social', '{account} edited a status', { account: this.notification.account.acct })
case 'admin.sign_up':
return t('social', '{account} signed up', { account: this.notification.account.acct })
case 'admin.report':
return t('social', '{account} filed a report', { account: this.notification.account.acct })
default:
return ''
}
}, },
}, },
methods: { methods: {

Wyświetl plik

@ -22,7 +22,7 @@
<template> <template>
<div class="social__timeline"> <div class="social__timeline">
<transition-group name="list" tag="div"> <transition-group name="list" tag="ul">
<TimelineEntry v-for="entry in timeline" <TimelineEntry v-for="entry in timeline"
:key="entry.id" :key="entry.id"
:item="entry" :item="entry"

Wyświetl plik

@ -44,7 +44,7 @@
</span> </span>
</template> </template>
</NcButton> </NcButton>
<NcButton v-if="item.visibility === 'public' || item.visibility === 'followers'" <NcButton v-if="item.visibility === 'public' || item.visibility === 'unlisted'"
:title="t('social', 'Boost')" :title="t('social', 'Boost')"
type="tertiary-no-background" type="tertiary-no-background"
@click="boost"> @click="boost">
@ -208,10 +208,10 @@ export default {
getSinglePostTimeline(e) { getSinglePostTimeline(e) {
// Display internal or external post // Display internal or external post
if (!this.isLocal) { if (!this.isLocal) {
// TODO - fix
if (this.type === 'Note') { if (this.type === 'Note') {
window.open(this.item.id) window.open(this.item.id)
} else if (this.type === 'Announce') { } else if (this.type === 'Announce') {
// TODO
window.open(this.item.object) window.open(this.item.object)
} else { } else {
logger.warn("Don't know what to do with posts of type " + this.type, { post: this.item }) logger.warn("Don't know what to do with posts of type " + this.type, { post: this.item })
@ -269,7 +269,6 @@ export default {
padding: 4px 8px; padding: 4px 8px;
font-size: 15px; font-size: 15px;
line-height: 1.6em; line-height: 1.6em;
position: relative;
border-radius: 8px; border-radius: 8px;
::v-deep a.widget-default { ::v-deep a.widget-default {
@ -304,7 +303,7 @@ export default {
} }
.post-visibility { .post-visibility {
opacity: 0.5; color: var(--color-text-lighter);
background-position: right; background-position: right;
} }

Wyświetl plik

@ -26,8 +26,11 @@ Vue.prototype.OC = window.OC
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
OCA.Dashboard.register('social_notifications', (el, { widget }) => { OCA.Dashboard.register('social_notifications', (el, { widget }) => {
const View = Vue.extend(Dashboard) const View = Vue.extend(Dashboard)
/* eslint-disable-next-line no-new */
new View({ new View({
propsData: { title: widget.title }, propsData: { title: widget.title },
}).$mount(el) el,
name: 'SocialDashboard',
})
}) })
}) })

Wyświetl plik

@ -57,7 +57,10 @@ Vue.use(VueMasonry)
/* eslint-disable-next-line no-new */ /* eslint-disable-next-line no-new */
new Vue({ new Vue({
el: '#content',
// eslint-disable-next-line vue/match-component-file-name
name: 'SocialRoot',
router, router,
render: h => h(App), render: h => h(App),
store, store,
}).$mount('#content') })

Wyświetl plik

@ -0,0 +1,32 @@
import { translate } from '@nextcloud/l10n'
/**
* @param {import("../types/Mastodon").Notification} notification
* @return {string}
*/
export function notificationSummary(notification) {
switch (notification.type) {
case 'mention':
return translate('social', '{account} mentioned you', { account: notification.account.acct })
case 'status':
return translate('social', '{account} posted a status', { account: notification.account.acct })
case 'reblog':
return translate('social', '{account} boosted your post', { account: notification.account.acct })
case 'follow':
return translate('social', '{account} started to follow you', { account: notification.account.acct })
case 'follow_request':
return translate('social', '{account} requested to follow you', { account: notification.account.acct })
case 'favourite':
return translate('social', '{account} liked your post', { account: notification.account.acct })
case 'poll':
return translate('social', '{account} ended the poll', { account: notification.account.acct })
case 'update':
return translate('social', '{account} edited a status', { account: notification.account.acct })
case 'admin.sign_up':
return translate('social', '{account} signed up', { account: notification.account.acct })
case 'admin.report':
return translate('social', '{account} filed a report', { account: notification.account.acct })
default:
return ''
}
}

Wyświetl plik

@ -47,6 +47,7 @@ import { generateUrl } from '@nextcloud/router'
import { showError } from '@nextcloud/dialogs' import { showError } from '@nextcloud/dialogs'
import NcDashboardWidget from '@nextcloud/vue/dist/Components/NcDashboardWidget.js' import NcDashboardWidget from '@nextcloud/vue/dist/Components/NcDashboardWidget.js'
import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js' import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js'
import { notificationSummary } from '../services/notifications.js'
export default { export default {
name: 'Dashboard', name: 'Dashboard',
@ -121,25 +122,19 @@ export default {
}, },
methods: { methods: {
fetchNotifications() { async fetchNotifications() {
const req = { const url = generateUrl('apps/social/api/v1/notifications')
params: {
limit: 10, try {
},
} const response = await axios.get(url)
const url = generateUrl('/apps/social/api/v1/stream/notifications') if (response.data) {
// TODO check why 'since' param is in fact 'until' this.processNotifications(response.data)
/* 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' this.state = 'ok'
} else { } else {
this.state = 'error' this.state = 'error'
} }
}).catch((error) => { } catch (error) {
clearInterval(this.loop) clearInterval(this.loop)
if (error.response?.status && error.response.status >= 400) { if (error.response?.status && error.response.status >= 400) {
showError(t('social', 'Failed to get Social notifications')) showError(t('social', 'Failed to get Social notifications'))
@ -148,8 +143,9 @@ export default {
// there was an error in notif processing // there was an error in notif processing
console.error(error) console.error(error)
} }
}) }
}, },
/** @param {import('../types/Mastodon.js').Notification[]} newNotifications */
processNotifications(newNotifications) { processNotifications(newNotifications) {
if (this.lastTimestamp !== 0) { if (this.lastTimestamp !== 0) {
// just add those which are more recent than our most recent one // just add those which are more recent than our most recent one
@ -166,55 +162,46 @@ export default {
this.notifications = this.filter(newNotifications) this.notifications = this.filter(newNotifications)
} }
}, },
/** @param {import('../types/Mastodon.js').Notification[]} notifications */
filter(notifications) { filter(notifications) {
return notifications return notifications
// TODO check if we need to filter
/* return notifications.filter((n) => {
return (n.type === 'something' || n.subtype === 'somethingElse')
}) */
}, },
/** @param {import('../types/Mastodon.js').Notification} n */
getMainText(n) { getMainText(n) {
if (n.subtype === 'Follow') { return notificationSummary(n)
return t('social', '{account} is following you', { account: this.getActorName(n) })
}
if (n.subtype === 'Like') {
return t('social', '{account} liked your post', { account: this.getActorName(n) })
}
}, },
/** @param {import('../types/Mastodon.js').Notification} n */
getAvatarUrl(n) { getAvatarUrl(n) {
return undefined return n.account.avatar
// TODO get external and internal avatars
/* return this.getActorAccountName(n)
? generateUrl('???')
: undefined */
}, },
/** @param {import('../types/Mastodon.js').Notification} n */
getActorName(n) { getActorName(n) {
return n.actor_info && n.actor_info.type === 'Person' && n.actor_info.preferredUsername return n.account.display_name
? n.actor_info.preferredUsername
: ''
}, },
/** @param {import('../types/Mastodon.js').Notification} n */
getActorAccountName(n) { getActorAccountName(n) {
return n.actor_info && n.actor_info.type === 'Person' && n.actor_info.account return n.account.acct
? n.actor_info.account
: ''
}, },
/** @param {import('../types/Mastodon.js').Notification} n */
getNotificationTarget(n) { getNotificationTarget(n) {
if (n.subtype === 'Follow') { if (n.type === 'follow') {
return generateUrl('/apps/social/@' + this.getActorAccountName(n) + '/') return generateUrl('/apps/social/@' + this.getActorAccountName(n) + '/')
} }
return this.showMoreUrl return this.showMoreUrl
}, },
/** @param {import('../types/Mastodon.js').Notification} n */
getSubline(n) { getSubline(n) {
if (n.subtype === 'Follow') { if (n.type === 'follow') {
return this.getActorAccountName(n) return this.getActorAccountName(n)
} }
if (n.subtype === 'Like') { if (n.type === 'favourite') {
return this.getActorAccountName(n) return this.getActorAccountName(n)
} }
return '' return ''
}, },
/** @param {import('../types/Mastodon.js').Notification} n */
getNotificationTypeImage(n) { getNotificationTypeImage(n) {
if (n.subtype === 'Follow') { if (n.type === 'follow') {
return generateUrl('/svg/social/add_user') return generateUrl('/svg/social/add_user')
} }
return '' return ''

Wyświetl plik

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<h2>Social</h2> <h2>Social</h2>
<transition-group name="list" tag="div"> <transition-group name="list" tag="ul">
<TimelineEntry v-for="entry in timeline" <TimelineEntry v-for="entry in timeline"
:key="entry.id" :key="entry.id"
:item="entry" /> :item="entry" />
@ -42,16 +42,14 @@ export default {
beforeMount() { beforeMount() {
const uid = this.userId const uid = this.userId
axios.get(generateUrl(`apps/social/api/v1/account/${uid}/info`)).then((response) => { axios.get(generateUrl(`apps/social/api/v1/global/account/info?account=${uid}`)).then(({ data }) => {
this.accountInfo = response.data.result.account this.accountInfo = data
logger.log(this.accountInfo) logger.log(this.accountInfo)
}) })
const since = Math.floor(Date.now() / 1000) + 1 axios.get(generateUrl(`apps/social/api/v1/accounts/${uid}/statuses`)).then(({ data }) => {
this.timeline = data
axios.get(generateUrl(`apps/social/api/v1/account/${uid}/stream?limit=25&since=${since}`)).then(({ data }) => {
logger.log(this.timeline) logger.log(this.timeline)
this.timeline = data.result
}) })
}, },
} }