diff options
author | Jonas Sulzer <jonas@violoncello.ch> | 2019-05-28 13:03:14 +0200 |
---|---|---|
committer | Jonas Sulzer <jonas@violoncello.ch> | 2019-05-28 13:03:14 +0200 |
commit | bc634c42a8713a7ba7e76e76dd924ef1f7d94bbe (patch) | |
tree | a805e8ad0feca8b83930b43baead5990f1be3080 /src | |
parent | 169b4927e570c8830870a9a4138743bc737d0dbe (diff) |
📦 NEW: display boosted posts with an according indication
Signed-off-by: Jonas Sulzer <jonas@violoncello.ch>
Diffstat (limited to 'src')
-rw-r--r-- | src/components/TimelineContent.vue | 215 | ||||
-rw-r--r-- | src/components/TimelineEntry.vue | 208 |
2 files changed, 246 insertions, 177 deletions
diff --git a/src/components/TimelineContent.vue b/src/components/TimelineContent.vue new file mode 100644 index 00000000..e2423425 --- /dev/null +++ b/src/components/TimelineContent.vue @@ -0,0 +1,215 @@ +<template> + <div class="entry-content"> + <div v-if="item.actor_info" class="post-avatar"> + <avatar v-if="item.local" :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-author-wrapper"> + <router-link v-if="item.actor_info" :to="{ name: 'profile', params: { account: item.local ? 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 class="post-message" v-html="formatedMessage" /> + <div 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" /> + <div v-if="popoverMenu.length > 0" v-tooltip.bottom="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> + </div> + <div> + <div :data-timestamp="timestamp" class="post-timestamp live-relative-timestamp"> + {{ relativeTimestamp }} + </div> + </div> + </div> +</template> + +<script> +import Avatar from 'nextcloud-vue/dist/Components/Avatar' +import * as linkify from 'linkifyjs' +import pluginTag from 'linkifyjs/plugins/hashtag' +import pluginMention from 'linkifyjs/plugins/mention' +import 'linkifyjs/string' +import popoverMenu from './../mixins/popoverMenu' +import currentUser from './../mixins/currentUserMixin' + +pluginTag(linkify) +pluginMention(linkify) + +export default { + name: 'TimelineContent', + components: { + Avatar + }, + mixins: [popoverMenu, currentUser], + props: { + item: { type: Object, default: () => {} } + }, + data() { + return { + } + }, + computed: { + popoverMenu() { + var actions = [ + ] + if (this.item.actor_info.account === this.cloudId) { + actions.push( + { + action: () => { + this.$store.dispatch('postDelete', this.item) + this.hidePopoverMenu() + }, + icon: 'icon-delete', + text: t('social', 'Delete post') + } + ) + } + return actions + }, + relativeTimestamp() { + return OC.Util.relativeModifiedDate(this.item.published) + }, + timestamp() { + return Date.parse(this.item.published) + }, + formatedMessage() { + let message = this.item.content + if (typeof message === 'undefined') { + return '' + } + message = message.replace(/(?:\r\n|\r|\n)/g, '<br />') + message = message.linkify({ + formatHref: { + hashtag: function(href) { + return OC.generateUrl('/apps/social/timeline/tags/' + href.substring(1)) + }, + mention: function(href) { + return OC.generateUrl('/apps/social/@' + href.substring(1)) + } + } + }) + message = this.$twemoji.parse(message) + return message + }, + avatarUrl() { + return OC.generateUrl('/apps/social/api/v1/global/actor/avatar?id=' + this.item.attributedTo) + }, + isBoosted() { + if (typeof this.item.action === 'undefined') { + return false + } + return !!this.item.action.values.boosted + } + }, + methods: { + userDisplayName(actorInfo) { + return actorInfo.name !== '' ? actorInfo.name : actorInfo.preferredUsername + }, + reply() { + this.$root.$emit('composer-reply', this.item) + }, + boost() { + if (this.isBoosted) { + this.$store.dispatch('postUnBoost', this.item) + } else { + this.$store.dispatch('postBoost', this.item) + } + } + } +} +</script> +<style scoped lang="scss"> + .post-author { + font-weight: bold; + } + + .post-author-id { + 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; + flex-shrink: 0; + } + + .post-actions { + margin-left: -13px; + height: 44px; + + .post-actions-more { + position: relative; + width: 44px; + height: 34px; + display: inline-block; + } + .icon-reply, + .icon-boost, + .icon-boosted, + .icon-more { + display: inline-block; + width: 44px; + height: 34px; + opacity: .5; + &:hover, &:focus { + opacity: 1; + } + } + .icon-boosted { + opacity: 1; + } + } + + span { + /* opacity: 0.5; */ + } + .entry-content { + display: flex; + } + + .post-content { + flex-grow: 1; + } + + .post-timestamp { + opacity: .7; + } +</style> +<style> + .post-message a { + text-decoration: underline; + } +</style> diff --git a/src/components/TimelineEntry.vue b/src/components/TimelineEntry.vue index d9d64e92..fa3d555b 100644 --- a/src/components/TimelineEntry.vue +++ b/src/components/TimelineEntry.vue @@ -1,70 +1,31 @@ <template> <div class="timeline-entry"> - <div class="entry-content"> - <div v-if="item.actor_info" class="post-avatar"> - <avatar v-if="item.local" :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-author-wrapper"> - <router-link v-if="item.actor_info" :to="{ name: 'profile', params: { account: item.local ? 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 class="post-message" v-html="formatedMessage" /> - <div 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" /> - <div v-if="popoverMenu.length > 0" v-tooltip.bottom="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> - </div> - <div> - <div :data-timestamp="timestamp" class="post-timestamp live-relative-timestamp"> - {{ relativeTimestamp }} - </div> - </div> + <div v-if="item.type === 'Announce'" class="boost"> + <span class="icon-container"><span class="icon-boost"></span></span> + <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-content :item="entryContent" /> </div> </template> <script> -import Avatar from 'nextcloud-vue/dist/Components/Avatar' -import * as linkify from 'linkifyjs' -import pluginTag from 'linkifyjs/plugins/hashtag' -import pluginMention from 'linkifyjs/plugins/mention' -import 'linkifyjs/string' -import popoverMenu from './../mixins/popoverMenu' -import currentUser from './../mixins/currentUserMixin' - -pluginTag(linkify) -pluginMention(linkify) +import TimelineContent from './TimelineContent.vue' export default { name: 'TimelineEntry', components: { - Avatar + TimelineContent }, - mixins: [popoverMenu, currentUser], props: { item: { type: Object, default: () => {} } }, @@ -73,71 +34,21 @@ export default { } }, computed: { - popoverMenu() { - var actions = [ - ] - if (this.item.actor_info.account === this.cloudId) { - actions.push( - { - action: () => { - this.$store.dispatch('postDelete', this.item) - this.hidePopoverMenu() - }, - icon: 'icon-delete', - text: t('social', 'Delete post') - } - ) - } - return actions - }, - relativeTimestamp() { - return OC.Util.relativeModifiedDate(this.item.published) - }, - timestamp() { - return Date.parse(this.item.published) - }, - formatedMessage() { - let message = this.item.content - if (typeof message === 'undefined') { - return '' + entryContent() { + if (this.item.type === 'Announce') { + return this.item.cache[this.item.object].object + } else { + return this.item } - message = message.replace(/(?:\r\n|\r|\n)/g, '<br />') - message = message.linkify({ - formatHref: { - hashtag: function(href) { - return OC.generateUrl('/apps/social/timeline/tags/' + href.substring(1)) - }, - mention: function(href) { - return OC.generateUrl('/apps/social/@' + href.substring(1)) - } - } - }) - message = this.$twemoji.parse(message) - return message - }, - avatarUrl() { - return OC.generateUrl('/apps/social/api/v1/global/actor/avatar?id=' + this.item.attributedTo) + }, - isBoosted() { - if (typeof this.item.action === 'undefined') { - return false - } - return !!this.item.action.values.boosted + boosted() { + return t('social', 'boosted') } }, methods: { userDisplayName(actorInfo) { return actorInfo.name !== '' ? actorInfo.name : actorInfo.preferredUsername - }, - reply() { - this.$root.$emit('composer-reply', this.item) - }, - boost() { - if (this.isBoosted) { - this.$store.dispatch('postUnBoost', this.item) - } else { - this.$store.dispatch('postBoost', this.item) - } } } } @@ -148,6 +59,13 @@ export default { margin-bottom: 10px; } + .icon-boost { + display: inline-block; + width: 44px; + height: 17px; + opacity: .5; + } + .post-author { font-weight: bold; } @@ -155,68 +73,4 @@ export default { .post-author-id { 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; - flex-shrink: 0; - } - - .post-actions { - margin-left: -13px; - height: 44px; - - .post-actions-more { - position: relative; - width: 44px; - height: 34px; - display: inline-block; - } - .icon-reply, - .icon-boost, - .icon-boosted, - .icon-more { - display: inline-block; - width: 44px; - height: 34px; - opacity: .5; - &:hover, &:focus { - opacity: 1; - } - } - .icon-boosted { - opacity: 1; - } - } - - span { - /* opacity: 0.5; */ - } - .entry-content { - display: flex; - } - - .post-content { - flex-grow: 1; - } - - .post-timestamp { - opacity: .7; - } -</style> -<style> - .post-message a { - text-decoration: underline; - } </style> |