From 490fb7fdeb3f666756504f10fd073a9776b700d5 Mon Sep 17 00:00:00 2001 From: ogerly Date: Sat, 21 Dec 2019 22:11:38 +0100 Subject: [PATCH 001/106] first example dynamisch usermention for answer comments --- webapp/components/Comment/Comment.vue | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/webapp/components/Comment/Comment.vue b/webapp/components/Comment/Comment.vue index cc7f815b9..3da39f435 100644 --- a/webapp/components/Comment/Comment.vue +++ b/webapp/components/Comment/Comment.vue @@ -55,6 +55,13 @@ + +
@@ -168,6 +175,22 @@ export default { this.$toast.error(err.message) } }, + answerComment() { + if (document.querySelector('.is-empty')) { + document.querySelector('.is-empty').innerHTML = '@' + this.comment.author.slug + } else { + const html = document.querySelector('.editor-content').innerHTML + const slug = + '@d' + + this.comment.author.slug + + '' + document.querySelector('.editor-content').innerHTML = html + '' + slug + } + }, }, } From 28839ee2b4f8c5d77965d24b4e24d312fc43eb91 Mon Sep 17 00:00:00 2001 From: ogerly Date: Mon, 6 Jan 2020 18:02:46 +0100 Subject: [PATCH 002/106] click on 'answered' to put the username of the respective comment into the editor as a mention. --- webapp/components/Comment/Comment.vue | 26 ++++++++++++++------------ webapp/locales/de.json | 3 ++- webapp/locales/en.json | 3 ++- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/webapp/components/Comment/Comment.vue b/webapp/components/Comment/Comment.vue index 3da39f435..46163bed0 100644 --- a/webapp/components/Comment/Comment.vue +++ b/webapp/components/Comment/Comment.vue @@ -56,7 +56,7 @@ @d' + + this.comment.author.slug + + '' + document.querySelector('.editor-content div').focus() if (document.querySelector('.is-empty')) { - document.querySelector('.is-empty').innerHTML = '@' + this.comment.author.slug + document.querySelector('.is-empty').innerHTML = slug + ' ' } else { - const html = document.querySelector('.editor-content').innerHTML - const slug = - '@d' + - this.comment.author.slug + - '' - document.querySelector('.editor-content').innerHTML = html + '' + slug + const html = document.querySelector('.editor-content').innerHTML + document.querySelector('.editor-content div').innerHTML = html + ' ' + slug + ' ' } }, }, diff --git a/webapp/locales/de.json b/webapp/locales/de.json index 880d22c87..060892d03 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -269,7 +269,8 @@ "comment": { "submit": "Kommentiere", "submitted": "Kommentar Gesendet", - "updated": "Änderungen gespeichert" + "updated": "Änderungen gespeichert", + "answered": "answered" }, "edited": "bearbeitet" }, diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 94183d7e7..69215d484 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -423,7 +423,8 @@ "comment": { "submit": "Comment", "submitted": "Comment Submitted", - "updated": "Changes Saved" + "updated": "Changes Saved", + "answered": "answered" }, "edited": "edited" }, From 9aa680ce509f62f57774981815094a1c273d8fa6 Mon Sep 17 00:00:00 2001 From: ogerly Date: Tue, 7 Jan 2020 05:40:09 +0100 Subject: [PATCH 003/106] Fix lint --- webapp/components/Comment/Comment.vue | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/components/Comment/Comment.vue b/webapp/components/Comment/Comment.vue index 46163bed0..ed7b9b064 100644 --- a/webapp/components/Comment/Comment.vue +++ b/webapp/components/Comment/Comment.vue @@ -113,8 +113,8 @@ export default { if (this.isLongComment && this.isCollapsed) { return this.$filters.truncate(this.comment.content, COMMENT_TRUNCATE_TO_LENGTH) } - - return this.comment.content + // console.log(this.comment.content.replace(/\?/gi, '?++')) + return this.comment.content.replace(/\?/gi, ' ') }, displaysComment() { return !this.unavailable || this.isModerator @@ -187,10 +187,10 @@ export default { '' document.querySelector('.editor-content div').focus() if (document.querySelector('.is-empty')) { - document.querySelector('.is-empty').innerHTML = slug + ' ' + document.querySelector('.is-empty').innerHTML = slug + ' ' } else { - const html = document.querySelector('.editor-content').innerHTML - document.querySelector('.editor-content div').innerHTML = html + ' ' + slug + ' ' + const html = document.querySelector('.editor-content').innerHTML + document.querySelector('.editor-content div').innerHTML = html + ' ' + slug + ' ' } }, }, From 223d1ca3c01659ebbf2c8da090cd17dfb5fc9f41 Mon Sep 17 00:00:00 2001 From: ogerly Date: Tue, 7 Jan 2020 06:02:19 +0100 Subject: [PATCH 004/106] remove console --- webapp/components/Comment/Comment.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/components/Comment/Comment.vue b/webapp/components/Comment/Comment.vue index ed7b9b064..3063fc400 100644 --- a/webapp/components/Comment/Comment.vue +++ b/webapp/components/Comment/Comment.vue @@ -114,7 +114,7 @@ export default { return this.$filters.truncate(this.comment.content, COMMENT_TRUNCATE_TO_LENGTH) } // console.log(this.comment.content.replace(/\?/gi, '?++')) - return this.comment.content.replace(/\?/gi, ' ') + return this.comment.content }, displaysComment() { return !this.unavailable || this.isModerator From 5d5574b1b5f7587139e3f822b62f388483379ab2 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Thu, 9 Jan 2020 16:07:25 +0100 Subject: [PATCH 005/106] Blocked users cannot comment on my posts --- .../src/middleware/permissionsMiddleware.js | 1 + backend/src/schema/resolvers/posts.js | 2 +- backend/src/schema/resolvers/users.js | 23 +++++++++ backend/src/schema/types/type/User.gql | 7 +-- webapp/components/Comment/Comment.vue | 6 +-- webapp/graphql/User.js | 7 +++ webapp/locales/en.json | 4 +- webapp/pages/post/_id/_slug/index.vue | 48 +++++++++++++++---- 8 files changed, 80 insertions(+), 18 deletions(-) diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index 3b42ae7fe..12a557232 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -102,6 +102,7 @@ export default shield( blockedUsers: isAuthenticated, notifications: isAuthenticated, Donations: isAuthenticated, + blockedByPostAuthor: isAuthenticated, }, Mutation: { '*': deny, diff --git a/backend/src/schema/resolvers/posts.js b/backend/src/schema/resolvers/posts.js index 47223faea..619b1de25 100644 --- a/backend/src/schema/resolvers/posts.js +++ b/backend/src/schema/resolvers/posts.js @@ -42,7 +42,7 @@ const maintainPinnedPosts = params => { export default { Query: { Post: async (object, params, context, resolveInfo) => { - params = await filterForBlockedUsers(params, context) + // params = await filterForBlockedUsers(params, context) params = await maintainPinnedPosts(params) return neo4jgraphql(object, params, context, resolveInfo) }, diff --git a/backend/src/schema/resolvers/users.js b/backend/src/schema/resolvers/users.js index 0b3f13631..c2600ab24 100644 --- a/backend/src/schema/resolvers/users.js +++ b/backend/src/schema/resolvers/users.js @@ -48,6 +48,29 @@ export default { throw new UserInputError(e.message) } }, + blockedByPostAuthor: async (_parent, params, context, _resolveInfo) => { + const { postAuthorId } = params + const { user, driver } = context + const session = driver.session() + const readTxResultPromise = session.readTransaction(async transaction => { + const blockedByPostAuthorTransactionResponse = await transaction.run( + ` + MATCH (currentUser:User {id: $currentUserId})<-[relationship:BLOCKED]-(postAuthor:User {id: $postAuthorId}) + RETURN COUNT(relationship) >= 1 as blockedByPostAuthor + `, + { postAuthorId, currentUserId: user.id }, + ) + return blockedByPostAuthorTransactionResponse.records.map(record => + record.get('blockedByPostAuthor'), + ) + }) + try { + const [blockedByPostAuthor] = await readTxResultPromise + return blockedByPostAuthor + } finally { + session.close() + } + }, User: async (object, args, context, resolveInfo) => { const { email } = args if (email) { diff --git a/backend/src/schema/types/type/User.gql b/backend/src/schema/types/type/User.gql index 243f45322..17ee2a733 100644 --- a/backend/src/schema/types/type/User.gql +++ b/backend/src/schema/types/type/User.gql @@ -68,10 +68,11 @@ type User { RETURN COUNT(u) >= 1 """ ) + isBlocked: Boolean! @cypher( statement: """ - MATCH (this)<-[: BLOCKED]-(u: User { id: $cypherParams.currentUserId}) - RETURN COUNT(u) >= 1 + MATCH (this)<-[:BLOCKED]-(user:User {id: $cypherParams.currentUserId}) + RETURN COUNT(user) >= 1 """ ) @@ -159,7 +160,7 @@ type Query { orderBy: [_UserOrdering] filter: _UserFilter ): [User] - + blockedByPostAuthor(postAuthorId: ID!): Boolean! blockedUsers: [User] currentUser: User } diff --git a/webapp/components/Comment/Comment.vue b/webapp/components/Comment/Comment.vue index cc7f815b9..f776aa413 100644 --- a/webapp/components/Comment/Comment.vue +++ b/webapp/components/Comment/Comment.vue @@ -33,7 +33,7 @@
- '' }, diff --git a/webapp/graphql/User.js b/webapp/graphql/User.js index 4ed832ad3..fa4b47423 100644 --- a/webapp/graphql/User.js +++ b/webapp/graphql/User.js @@ -216,3 +216,10 @@ export const checkSlugAvailableQuery = gql` } } ` +export const blockedByPostAuthor = () => { + return gql` + query($postAuthorId: ID!) { + blockedByPostAuthor(postAuthorId: $postAuthorId) + } + ` +} diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 6d8401e21..7d970998d 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -317,7 +317,9 @@ "their-perspective": "Vice versa: The blocked person will also no longer see your posts in their news feed.", "search": "Posts of blocked people disappear from your search results.", "notifications": "Blocked users will no longer receive notifications if they are mentioned in your posts.", - "closing": "This should be sufficient for now so that blocked users can no longer bother you." + "closing": "This should be sufficient for now so that blocked users can no longer bother you.", + "commenting-disabled": "Commenting is not possible at this time on this post.", + "commenting-explanation": "This can happen for several reasons, please see " }, "columns": { "name": "Name", diff --git a/webapp/pages/post/_id/_slug/index.vue b/webapp/pages/post/_id/_slug/index.vue index a94bf7b2d..bdfa13cf2 100644 --- a/webapp/pages/post/_id/_slug/index.vue +++ b/webapp/pages/post/_id/_slug/index.vue @@ -89,7 +89,19 @@ @toggleNewCommentForm="toggleNewCommentForm" /> - + + + + {{ $t('settings.blocked-users.explanation.commenting-disabled') }} +
+ {{ $t('settings.blocked-users.explanation.commenting-explanation') }} + https://human-connection.org +
+
@@ -102,12 +114,13 @@ import HcHashtag from '~/components/Hashtag/Hashtag' import ContentMenu from '~/components/ContentMenu/ContentMenu' import HcUser from '~/components/User/User' import HcShoutButton from '~/components/ShoutButton.vue' -import HcCommentForm from '~/components/CommentForm/CommentForm' +import CommentForm from '~/components/CommentForm/CommentForm' import HcCommentList from '~/components/CommentList/CommentList' import { postMenuModalsData, deletePostMutation } from '~/components/utils/PostHelpers' import PostQuery from '~/graphql/PostQuery' import HcEmotions from '~/components/Emotions/Emotions' import PostMutations from '~/graphql/PostMutations' +import { blockedByPostAuthor } from '~/graphql/User' export default { name: 'PostSlug', @@ -121,7 +134,7 @@ export default { HcUser, HcShoutButton, ContentMenu, - HcCommentForm, + CommentForm, HcCommentList, HcEmotions, ContentViewer, @@ -138,15 +151,10 @@ export default { title: 'loading', showNewCommentForm: true, blurred: false, + blocked: null, + postAuthor: null, } }, - watch: { - Post(post) { - this.post = post[0] || {} - this.title = this.post.title - this.blurred = this.post.imageBlurred - }, - }, mounted() { setTimeout(() => { // NOTE: quick fix for jumping flexbox implementation @@ -215,6 +223,26 @@ export default { id: this.$route.params.id, } }, + update({ Post }) { + this.post = Post[0] || {} + this.title = this.post.title + this.blurred = this.post.imageBlurred + this.postAuthor = this.post.author + }, + fetchPolicy: 'cache-and-network', + }, + blockedByPostAuthor: { + query() { + return blockedByPostAuthor() + }, + variables() { + return { + postAuthorId: this.postAuthor ? this.postAuthor.id : this.$store.getters['auth/user'].id, + } + }, + update({ blockedByPostAuthor }) { + this.blocked = blockedByPostAuthor + }, fetchPolicy: 'cache-and-network', }, }, From 9d09dae2f44995a8216804afe15e4a51d4f8b5c8 Mon Sep 17 00:00:00 2001 From: ogerly Date: Tue, 14 Jan 2020 18:36:35 +0100 Subject: [PATCH 006/106] slug from editor comands set --- webapp/components/Comment/Comment.vue | 41 ++++++++----------- webapp/components/CommentForm/CommentForm.vue | 3 ++ webapp/components/CommentList/CommentList.vue | 4 ++ webapp/components/Editor/Editor.vue | 4 +- webapp/locales/de.json | 2 +- webapp/locales/en.json | 2 +- webapp/pages/post/_id/_slug/index.vue | 11 ++++- 7 files changed, 38 insertions(+), 29 deletions(-) diff --git a/webapp/components/Comment/Comment.vue b/webapp/components/Comment/Comment.vue index 3063fc400..aeedb9e52 100644 --- a/webapp/components/Comment/Comment.vue +++ b/webapp/components/Comment/Comment.vue @@ -54,13 +54,14 @@
+ -
@@ -86,7 +87,6 @@ export default { isTarget, isCollapsed: !isTarget, openEditCommentMenu: false, - answered: this.$t('post.comment.answered'), } }, components: { @@ -113,7 +113,6 @@ export default { if (this.isLongComment && this.isCollapsed) { return this.$filters.truncate(this.comment.content, COMMENT_TRUNCATE_TO_LENGTH) } - // console.log(this.comment.content.replace(/\?/gi, '?++')) return this.comment.content }, displaysComment() { @@ -149,6 +148,10 @@ export default { }, }, methods: { + reply(comment) { + const message = { slug: this.comment.author.slug, id: this.comment.author.id } + this.$emit('reply', message) + }, checkAnchor(anchor) { return `#${this.anchor}` === anchor }, @@ -176,23 +179,6 @@ export default { this.$toast.error(err.message) } }, - answerComment() { - const slug = - '@d' + - this.comment.author.slug + - '' - document.querySelector('.editor-content div').focus() - if (document.querySelector('.is-empty')) { - document.querySelector('.is-empty').innerHTML = slug + ' ' - } else { - const html = document.querySelector('.editor-content').innerHTML - document.querySelector('.editor-content div').innerHTML = html + ' ' + slug + ' ' - } - }, }, } @@ -218,6 +204,11 @@ export default { float: right; } +.answerbutton { + float: right; + top: 0px; +} + @keyframes highlight { 0% { border: 1px solid $color-primary; diff --git a/webapp/components/CommentForm/CommentForm.vue b/webapp/components/CommentForm/CommentForm.vue index 6cdd08af3..c2a0e4562 100644 --- a/webapp/components/CommentForm/CommentForm.vue +++ b/webapp/components/CommentForm/CommentForm.vue @@ -57,6 +57,9 @@ export default { } }, methods: { + reply(message) { + this.$refs.editor.insertReply(message) + }, updateEditorContent(value) { const sanitizedContent = this.$filters.removeHtml(value, false) if (!this.update) { diff --git a/webapp/components/CommentList/CommentList.vue b/webapp/components/CommentList/CommentList.vue index 25ed62f68..d21f2b407 100644 --- a/webapp/components/CommentList/CommentList.vue +++ b/webapp/components/CommentList/CommentList.vue @@ -8,6 +8,7 @@
{} }, }, methods: { + reply(message) { + this.$emit('reply', message) + }, checkAnchor(anchor) { return anchor === '#comments' }, diff --git a/webapp/components/Editor/Editor.vue b/webapp/components/Editor/Editor.vue index 6c8a1908a..f62d7abf7 100644 --- a/webapp/components/Editor/Editor.vue +++ b/webapp/components/Editor/Editor.vue @@ -141,7 +141,6 @@ export default { methods: { openSuggestionList({ items, query, range, command, virtualNode }, suggestionType) { this.suggestionType = suggestionType - this.query = this.sanitizeQuery(query) this.filteredItems = items this.suggestionRange = range @@ -237,6 +236,9 @@ export default { const content = e.getHTML() this.$emit('input', content) }, + insertReply(message) { + this.editor.commands.mention({ id: message.id, label: message.slug }) + }, toggleLinkInput(attrs, element) { if (!this.isLinkInputActive && attrs && element) { this.$refs.linkInput.linkUrl = attrs.href diff --git a/webapp/locales/de.json b/webapp/locales/de.json index 060892d03..a394424cb 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -270,7 +270,7 @@ "submit": "Kommentiere", "submitted": "Kommentar Gesendet", "updated": "Änderungen gespeichert", - "answered": "answered" + "answer": "Antworten" }, "edited": "bearbeitet" }, diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 69215d484..e6e3426a7 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -424,7 +424,7 @@ "submit": "Comment", "submitted": "Comment Submitted", "updated": "Changes Saved", - "answered": "answered" + "answer": "Answer" }, "edited": "edited" }, diff --git a/webapp/pages/post/_id/_slug/index.vue b/webapp/pages/post/_id/_slug/index.vue index 067650d15..06bbe554d 100644 --- a/webapp/pages/post/_id/_slug/index.vue +++ b/webapp/pages/post/_id/_slug/index.vue @@ -72,12 +72,18 @@ - + @@ -150,6 +156,9 @@ export default { }, }, methods: { + reply(message) { + this.$refs.commentForm && this.$refs.commentForm.reply(message) + }, isAuthor(id) { return this.$store.getters['auth/user'].id === id }, From 8edb551c159272f005da502d7a021105dd0904d2 Mon Sep 17 00:00:00 2001 From: ogerly Date: Wed, 15 Jan 2020 07:40:01 +0100 Subject: [PATCH 007/106] ready to be merged or rebased --- webapp/components/Comment/Comment.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/webapp/components/Comment/Comment.vue b/webapp/components/Comment/Comment.vue index aeedb9e52..b31414449 100644 --- a/webapp/components/Comment/Comment.vue +++ b/webapp/components/Comment/Comment.vue @@ -60,9 +60,9 @@ @click.prevent="reply" v-scroll-to="'.editor'" class="answerbutton" + size="small" > -
@@ -208,6 +208,9 @@ export default { float: right; top: 0px; } +.answerbutton:after { + clear: both; +} @keyframes highlight { 0% { From 6877c9da91c65037b06c653af2ea85effd827ebf Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Thu, 16 Jan 2020 12:29:28 +0100 Subject: [PATCH 008/106] Use new base-button, rename CSS class --- webapp/assets/_new/icons/svgs/level-down.svg | 5 +++++ webapp/components/Comment/Comment.vue | 13 ++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) create mode 100755 webapp/assets/_new/icons/svgs/level-down.svg diff --git a/webapp/assets/_new/icons/svgs/level-down.svg b/webapp/assets/_new/icons/svgs/level-down.svg new file mode 100755 index 000000000..e6455391e --- /dev/null +++ b/webapp/assets/_new/icons/svgs/level-down.svg @@ -0,0 +1,5 @@ + + +level-down + + diff --git a/webapp/components/Comment/Comment.vue b/webapp/components/Comment/Comment.vue index b31414449..b682c712a 100644 --- a/webapp/components/Comment/Comment.vue +++ b/webapp/components/Comment/Comment.vue @@ -54,14 +54,15 @@ - + > @@ -75,6 +76,7 @@ import ContentViewer from '~/components/Editor/ContentViewer' import HcCommentForm from '~/components/CommentForm/CommentForm' import CommentMutations from '~/graphql/CommentMutations' import scrollToAnchor from '~/mixins/scrollToAnchor.js' +import BaseButton from '~/components/_new/generic/BaseButton/BaseButton' export default { mixins: [scrollToAnchor], @@ -94,6 +96,7 @@ export default { ContentMenu, ContentViewer, HcCommentForm, + BaseButton, }, props: { routeHash: { type: String, default: () => '' }, @@ -204,11 +207,11 @@ export default { float: right; } -.answerbutton { +.reply-button { float: right; top: 0px; } -.answerbutton:after { +.reply-button:after { clear: both; } From 73b005a900d43ccc639a3250ac77ae0ff4a54718 Mon Sep 17 00:00:00 2001 From: ogerly Date: Mon, 20 Jan 2020 11:18:35 +0100 Subject: [PATCH 009/106] test: added some test --- webapp/components/Comment/Comment.spec.js | 25 ++++++++++++++++--- webapp/components/Comment/Comment.vue | 11 ++++---- .../CommentList/CommentList.spec.js | 18 +++++++++++++ webapp/components/CommentList/CommentList.vue | 1 + webapp/locales/de.json | 2 +- webapp/locales/en.json | 2 +- webapp/pages/post/_id/_slug/index.spec.js | 19 ++++++++++++-- 7 files changed, 66 insertions(+), 12 deletions(-) diff --git a/webapp/components/Comment/Comment.spec.js b/webapp/components/Comment/Comment.spec.js index b307700d9..ea04d7c5b 100644 --- a/webapp/components/Comment/Comment.spec.js +++ b/webapp/components/Comment/Comment.spec.js @@ -1,4 +1,4 @@ -import { config, shallowMount } from '@vue/test-utils' +import { config, mount } from '@vue/test-utils' import Comment from './Comment.vue' import Vuex from 'vuex' @@ -47,14 +47,14 @@ describe('Comment.vue', () => { } }) - describe('shallowMount', () => { + describe('mount', () => { beforeEach(jest.useFakeTimers) Wrapper = () => { const store = new Vuex.Store({ getters, }) - return shallowMount(Comment, { + return mount(Comment, { store, propsData, mocks, @@ -68,6 +68,7 @@ describe('Comment.vue', () => { id: '2', contentExcerpt: 'Hello I am a comment content', content: 'Hello I am comment content', + author: { id: 'commentAuthorId', slug: 'ogerly'} } }) @@ -199,6 +200,24 @@ describe('Comment.vue', () => { }) }) }) + + describe('click reply button', () => { + + beforeEach(async () => { + wrapper = Wrapper() + await wrapper.find('.reply-button').trigger('click') + }) + it('emits "reply"', () => { + expect(wrapper.emitted('reply')).toEqual([ + [ + { + id: 'commentAuthorId', + slug: 'ogerly' + }, + ], + ]) + }) + }) }) }) }) diff --git a/webapp/components/Comment/Comment.vue b/webapp/components/Comment/Comment.vue index b31414449..da205aaee 100644 --- a/webapp/components/Comment/Comment.vue +++ b/webapp/components/Comment/Comment.vue @@ -55,11 +55,11 @@ @@ -76,6 +76,7 @@ import HcCommentForm from '~/components/CommentForm/CommentForm' import CommentMutations from '~/graphql/CommentMutations' import scrollToAnchor from '~/mixins/scrollToAnchor.js' + export default { mixins: [scrollToAnchor], data() { @@ -148,7 +149,7 @@ export default { }, }, methods: { - reply(comment) { + reply() { const message = { slug: this.comment.author.slug, id: this.comment.author.id } this.$emit('reply', message) }, @@ -204,11 +205,11 @@ export default { float: right; } -.answerbutton { +.reply-button { float: right; top: 0px; } -.answerbutton:after { +.reply-button:after { clear: both; } diff --git a/webapp/components/CommentList/CommentList.spec.js b/webapp/components/CommentList/CommentList.spec.js index 0c037d2ff..0b4a667a7 100644 --- a/webapp/components/CommentList/CommentList.spec.js +++ b/webapp/components/CommentList/CommentList.spec.js @@ -103,4 +103,22 @@ describe('CommentList.vue', () => { }) }) }) + + describe('Comment', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + it('Comment emitted reply()', () => { + wrapper.find('.comment-tag').vm.$emit('reply') + expect(wrapper.emitted('reply')).toEqual([ + [ + { + id: 'commentAuthorId', + slug: 'ogerly' + }, + ], + ]) + }) + }) }) diff --git a/webapp/components/CommentList/CommentList.vue b/webapp/components/CommentList/CommentList.vue index d21f2b407..061210176 100644 --- a/webapp/components/CommentList/CommentList.vue +++ b/webapp/components/CommentList/CommentList.vue @@ -17,6 +17,7 @@ @deleteComment="updateCommentList" @updateComment="updateCommentList" @toggleNewCommentForm="toggleNewCommentForm" + class="comment-tag" /> diff --git a/webapp/locales/de.json b/webapp/locales/de.json index a394424cb..de9797596 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -270,7 +270,7 @@ "submit": "Kommentiere", "submitted": "Kommentar Gesendet", "updated": "Änderungen gespeichert", - "answer": "Antworten" + "reply": "Antworten" }, "edited": "bearbeitet" }, diff --git a/webapp/locales/en.json b/webapp/locales/en.json index e6e3426a7..9226793c1 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -424,7 +424,7 @@ "submit": "Comment", "submitted": "Comment Submitted", "updated": "Changes Saved", - "answer": "Answer" + "reply": "Reply" }, "edited": "edited" }, diff --git a/webapp/pages/post/_id/_slug/index.spec.js b/webapp/pages/post/_id/_slug/index.spec.js index db960bb67..d7c234c4c 100644 --- a/webapp/pages/post/_id/_slug/index.spec.js +++ b/webapp/pages/post/_id/_slug/index.spec.js @@ -1,16 +1,16 @@ import { config, shallowMount } from '@vue/test-utils' import PostSlug from './index.vue' import Vuex from 'vuex' +import CommentList from '~/components/CommentList/CommentList' const localVue = global.localVue -config.stubs['client-only'] = '' - describe('PostSlug', () => { let wrapper let Wrapper let store let mocks + let propsData beforeEach(() => { store = new Vuex.Store({ @@ -20,6 +20,7 @@ describe('PostSlug', () => { }, }, }) + propsData = {} mocks = { $t: jest.fn(), $filters: { @@ -44,12 +45,14 @@ describe('PostSlug', () => { } }) + describe('shallowMount', () => { Wrapper = () => { return shallowMount(PostSlug, { store, mocks, localVue, + propsData, }) } @@ -92,4 +95,16 @@ describe('PostSlug', () => { }) }) }) + + describe('given a comment', () => { + wrapper = Wrapper() + const bar = wrapper.find(CommentList) + it('hc-comment-list', () => { + expect(bar).toBe({"selector": "Component"}) + }) + + + + }) }) + From c7ee90e9800903402ea5db261112f7655b932ab0 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Tue, 21 Jan 2020 18:50:46 +0100 Subject: [PATCH 010/106] Follow @roschaefer PR suggestions - As blocking is now reciprocal, we do not need another query, we can use neo4j-graphql-js magic to query for a BLOCKED relationship between the postAuthor and the currentUser --- .../src/middleware/permissionsMiddleware.js | 1 - backend/src/schema/resolvers/users.js | 23 ------------------- backend/src/schema/types/type/User.gql | 3 +-- webapp/graphql/PostQuery.js | 1 + webapp/graphql/User.js | 7 ------ webapp/pages/post/_id/_slug/index.vue | 17 +------------- 6 files changed, 3 insertions(+), 49 deletions(-) diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index 12a557232..3b42ae7fe 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -102,7 +102,6 @@ export default shield( blockedUsers: isAuthenticated, notifications: isAuthenticated, Donations: isAuthenticated, - blockedByPostAuthor: isAuthenticated, }, Mutation: { '*': deny, diff --git a/backend/src/schema/resolvers/users.js b/backend/src/schema/resolvers/users.js index c2600ab24..0b3f13631 100644 --- a/backend/src/schema/resolvers/users.js +++ b/backend/src/schema/resolvers/users.js @@ -48,29 +48,6 @@ export default { throw new UserInputError(e.message) } }, - blockedByPostAuthor: async (_parent, params, context, _resolveInfo) => { - const { postAuthorId } = params - const { user, driver } = context - const session = driver.session() - const readTxResultPromise = session.readTransaction(async transaction => { - const blockedByPostAuthorTransactionResponse = await transaction.run( - ` - MATCH (currentUser:User {id: $currentUserId})<-[relationship:BLOCKED]-(postAuthor:User {id: $postAuthorId}) - RETURN COUNT(relationship) >= 1 as blockedByPostAuthor - `, - { postAuthorId, currentUserId: user.id }, - ) - return blockedByPostAuthorTransactionResponse.records.map(record => - record.get('blockedByPostAuthor'), - ) - }) - try { - const [blockedByPostAuthor] = await readTxResultPromise - return blockedByPostAuthor - } finally { - session.close() - } - }, User: async (object, args, context, resolveInfo) => { const { email } = args if (email) { diff --git a/backend/src/schema/types/type/User.gql b/backend/src/schema/types/type/User.gql index 17ee2a733..715a1f3e1 100644 --- a/backend/src/schema/types/type/User.gql +++ b/backend/src/schema/types/type/User.gql @@ -71,7 +71,7 @@ type User { isBlocked: Boolean! @cypher( statement: """ - MATCH (this)<-[:BLOCKED]-(user:User {id: $cypherParams.currentUserId}) + MATCH (this)-[:BLOCKED]-(user:User {id: $cypherParams.currentUserId}) RETURN COUNT(user) >= 1 """ ) @@ -160,7 +160,6 @@ type Query { orderBy: [_UserOrdering] filter: _UserFilter ): [User] - blockedByPostAuthor(postAuthorId: ID!): Boolean! blockedUsers: [User] currentUser: User } diff --git a/webapp/graphql/PostQuery.js b/webapp/graphql/PostQuery.js index c59c894a5..a3ac8345a 100644 --- a/webapp/graphql/PostQuery.js +++ b/webapp/graphql/PostQuery.js @@ -29,6 +29,7 @@ export default i18n => { ...user ...userCounts ...locationAndBadges + isBlocked } comments(orderBy: createdAt_asc) { ...comment diff --git a/webapp/graphql/User.js b/webapp/graphql/User.js index fa4b47423..4ed832ad3 100644 --- a/webapp/graphql/User.js +++ b/webapp/graphql/User.js @@ -216,10 +216,3 @@ export const checkSlugAvailableQuery = gql` } } ` -export const blockedByPostAuthor = () => { - return gql` - query($postAuthorId: ID!) { - blockedByPostAuthor(postAuthorId: $postAuthorId) - } - ` -} diff --git a/webapp/pages/post/_id/_slug/index.vue b/webapp/pages/post/_id/_slug/index.vue index bdfa13cf2..41e9f4503 100644 --- a/webapp/pages/post/_id/_slug/index.vue +++ b/webapp/pages/post/_id/_slug/index.vue @@ -90,7 +90,7 @@ /> @@ -120,7 +120,6 @@ import { postMenuModalsData, deletePostMutation } from '~/components/utils/PostH import PostQuery from '~/graphql/PostQuery' import HcEmotions from '~/components/Emotions/Emotions' import PostMutations from '~/graphql/PostMutations' -import { blockedByPostAuthor } from '~/graphql/User' export default { name: 'PostSlug', @@ -231,20 +230,6 @@ export default { }, fetchPolicy: 'cache-and-network', }, - blockedByPostAuthor: { - query() { - return blockedByPostAuthor() - }, - variables() { - return { - postAuthorId: this.postAuthor ? this.postAuthor.id : this.$store.getters['auth/user'].id, - } - }, - update({ blockedByPostAuthor }) { - this.blocked = blockedByPostAuthor - }, - fetchPolicy: 'cache-and-network', - }, }, } From bcae52180e3ff5ae7ecf9de31800aa691e47a648 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Tue, 21 Jan 2020 19:28:03 +0100 Subject: [PATCH 011/106] Remove filter on posts for BLOCKED --- backend/src/schema/resolvers/posts.js | 29 +-------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/backend/src/schema/resolvers/posts.js b/backend/src/schema/resolvers/posts.js index 619b1de25..72af86b4f 100644 --- a/backend/src/schema/resolvers/posts.js +++ b/backend/src/schema/resolvers/posts.js @@ -1,34 +1,10 @@ import uuid from 'uuid/v4' import { neo4jgraphql } from 'neo4j-graphql-js' import fileUpload from './fileUpload' -import { getBlockedUsers, getBlockedByUsers } from './users.js' -import { mergeWith, isArray, isEmpty } from 'lodash' +import { isEmpty } from 'lodash' import { UserInputError } from 'apollo-server' import Resolver from './helpers/Resolver' -const filterForBlockedUsers = async (params, context) => { - if (!context.user) return params - const [blockedUsers, blockedByUsers] = await Promise.all([ - getBlockedUsers(context), - getBlockedByUsers(context), - ]) - const badIds = [...blockedByUsers.map(b => b.id), ...blockedUsers.map(b => b.id)] - if (!badIds.length) return params - - params.filter = mergeWith( - params.filter, - { - author_not: { id_in: badIds }, - }, - (objValue, srcValue) => { - if (isArray(objValue)) { - return objValue.concat(srcValue) - } - }, - ) - return params -} - const maintainPinnedPosts = params => { const pinnedPostFilter = { pinned: true } if (isEmpty(params.filter)) { @@ -42,16 +18,13 @@ const maintainPinnedPosts = params => { export default { Query: { Post: async (object, params, context, resolveInfo) => { - // params = await filterForBlockedUsers(params, context) params = await maintainPinnedPosts(params) return neo4jgraphql(object, params, context, resolveInfo) }, findPosts: async (object, params, context, resolveInfo) => { - params = await filterForBlockedUsers(params, context) return neo4jgraphql(object, params, context, resolveInfo) }, profilePagePosts: async (object, params, context, resolveInfo) => { - params = await filterForBlockedUsers(params, context) return neo4jgraphql(object, params, context, resolveInfo) }, PostsEmotionsCountByEmotion: async (object, params, context, resolveInfo) => { From 2df4e463f89b988eb5e37f55ab1eedd688423909 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Tue, 21 Jan 2020 19:28:19 +0100 Subject: [PATCH 012/106] Update/Add translations for placeholder message - @alina-beck, I had a look at the Placeholder component and it's quite simple, but I guess migrating that is a separate PR, but I don't know aobut adding more ds-placeholders, do you have some design ideas on what could look better? --- webapp/locales/de.json | 13 +++++++------ webapp/locales/en.json | 9 ++++----- webapp/pages/post/_id/_slug/index.vue | 14 ++++++-------- webapp/pages/settings/blocked-users.vue | 3 --- 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/webapp/locales/de.json b/webapp/locales/de.json index 1417d3a51..03fcc0f74 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -149,11 +149,12 @@ "name": "Blockierte Benutzer", "explanation": { "intro": "Wenn ein anderer Benutzer von dir blockiert wurde, dann passiert folgendes:", - "your-perspective": "In deiner Beitragsübersicht tauchen keine Beiträge der blockierten Person mehr auf.", - "their-perspective": "Umgekehrt das gleiche: Die blockierte Person sieht deine Beiträge auch nicht mehr in ihrer Übersicht.", - "search": "Die Beiträge von blockierten Personen verschwinden aus deinen Suchergebnissen.", - "notifications": "Von dir blockierte Personen erhalten keine Benachrichtigungen mehr, wenn sie in deinen Beiträgen erwähnt werden.", - "closing": "Das sollte fürs Erste genügen, damit blockierte Benutzer dich nicht mehr länger belästigen können." + "your-perspective": "Sie werden nicht mehr in der Lage sein, mit ihren Beiträgen zu interagieren.", + "their-perspective": "Umgekehrt das gleiche: Die blockierte Person kann auch nicht mehr mit Ihren Beiträgen interagieren.", + "notifications": "Gesperrte Benutzer erhalten keine Benachrichtigungen mehr, wenn sie sich gegenseitig erwähnen.", + "closing": "Das sollte fürs Erste genügen, damit blockierte Benutzer dich nicht mehr länger belästigen können.", + "commenting-disabled": "Ein Kommentar zu diesem Beitrag ist zur Zeit nicht möglich.", + "commenting-explanation": "Dies kann aus verschiedenen Gründen geschehen, siehe " }, "columns": { "name": "Name", @@ -485,7 +486,7 @@ }, "teaserImage": { "cropperConfirm": "Bestätigen" - }, + }, "inappropriatePicture" : "Dieses Bild kann für einige Menschen unangemessen sein.", "inappropriatePictureText" : "Wann soll ein Foto versteckt werden" }, diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 7d970998d..330497a3c 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -313,13 +313,12 @@ "name": "Blocked users", "explanation": { "intro": "If another user has been blocked by you, this is what happens:", - "your-perspective": "The blocked person's posts will no longer appear in your news feed.", - "their-perspective": "Vice versa: The blocked person will also no longer see your posts in their news feed.", - "search": "Posts of blocked people disappear from your search results.", - "notifications": "Blocked users will no longer receive notifications if they are mentioned in your posts.", + "your-perspective": "You will no longer be able to interact with their contributions.", + "their-perspective": "Vice versa: The blocked person will also no longer be able to interact with your contributions.", + "notifications": "Blocked users will no longer receive notifications if they mention each other.", "closing": "This should be sufficient for now so that blocked users can no longer bother you.", "commenting-disabled": "Commenting is not possible at this time on this post.", - "commenting-explanation": "This can happen for several reasons, please see " + "commenting-explanation": "This can happen for several reasons, please see our " }, "columns": { "name": "Name", diff --git a/webapp/pages/post/_id/_slug/index.vue b/webapp/pages/post/_id/_slug/index.vue index 41e9f4503..f0757e5a3 100644 --- a/webapp/pages/post/_id/_slug/index.vue +++ b/webapp/pages/post/_id/_slug/index.vue @@ -94,14 +94,12 @@ :post="post" @createComment="createComment" /> - - - {{ $t('settings.blocked-users.explanation.commenting-disabled') }} -
- {{ $t('settings.blocked-users.explanation.commenting-explanation') }} - https://human-connection.org -
-
+ + {{ $t('settings.blocked-users.explanation.commenting-disabled') }} +
+ {{ $t('settings.blocked-users.explanation.commenting-explanation') }} + FAQ +
diff --git a/webapp/pages/settings/blocked-users.vue b/webapp/pages/settings/blocked-users.vue index acbd253aa..db7dd9f93 100644 --- a/webapp/pages/settings/blocked-users.vue +++ b/webapp/pages/settings/blocked-users.vue @@ -12,9 +12,6 @@ {{ $t('settings.blocked-users.explanation.their-perspective') }} - - {{ $t('settings.blocked-users.explanation.search') }} - {{ $t('settings.blocked-users.explanation.notifications') }} From e639358557461dfd8c32be4a6d03cb0e879a5014 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Tue, 21 Jan 2020 21:16:42 +0100 Subject: [PATCH 013/106] Test drive to get the functionality how we'd like --- backend/src/schema/resolvers/searches.js | 2 +- backend/src/schema/resolvers/users.js | 22 +++++++++ cypress/integration/common/steps.js | 32 ++++++++++++- .../user_profile/BlockUser.feature | 48 +++++++++++++++++++ .../user_profile/mute-users/Mute.feature | 5 +- webapp/components/ContentMenu/ContentMenu.vue | 17 +++++++ webapp/locales/en.json | 4 +- webapp/pages/profile/_id/_slug.vue | 23 ++++++++- 8 files changed, 145 insertions(+), 8 deletions(-) create mode 100644 cypress/integration/user_profile/BlockUser.feature diff --git a/backend/src/schema/resolvers/searches.js b/backend/src/schema/resolvers/searches.js index 5316ccd9a..bfbdd21b1 100644 --- a/backend/src/schema/resolvers/searches.js +++ b/backend/src/schema/resolvers/searches.js @@ -20,7 +20,7 @@ export default { AND NOT ( author.deleted = true OR author.disabled = true OR resource.deleted = true OR resource.disabled = true - OR (:User { id: $thisUserId })-[:BLOCKED]-(author) + OR (:User { id: $thisUserId })-[:MUTED]-(author) ) WITH resource, author, [(resource)<-[:COMMENTS]-(comment:Comment) | comment] as comments, diff --git a/backend/src/schema/resolvers/users.js b/backend/src/schema/resolvers/users.js index dc962dc00..915d8b6f8 100644 --- a/backend/src/schema/resolvers/users.js +++ b/backend/src/schema/resolvers/users.js @@ -23,6 +23,21 @@ export const getMutedUsers = async context => { return mutedUsers } +export const getBlockedUsers = async context => { + const { neode } = context + const userModel = neode.model('User') + let blockedUsers = neode + .query() + .match('user', userModel) + .where('user.id', context.user.id) + .relationship(userModel.relationships().get('blocked')) + .to('blocked', userModel) + .return('blocked') + blockedUsers = await blockedUsers.execute() + blockedUsers = blockedUsers.records.map(r => r.get('blocked').properties) + return blockedUsers +} + export default { Query: { mutedUsers: async (object, args, context, resolveInfo) => { @@ -32,6 +47,13 @@ export default { throw new UserInputError(e.message) } }, + blockedUsers: async (object, args, context, resolveInfo) => { + try { + return getBlockedUsers(context) + } catch (e) { + throw new UserInputError(e.message) + } + }, User: async (object, args, context, resolveInfo) => { const { email } = args if (email) { diff --git a/cypress/integration/common/steps.js b/cypress/integration/common/steps.js index 53b5f7c3d..29f2fa85a 100644 --- a/cypress/integration/common/steps.js +++ b/cypress/integration/common/steps.js @@ -39,7 +39,7 @@ Given("I am logged in", () => { cy.login(loginCredentials); }); -Given("I am logged in as the muted user", () => { +Given("I am logged in as the {string} user", _ => { cy.login({ email: annoyingParams.email, password: '1234' }); }); @@ -123,6 +123,12 @@ When("I visit the {string} page", page => { cy.openPage(page); }); +When("the blocked user visits my post", () => { + cy.logout() + .login({ email: annoyingParams.email, password: annoyingParams.password }) + .openPage('/post/previously-created-post') +}) + Given("I am on the {string} page", page => { cy.openPage(page); }); @@ -485,7 +491,7 @@ Given("I follow the user {string}", name => { }); }); -Given('"Spammy Spammer" wrote a post {string}', title => { +Given('{string} wrote a post {string}', (_, title) => { cy.createCategories("cat21") .factory() .create("Post", { @@ -532,6 +538,20 @@ When("I mute the user {string}", name => { }); }); +When("I block the user {string}", name => { + cy.neode() + .first("User", { + name + }) + .then(blockedUser => { + cy.neode() + .first("User", { + name: narratorParams.name + }) + .relateTo(blockedUser, "blocked"); + }); +}); + When("I log in with:", table => { const [firstRow] = table.hashes(); const { @@ -550,3 +570,11 @@ Then("I see only one post with the title {string}", title => { .should("have.length", 1); cy.get(".main-container").contains(".post-link", title); }); + +Then("they should not see the comment from", () => { + cy.get(".ds-card-footer").children().should('not.have.class', 'comment-form') +}) + +Then("they should see a text explaining commenting is not possible", () => { + cy.get('.ds-placeholder').should('contain', "Commenting is not possible at this time on this post.") +}) \ No newline at end of file diff --git a/cypress/integration/user_profile/BlockUser.feature b/cypress/integration/user_profile/BlockUser.feature new file mode 100644 index 000000000..a04455b53 --- /dev/null +++ b/cypress/integration/user_profile/BlockUser.feature @@ -0,0 +1,48 @@ +Feature: Block a User + As a user + I'd like to have a button to block another user + To prevent him from seeing and interacting with my contributions + + Background: + Given I have a user account + And there is an annoying user called "Harassing User" + And I am logged in + + Scenario: Block a user + Given I am on the profile page of the annoying user + When I click on "Block user" from the content menu in the user info box + And I navigate to my "Blocked users" settings page + Then I can see the following table: + | Avatar | Name | + | | Harassing User | + + Scenario: Blocked user cannot interact with my contributions + Given I block the user "Harassing User" + And I previously created a post + And the blocked user visits my post + Then they should not see the comment from + And they should see a text explaining commenting is not possible + + Scenario: Block a previously followed user + Given I follow the user "Harassing User" + When I visit the profile page of the annoying user + And I click on "Block user" from the content menu in the user info box + And nobody is following the user profile anymore + + Scenario: Posts of blocked users are not filtered from search results + Given "Harassing User" wrote a post "You can still see my posts" + And I block the user "Harassing User" + When I search for "see" + Then I should see the following posts in the select dropdown: + | title | + | You can still see my posts | + + Scenario: Blocked users can still see my posts + Given I previously created a post + And I block the user "Harassing User" + Given I log out + And I am logged in as the "blocked" user + When I search for "previously created" + Then I should see the following posts in the select dropdown: + | title | + | previously created post | diff --git a/cypress/integration/user_profile/mute-users/Mute.feature b/cypress/integration/user_profile/mute-users/Mute.feature index b52faeeaa..cd7d1c827 100644 --- a/cypress/integration/user_profile/mute-users/Mute.feature +++ b/cypress/integration/user_profile/mute-users/Mute.feature @@ -1,8 +1,7 @@ Feature: Mute a User As a user I'd like to have a button to mute another user - To prevent him from seeing and interacting with my contributions and also to avoid seeing his/her posts - + To prevent him from seeing and interacting with my contributions Background: Given I have a user account And there is an annoying user called "Spammy Spammer" @@ -46,7 +45,7 @@ Feature: Mute a User Given I previously created a post And I mute the user "Spammy Spammer" Given I log out - And I am logged in as the muted user + And I am logged in as the "muted" user When I search for "previously created" Then I should see the following posts in the select dropdown: | title | diff --git a/webapp/components/ContentMenu/ContentMenu.vue b/webapp/components/ContentMenu/ContentMenu.vue index a22bc3267..31d90a614 100644 --- a/webapp/components/ContentMenu/ContentMenu.vue +++ b/webapp/components/ContentMenu/ContentMenu.vue @@ -172,6 +172,23 @@ export default { icon: 'user-times', }) } + if (this.resource.isBlocked) { + routes.push({ + label: this.$t(`settings.blocked-users.unblock`), + callback: () => { + this.$emit('unblock', this.resource) + }, + icon: 'user-plus', + }) + } else { + routes.push({ + label: this.$t(`settings.blocked-users.block`), + callback: () => { + this.$emit('block', this.resource) + }, + icon: 'user-times', + }) + } } } diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 2191a91e7..e36214d8a 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -337,7 +337,9 @@ "your-perspective": "You will no longer be able to interact with their contributions.", "their-perspective": "Vice versa: The blocked person will also no longer be able to interact with your contributions.", "notifications": "Blocked users will no longer receive notifications if they mention each other.", - "closing": "This should be sufficient for now so that blocked users can no longer bother you." + "closing": "This should be sufficient for now so that blocked users can no longer bother you.", + "commenting-disabled": "Commenting is not possible at this time on this post.", + "commenting-explanation": "This can happen for several reasons, please see our " }, "columns": { "name": "Name", diff --git a/webapp/pages/profile/_id/_slug.vue b/webapp/pages/profile/_id/_slug.vue index ce220b66b..01190caa2 100644 --- a/webapp/pages/profile/_id/_slug.vue +++ b/webapp/pages/profile/_id/_slug.vue @@ -24,6 +24,8 @@ class="user-content-menu" @mute="muteUser" @unmute="unmuteUser" + @block="blockUser" + @unblock="unblockUser" /> @@ -67,7 +69,7 @@ @@ -127,6 +127,10 @@ export default { display: flex; align-items: center; padding-left: $space-xx-small; + + > .user-avatar { + margin-right: $space-xx-small; + } } .avatar-menu-popover { padding-top: $space-x-small; diff --git a/webapp/components/UserTeaser/UserTeaser.vue b/webapp/components/UserTeaser/UserTeaser.vue index 9d1b22fd9..2057a8fcd 100644 --- a/webapp/components/UserTeaser/UserTeaser.vue +++ b/webapp/components/UserTeaser/UserTeaser.vue @@ -1,6 +1,6 @@