summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJonas Sulzer <jonas@violoncello.ch>2019-05-28 13:03:14 +0200
committerJonas Sulzer <jonas@violoncello.ch>2019-05-28 13:03:14 +0200
commitbc634c42a8713a7ba7e76e76dd924ef1f7d94bbe (patch)
treea805e8ad0feca8b83930b43baead5990f1be3080 /src
parent169b4927e570c8830870a9a4138743bc737d0dbe (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.vue215
-rw-r--r--src/components/TimelineEntry.vue208
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>