summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMaxence Lange <maxence@artificial-owl.com>2020-10-19 10:04:15 -0100
committerGitHub <noreply@github.com>2020-10-19 10:04:15 -0100
commit37fa47804839e12f8b29e23106fedf2b82b0d9a7 (patch)
treece050bc128b43503228631bcc04ec20448d018cc /src
parente659931aab6ce6a96792c8642ac74c95f353e54b (diff)
parentccb6196a71a2fc2bc4db877a53331db1ec919885 (diff)
Merge pull request #748 from nextcloud/timeline-to-grid
👌 IMPROVE: move timeline to css grid layout and restructure code
Diffstat (limited to 'src')
-rw-r--r--src/components/MessageContent.js3
-rw-r--r--src/components/TimelineAvatar.vue44
-rw-r--r--src/components/TimelineEntry.vue112
-rw-r--r--src/components/TimelinePost.vue107
4 files changed, 174 insertions, 92 deletions
diff --git a/src/components/MessageContent.js b/src/components/MessageContent.js
index ff4e23cf..d8fe78a8 100644
--- a/src/components/MessageContent.js
+++ b/src/components/MessageContent.js
@@ -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')
diff --git a/src/components/TimelineAvatar.vue b/src/components/TimelineAvatar.vue
new file mode 100644
index 00000000..25104274
--- /dev/null
+++ b/src/components/TimelineAvatar.vue
@@ -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>
diff --git a/src/components/TimelineEntry.vue b/src/components/TimelineEntry.vue
index 61833343..97f714c6 100644
--- a/src/components/TimelineEntry.vue
+++ b/src/components/TimelineEntry.vue
@@ -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;
diff --git a/src/components/TimelinePost.vue b/src/components/TimelinePost.vue
index 411dc5ff..2cfe98c4 100644
--- a/src/components/TimelinePost.vue
+++ b/src/components/TimelinePost.vue
@@ -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;