diff options
author | Julius Härtl <jus@bitgrid.net> | 2019-01-10 19:42:05 +0100 |
---|---|---|
committer | Julius Härtl <jus@bitgrid.net> | 2019-02-20 21:05:24 +0100 |
commit | 80b7c572215e4f7d2efabbe88a5669140d33834a (patch) | |
tree | 4020b23e4e32e52ce541d30b350cd19db387a03f /src | |
parent | a4a5b29550cbe75d4fd0ea4b0c1eed5687821f41 (diff) |
Add UI to reply to a post
Signed-off-by: Julius Härtl <jus@bitgrid.net>
Diffstat (limited to 'src')
-rw-r--r-- | src/components/ActorAvatar.vue | 53 | ||||
-rw-r--r-- | src/components/Composer.vue | 52 | ||||
-rw-r--r-- | src/components/TimelineEntry.vue | 47 | ||||
-rw-r--r-- | src/main.js | 3 | ||||
-rw-r--r-- | src/mixins/popoverMenu.js | 42 |
5 files changed, 189 insertions, 8 deletions
diff --git a/src/components/ActorAvatar.vue b/src/components/ActorAvatar.vue new file mode 100644 index 00000000..9621e2ce --- /dev/null +++ b/src/components/ActorAvatar.vue @@ -0,0 +1,53 @@ +<!-- + - @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> + - + - @author Julius Härtl <jus@bitgrid.net> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> + +<template> + <avatar v-if="actor.local" :size="size" :user="actor.preferredUsername" + :display-name="actor.account" :disable-tooltip="true" /> + <avatar v-else :size="size" :url="avatarUrl" + :disable-tooltip="true" /> +</template> + +<script> +import { Avatar } from 'nextcloud-vue' + +export default { + name: 'ActorAvatar', + components: { + Avatar + }, + props: { + actor: { type: Object, default: () => {} }, + size: { type: Number, default: 32 } + }, + data: function() { + return { + followingText: t('social', 'Following') + } + }, + computed: { + avatarUrl() { + return OC.generateUrl('/apps/social/api/v1/global/actor/avatar?id=' + this.item.attributedTo) + } + } +} +</script> diff --git a/src/components/Composer.vue b/src/components/Composer.vue index c4767aa6..7ec53222 100644 --- a/src/components/Composer.vue +++ b/src/components/Composer.vue @@ -34,11 +34,22 @@ </span> </div> </div> + <div v-if="replyTo" class="reply-to"> + <p> + <span>In reply to</span> + <actor-avatar :actor="replyTo.actor_info" :size="16" /> + <strong>{{ replyTo.actor_info.account }}</strong> + <a class="icon-close" @click="replyTo=null" /> + </p> + <div class="reply-to-preview"> + {{ replyTo.content }} + </div> + </div> <form class="new-post-form" @submit.prevent="createPost"> <vue-tribute :options="tributeOptions"> <!-- eslint-disable-next-line vue/valid-v-model --> - <div ref="composerInput" v-contenteditable:post.dangerousHTML="canType" class="message" - placeholder="What would you like to share?" @keyup.enter="keyup" /> + <div ref="composerInput" v-contenteditable:post.dangerousHTML="canType && !loading" class="message" + placeholder="What would you like to share?" :class="{'icon-loading': loading}" @keyup.enter="keyup" /> </vue-tribute> <emoji-picker ref="emojiPicker" :search="search" class="emoji-picker-wrapper" @emoji="insert"> @@ -107,6 +118,25 @@ } } + .reply-to { + background-image: url(../../img/reply.svg); + background-position: 5px 5px; + background-repeat: no-repeat; + margin-left: 39px; + margin-bottom: 20px; + overflow: hidden; + background-color: #fafafa; + border-radius: 3px; + padding: 5px; + padding-left: 30px; + .icon-close { + display: inline-block; + float: right; + opacity: .7; + padding: 3px; + } + } + .new-post-form { flex-grow: 1; position: relative; @@ -309,12 +339,14 @@ import { VTooltip } from 'v-tooltip' import CurrentUserMixin from './../mixins/currentUserMixin' import FocusOnCreate from '../directives/focusOnCreate' import axios from 'nextcloud-axios' +import ActorAvatar from './ActorAvatar' export default { name: 'Composer', components: { PopoverMenu, Avatar, + ActorAvatar, EmojiPicker, VueTribute }, @@ -330,9 +362,11 @@ export default { data() { return { type: localStorage.getItem('social.lastPostType') || 'followers', + loading: false, post: '', canType: true, search: '', + replyTo: null, tributeOptions: { lookup: function(item) { return item.key + item.value @@ -463,6 +497,11 @@ export default { ] } }, + mounted() { + this.$root.$on('composer-reply', (data) => { + this.replyTo = data + }) + }, methods: { insert(emoji) { if (typeof emoji === 'object') { @@ -513,12 +552,16 @@ export default { } } while (match) - return { + let data = { content: content, to: to, hashtags: hashtags, type: this.type } + if (this.replyTo) { + data.replyTo = this.replyTo.id + } + return data }, keyup(event) { if (event.shiftKey) { @@ -526,7 +569,10 @@ export default { } }, createPost(event) { + this.loading = true this.$store.dispatch('post', this.getPostData()).then((response) => { + this.loading = false + this.replyTo = null this.post = '' this.$refs.composerInput.innerText = this.post this.$store.dispatch('refreshTimeline') diff --git a/src/components/TimelineEntry.vue b/src/components/TimelineEntry.vue index 5cabdbac..ed9dd7b0 100644 --- a/src/components/TimelineEntry.vue +++ b/src/components/TimelineEntry.vue @@ -26,8 +26,16 @@ <!-- eslint-disable-next-line vue/no-v-html --> <div class="post-message" v-html="formatedMessage" /> </div> - <div :data-timestamp="timestamp" class="post-timestamp live-relative-timestamp"> - {{ relativeTimestamp }} + <div> + <div :data-timestamp="timestamp" class="post-timestamp live-relative-timestamp"> + {{ relativeTimestamp }} + </div> + <div v-click-outside="hidePopoverMenu" class="post-actions"> + <a class="icon-more" @click.prevent="togglePopoverMenu" /> + <div :class="{open: menuOpened}" class="popovermenu menu-center"> + <popover-menu :menu="popoverMenu" /> + </div> + </div> </div> </div> </div> @@ -39,6 +47,7 @@ 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' pluginTag(linkify) pluginMention(linkify) @@ -48,15 +57,32 @@ export default { components: { Avatar }, + mixins: [popoverMenu], props: { item: { type: Object, default: () => {} } }, data() { return { - } }, computed: { + popoverMenu() { + var actions = [ + { + action: () => { this.$root.$emit('composer-reply', this.item) }, + icon: 'icon-comment', + text: t('social', 'Reply to post') + } + ] + actions.push( + { + action: () => { }, + icon: 'icon-delete', + text: t('social', 'Delete post') + } + ) + return actions + }, relativeTimestamp() { return OC.Util.relativeModifiedDate(this.item.published) }, @@ -90,7 +116,7 @@ export default { } } </script> -<style scoped> +<style scoped lang="scss"> .timeline-entry { padding: 10px; margin-bottom: 10px; @@ -125,6 +151,19 @@ export default { flex-shrink: 0; } + .post-actions { + position: relative; + width: 44px; + height: 44px; + float: right; + + .icon-more { + display: inline-block; + width: 44px; + height: 44px; + } + } + span { /* opacity: 0.5; */ } diff --git a/src/main.js b/src/main.js index 79a1039e..f1059a7e 100644 --- a/src/main.js +++ b/src/main.js @@ -28,7 +28,7 @@ import store from './store' import router from './router' import vuetwemoji from 'vue-twemoji' import contenteditableDirective from 'vue-contenteditable-directive' - +import ClickOutside from 'vue-click-outside' sync(store, router) // CSP config for webpack dynamic chunk loading @@ -45,6 +45,7 @@ Vue.prototype.n = n Vue.prototype.OC = OC Vue.prototype.OCA = OCA +Vue.directive('ClickOutside', ClickOutside) Vue.use(contenteditableDirective) Vue.use(vuetwemoji, { baseUrl: OC.linkTo('social', 'img/'), // can set to local folder of emojis. default: https://twemoji.maxcdn.com/ diff --git a/src/mixins/popoverMenu.js b/src/mixins/popoverMenu.js new file mode 100644 index 00000000..cbbca5d4 --- /dev/null +++ b/src/mixins/popoverMenu.js @@ -0,0 +1,42 @@ +/* + * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> + * + * @author Julius Härtl <jus@bitgrid.net> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +import { PopoverMenu } from 'nextcloud-vue' + +export default { + components: { + PopoverMenu + }, + data() { + return { + menuOpened: false + } + }, + methods: { + togglePopoverMenu() { + this.menuOpened = !this.menuOpened + }, + hidePopoverMenu() { + this.menuOpened = false + } + } +} |