Merge pull request #748 from nextcloud/timeline-to-grid

👌 IMPROVE: move timeline to css grid layout and restructure code
pull/1068/head
Maxence Lange 2020-10-19 10:04:15 -01:00 zatwierdzone przez GitHub
commit 37fa478048
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
6 zmienionych plików z 177 dodań i 97 usunięć

Wyświetl plik

@ -50,7 +50,7 @@ return [
['name' => 'Navigation#resizedGetPublic', 'url' => '/document/public/resized', 'verb' => 'GET'],
['name' => 'ActivityPub#actor', 'url' => '/users/{username}', 'verb' => 'GET'],
['name' => 'ActivityPub#actorAlias', 'url' => '/@{username}', 'verb' => 'GET'],
['name' => 'ActivityPub#actorAlias', 'url' => '/@{username}/', 'verb' => 'GET'],
['name' => 'ActivityPub#inbox', 'url' => '/@{username}/inbox', 'verb' => 'POST'],
['name' => 'ActivityPub#getInbox', 'url' => '/@{username}/inbox', 'verb' => 'GET'],
['name' => 'ActivityPub#sharedInbox', 'url' => '/inbox', 'verb' => 'POST'],

Wyświetl plik

@ -24,6 +24,9 @@ export default Vue.component('MessageContent', {
* All attributes other than `href` for links are stripped from the source
*/
export function formatMessage(createElement, source) {
if (!source.tag) {
source.tag = []
}
let mentions = source.tag.filter(tag => tag.type === 'Mention')
let hashtags = source.tag.filter(tag => tag.type === 'Hashtag')

Wyświetl plik

@ -0,0 +1,44 @@
<template>
<div v-if="item.actor_info" class="post-avatar">
<avatar v-if="item.local" :size="32" :user="userTest"
:display-name="item.actor_info.account" :disable-tooltip="true" />
<avatar v-else :size="32" :url="avatarUrl"
:disable-tooltip="true" />
</div>
</template>
<script>
import Avatar from '@nextcloud/vue/dist/Components/Avatar'
export default {
name: 'TimelineAvatar',
components: {
Avatar
},
props: {
item: { type: Object, default: () => {} }
},
computed: {
userTest() {
return this.item.actor_info.preferredUsername
},
avatarUrl() {
return OC.generateUrl('/apps/social/api/v1/global/actor/avatar?id=' + this.item.attributedTo)
}
}
}
</script>
<style lang="scss" scoped>
.post-avatar {
margin: 5px;
margin-right: 10px;
border-radius: 50%;
overflow: hidden;
width: 32px;
height: 32px;
min-width: 32px;
align-self: start;
}
</style>

Wyświetl plik

@ -1,41 +1,50 @@
<template>
<div class="timeline-entry">
<div v-if="item.type === 'SocialAppNotification'">
{{ actionSummary }}
</div>
<div v-if="item.type === 'Announce'" class="boost">
<div class="container-icon-boost">
<div :class="['timeline-entry', hasHeader ? 'with-header' : '']">
<template v-if="item.type === 'SocialAppNotification'">
<div class="notification-icon" :class="notificationIcon" />
<span class="notification-action">
{{ actionSummary }}
</span>
</template>
<template v-else-if="item.type === 'Announce'">
<div class="container-icon-boost boost">
<span class="icon-boost" />
</div>
<router-link v-if="item.actor_info" :to="{ name: 'profile', params: { account: item.local ? item.actor_info.preferredUsername : item.actor_info.account }}">
<span v-tooltip.bottom="item.actor_info.account" class="post-author">
{{ userDisplayName(item.actor_info) }}
</span>
</router-link>
<a v-else :href="item.attributedTo">
<span class="post-author-id">
{{ item.attributedTo }}
</span>
</a>
{{ boosted }}
</div>
<timeline-post
v-if="item.type === 'SocialAppNotification' && item.details.post"
:item="item.details.post" />
<timeline-post
v-else
:item="entryContent"
:parent-announce="isBoost" />
<div class="boost">
<router-link v-if="item.actor_info" :to="{ name: 'profile', params: { account: item.local ? item.actor_info.preferredUsername : item.actor_info.account }}">
<span v-tooltip.bottom="item.actor_info.account" class="post-author">
{{ userDisplayName(item.actor_info) }}
</span>
</router-link>
<a v-else :href="item.attributedTo">
<span class="post-author-id">
{{ item.attributedTo }}
</span>
</a>
{{ boosted }}
</div>
</template>
<user-entry v-if="item.type === 'SocialAppNotification' && item.details.actor" :key="item.details.actor.id" :item="item.details.actor" />
<template v-else>
<timeline-avatar :item="entryContent" />
<timeline-post
:item="entryContent"
:parent-announce="isBoost" />
</template>
</div>
</template>
<script>
import TimelinePost from './TimelinePost.vue'
import TimelineAvatar from './TimelineAvatar.vue'
import UserEntry from './UserEntry.vue'
export default {
name: 'TimelineEntry',
components: {
TimelinePost
TimelinePost,
TimelineAvatar,
UserEntry
},
props: {
item: { type: Object, default: () => {} }
@ -48,6 +57,8 @@ export default {
entryContent() {
if (this.item.type === 'Announce') {
return this.item.cache[this.item.object].object
} else if (this.item.type === 'SocialAppNotification') {
return this.item.details.post
} else {
return this.item
}
@ -58,6 +69,9 @@ export default {
}
return {}
},
hasHeader() {
return this.item.type === 'Announce' || this.item.type === 'SocialAppNotification'
},
boosted() {
return t('social', 'boosted')
},
@ -101,13 +115,59 @@ export default {
}
</script>
<style scoped lang="scss">
.timeline-entry.with-header {
grid-template-rows: 30px 1fr;
}
.timeline-entry {
display: grid;
grid-template-columns: 44px 1fr;
grid-template-rows: 1fr;
padding: 10px;
margin-bottom: 10px;
&:hover {
background-color: var(--color-background-hover);
}
}
.notification-header {
display: flex;
align-items: bottom;
}
.notification-action {
flex-grow: 1;
display: inline-block;
grid-row: 1;
grid-column: 2;
}
.notification-icon {
opacity: .5;
background-position: center;
background-size: contain;
overflow: hidden;
height: 20px;
min-width: 32px;
flex-shrink: 0;
display: inline-block;
vertical-align: middle;
grid-column: 1;
grid-row: 1;
}
.icon-boost {
display: inline-block;
vertical-align: middle;
}
.icon-favorite {
display: inline-block;
vertical-align: middle;
}
.icon-user {
display: inline-block;
vertical-align: middle;
}
.container-icon-boost {
display: inline-block;

Wyświetl plik

@ -1,55 +1,47 @@
<template>
<div class="entry-content">
<div v-if="item.actor_info" class="post-avatar">
<avatar v-if="item.local && item.type!=='SocialAppNotification'" :size="32" :user="item.actor_info.preferredUsername"
:display-name="item.actor_info.account" :disable-tooltip="true" />
<avatar v-else :size="32" :url="avatarUrl"
:disable-tooltip="true" />
</div>
<div class="post-content">
<div class="post-header">
<div class="post-author-wrapper">
<router-link v-if="item.actor_info"
:to="{ name: 'profile',
params: { account: (item.local && item.type!=='SocialAppNotification') ? item.actor_info.preferredUsername : item.actor_info.account }
}">
<span class="post-author">
{{ userDisplayName(item.actor_info) }}
</span>
<span class="post-author-id">
@{{ item.actor_info.account }}
</span>
</router-link>
<a v-else :href="item.attributedTo">
<span class="post-author-id">
{{ item.attributedTo }}
</span>
</a>
</div>
<a :data-timestamp="timestamp" class="post-timestamp live-relative-timestamp" @click="getSinglePostTimeline">
{{ relativeTimestamp }}
<div class="post-content">
<div class="post-header">
<div class="post-author-wrapper">
<router-link v-if="item.actor_info"
:to="{ name: 'profile',
params: { account: (item.local && item.type!=='SocialAppNotification') ? item.actor_info.preferredUsername : item.actor_info.account }
}">
<span class="post-author">
{{ userDisplayName(item.actor_info) }}
</span>
<span class="post-author-id">
@{{ item.actor_info.account }}
</span>
</router-link>
<a v-else :href="item.attributedTo">
<span class="post-author-id">
{{ item.attributedTo }}
</span>
</a>
</div>
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-if="item.content" class="post-message">
<MessageContent :source="source" />
</div>
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-else class="post-message" v-html="item.actor_info.summary" />
<div v-if="hasAttachments" class="post-attachments">
<post-attachment :attachments="item.attachment" />
</div>
<div v-if="this.$route.params.type!=='notifications' && !serverData.public" v-click-outside="hidePopoverMenu" class="post-actions">
<a v-tooltip.bottom="t('social', 'Reply')" class="icon-reply" @click.prevent="reply" />
<a v-if="item.actor_info.account !== cloudId" v-tooltip.bottom="t('social', 'Boost')"
:class="(isBoosted) ? 'icon-boosted' : 'icon-boost'"
@click.prevent="boost" />
<a v-tooltip.bottom="t('social', 'Like')" :class="(isLiked) ? 'icon-starred' : 'icon-favorite'" @click.prevent="like" />
<div v-if="popoverMenu.length > 0" v-tooltip.bottom="menuOpened ? '' : t('social', 'More actions')" class="post-actions-more">
<a class="icon-more" @click.prevent="togglePopoverMenu" />
<div :class="{open: menuOpened}" class="popovermenu menu-center">
<popover-menu :menu="popoverMenu" />
</div>
<a :data-timestamp="timestamp" class="post-timestamp live-relative-timestamp" @click="getSinglePostTimeline">
{{ relativeTimestamp }}
</a>
</div>
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-if="item.content" class="post-message">
<MessageContent :source="source" />
</div>
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-else class="post-message" v-html="item.actor_info.summary" />
<div v-if="hasAttachments" class="post-attachments">
<post-attachment :attachments="item.attachment" />
</div>
<div v-if="this.$route.params.type!=='notifications' && !serverData.public" v-click-outside="hidePopoverMenu" class="post-actions">
<a v-tooltip.bottom="t('social', 'Reply')" class="icon-reply" @click.prevent="reply" />
<a v-if="item.actor_info.account !== cloudId" v-tooltip.bottom="t('social', 'Boost')"
:class="(isBoosted) ? 'icon-boosted' : 'icon-boost'"
@click.prevent="boost" />
<a v-tooltip.bottom="t('social', 'Like')" :class="(isLiked) ? 'icon-starred' : 'icon-favorite'" @click.prevent="like" />
<div v-if="popoverMenu.length > 0" v-tooltip.bottom="menuOpened ? '' : t('social', 'More actions')" class="post-actions-more">
<a class="icon-more" @click.prevent="togglePopoverMenu" />
<div :class="{open: menuOpened}" class="popovermenu menu-center">
<popover-menu :menu="popoverMenu" />
</div>
</div>
</div>
@ -57,7 +49,6 @@
</template>
<script>
import Avatar from '@nextcloud/vue/dist/Components/Avatar'
import * as linkify from 'linkifyjs'
import pluginMention from 'linkifyjs/plugins/mention'
import 'linkifyjs/string'
@ -74,7 +65,6 @@ pluginMention(linkify)
export default {
name: 'TimelinePost',
components: {
Avatar,
PostAttachment,
MessageContent
},
@ -207,17 +197,6 @@ export default {
opacity: .7;
}
.post-avatar {
margin: 5px;
margin-right: 10px;
border-radius: 50%;
overflow: hidden;
width: 32px;
height: 32px;
min-width: 32px;
flex-shrink: 0;
}
.post-timestamp {
width: 120px;
text-align: right;
@ -260,10 +239,6 @@ export default {
display: flex;
}
.post-content {
flex-grow: 1;
}
.post-header {
display: flex;
flex-direction: row;

Wyświetl plik

@ -50,9 +50,7 @@ module.exports = {
},
plugins: [new VueLoaderPlugin()],
resolve: {
alias: {
vue$: 'vue/dist/vue.esm.js'
},
extensions: ['*', '.js', '.vue', '.json']
extensions: ['*', '.js', '.vue'],
symlinks: false
}
};