mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge pull request #475 from Human-Connection/260-add-comment-form
Add Comment Form
This commit is contained in:
commit
890810743a
@ -11,6 +11,7 @@ import shout from './resolvers/shout.js'
|
||||
import rewards from './resolvers/rewards.js'
|
||||
import socialMedia from './resolvers/socialMedia.js'
|
||||
import notifications from './resolvers/notifications'
|
||||
import comments from './resolvers/comments'
|
||||
|
||||
export const typeDefs = fs
|
||||
.readFileSync(
|
||||
@ -22,7 +23,8 @@ export const resolvers = {
|
||||
Query: {
|
||||
...statistics.Query,
|
||||
...userManagement.Query,
|
||||
...notifications.Query
|
||||
...notifications.Query,
|
||||
...comments.Query
|
||||
},
|
||||
Mutation: {
|
||||
...userManagement.Mutation,
|
||||
@ -33,6 +35,7 @@ export const resolvers = {
|
||||
...shout.Mutation,
|
||||
...rewards.Mutation,
|
||||
...socialMedia.Mutation,
|
||||
...notifications.Mutation
|
||||
...notifications.Mutation,
|
||||
...comments.Mutation
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,7 +86,8 @@ const permissions = shield({
|
||||
unshout: isAuthenticated,
|
||||
changePassword: isAuthenticated,
|
||||
enable: isModerator,
|
||||
disable: isModerator
|
||||
disable: isModerator,
|
||||
CreateComment: isAuthenticated
|
||||
// CreateUser: allow,
|
||||
},
|
||||
User: {
|
||||
|
||||
@ -23,21 +23,19 @@ beforeAll(async () => {
|
||||
])
|
||||
|
||||
await Promise.all([
|
||||
factory.create('Comment', { id: 'c2', content: 'Enabled comment on public post' })
|
||||
factory.create('Comment', { id: 'c2', postId: 'p3', content: 'Enabled comment on public post' })
|
||||
])
|
||||
|
||||
await Promise.all([
|
||||
factory.relate('Comment', 'Author', { from: 'u1', to: 'c2' }),
|
||||
factory.relate('Comment', 'Post', { from: 'c2', to: 'p3' })
|
||||
factory.relate('Comment', 'Author', { from: 'u1', to: 'c2' })
|
||||
])
|
||||
|
||||
const asTroll = Factory()
|
||||
await asTroll.authenticateAs({ email: 'troll@example.org', password: '1234' })
|
||||
await asTroll.create('Post', { id: 'p2', title: 'Disabled post', content: 'This is an offensive post content', image: '/some/offensive/image.jpg', deleted: false })
|
||||
await asTroll.create('Comment', { id: 'c1', content: 'Disabled comment' })
|
||||
await asTroll.create('Comment', { id: 'c1', postId: 'p3', content: 'Disabled comment' })
|
||||
await Promise.all([
|
||||
asTroll.relate('Comment', 'Author', { from: 'u2', to: 'c1' }),
|
||||
asTroll.relate('Comment', 'Post', { from: 'c1', to: 'p3' })
|
||||
asTroll.relate('Comment', 'Author', { from: 'u2', to: 'c1' })
|
||||
])
|
||||
|
||||
const asModerator = Factory()
|
||||
|
||||
52
backend/src/resolvers/comments.js
Normal file
52
backend/src/resolvers/comments.js
Normal file
@ -0,0 +1,52 @@
|
||||
import { neo4jgraphql } from 'neo4j-graphql-js'
|
||||
import { UserInputError } from 'apollo-server'
|
||||
|
||||
const COMMENT_MIN_LENGTH = 1
|
||||
export default {
|
||||
Query: {
|
||||
CommentByPost: async (object, params, context, resolveInfo) => {
|
||||
const { postId } = params
|
||||
|
||||
const session = context.driver.session()
|
||||
const transactionRes = await session.run(`
|
||||
MATCH (comment:Comment)-[:COMMENTS]->(post:Post {id: $postId})
|
||||
RETURN comment {.id, .contentExcerpt, .createdAt} ORDER BY comment.createdAt ASC`, {
|
||||
postId
|
||||
})
|
||||
|
||||
session.close()
|
||||
let comments = []
|
||||
transactionRes.records.map(record => {
|
||||
comments.push(record.get('comment'))
|
||||
})
|
||||
|
||||
return comments
|
||||
}
|
||||
},
|
||||
Mutation: {
|
||||
CreateComment: async (object, params, context, resolveInfo) => {
|
||||
const content = params.content.replace(/<(?:.|\n)*?>/gm, '').trim()
|
||||
|
||||
if (!params.content || content.length < COMMENT_MIN_LENGTH) {
|
||||
throw new UserInputError(`Comment must be at least ${COMMENT_MIN_LENGTH} character long!`)
|
||||
}
|
||||
const { postId } = params
|
||||
delete params.postId
|
||||
const comment = await neo4jgraphql(object, params, context, resolveInfo, false)
|
||||
|
||||
const session = context.driver.session()
|
||||
|
||||
await session.run(`
|
||||
MATCH (post:Post {id: $postId}), (comment:Comment {id: $commentId})
|
||||
MERGE (post)<-[:COMMENTS]-(comment)
|
||||
RETURN comment {.id, .content}`, {
|
||||
postId,
|
||||
commentId: comment.id
|
||||
}
|
||||
)
|
||||
session.close()
|
||||
|
||||
return comment
|
||||
}
|
||||
}
|
||||
}
|
||||
81
backend/src/resolvers/comments.spec.js
Normal file
81
backend/src/resolvers/comments.spec.js
Normal file
@ -0,0 +1,81 @@
|
||||
import Factory from '../seed/factories'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import { host, login } from '../jest/helpers'
|
||||
|
||||
const factory = Factory()
|
||||
let client
|
||||
let variables
|
||||
|
||||
beforeEach(async () => {
|
||||
await factory.create('User', {
|
||||
email: 'test@example.org',
|
||||
password: '1234'
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await factory.cleanDatabase()
|
||||
})
|
||||
|
||||
describe('CreateComment', () => {
|
||||
const mutation = `
|
||||
mutation($postId: ID, $content: String!) {
|
||||
CreateComment(postId: $postId, content: $content) {
|
||||
id
|
||||
content
|
||||
}
|
||||
}
|
||||
`
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
variables = {
|
||||
postId: 'p1',
|
||||
content: 'I\'m not authorised to comment'
|
||||
}
|
||||
client = new GraphQLClient(host)
|
||||
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated', () => {
|
||||
let headers
|
||||
beforeEach(async () => {
|
||||
headers = await login({ email: 'test@example.org', password: '1234' })
|
||||
client = new GraphQLClient(host, { headers })
|
||||
})
|
||||
|
||||
it('creates a comment', async () => {
|
||||
variables = {
|
||||
postId: 'p1',
|
||||
content: 'I\'m authorised to comment'
|
||||
}
|
||||
const expected = {
|
||||
CreateComment: {
|
||||
content: 'I\'m authorised to comment'
|
||||
}
|
||||
}
|
||||
|
||||
await expect(client.request(mutation, variables)).resolves.toMatchObject(expected)
|
||||
})
|
||||
|
||||
it('throw an error if an empty string is sent as content', async () => {
|
||||
variables = {
|
||||
postId: 'p1',
|
||||
content: '<p></p>'
|
||||
}
|
||||
|
||||
await expect(client.request(mutation, variables))
|
||||
.rejects.toThrow('Comment must be at least 1 character long!')
|
||||
})
|
||||
|
||||
it('throws an error if a comment does not contain a single character', async () => {
|
||||
variables = {
|
||||
postId: 'p1',
|
||||
content: '<p> </p>'
|
||||
}
|
||||
|
||||
await expect(client.request(mutation, variables))
|
||||
.rejects.toThrow('Comment must be at least 1 character long!')
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -109,11 +109,11 @@ describe('disable', () => {
|
||||
await factory.authenticateAs({ email: 'commenter@example.org', password: '1234' })
|
||||
await Promise.all([
|
||||
factory.create('Post', { id: 'p3' }),
|
||||
factory.create('Comment', { id: 'c47' })
|
||||
factory.create('Comment', { id: 'c47', postId: 'p3', content: 'this comment was created for this post' })
|
||||
])
|
||||
|
||||
await Promise.all([
|
||||
factory.relate('Comment', 'Author', { from: 'u45', to: 'c47' }),
|
||||
factory.relate('Comment', 'Post', { from: 'c47', to: 'p3' })
|
||||
factory.relate('Comment', 'Author', { from: 'u45', to: 'c47' })
|
||||
])
|
||||
}
|
||||
})
|
||||
@ -286,8 +286,7 @@ describe('enable', () => {
|
||||
factory.create('Comment', { id: 'c456' })
|
||||
])
|
||||
await Promise.all([
|
||||
factory.relate('Comment', 'Author', { from: 'u123', to: 'c456' }),
|
||||
factory.relate('Comment', 'Post', { from: 'c456', to: 'p9' })
|
||||
factory.relate('Comment', 'Author', { from: 'u123', to: 'c456' })
|
||||
])
|
||||
|
||||
const disableMutation = `
|
||||
|
||||
@ -3,7 +3,7 @@ import { neo4jgraphql } from 'neo4j-graphql-js'
|
||||
export default {
|
||||
Mutation: {
|
||||
CreateSocialMedia: async (object, params, context, resolveInfo) => {
|
||||
const socialMedia = await neo4jgraphql(object, params, context, resolveInfo, true)
|
||||
const socialMedia = await neo4jgraphql(object, params, context, resolveInfo, false)
|
||||
const session = context.driver.session()
|
||||
await session.run(
|
||||
`MATCH (owner:User {id: $userId}), (socialMedia:SocialMedia {id: $socialMediaId})
|
||||
|
||||
@ -16,6 +16,7 @@ type Query {
|
||||
LIMIT $limit
|
||||
"""
|
||||
)
|
||||
CommentByPost(postId: ID!): [Comment]!
|
||||
}
|
||||
type Mutation {
|
||||
# Get a JWT Token for the given Email and password
|
||||
@ -210,6 +211,7 @@ type Post {
|
||||
type Comment {
|
||||
id: ID!
|
||||
activityId: String
|
||||
postId: ID
|
||||
author: User @relation(name: "WROTE", direction: "IN")
|
||||
content: String!
|
||||
contentExcerpt: String
|
||||
|
||||
@ -4,6 +4,7 @@ import uuid from 'uuid/v4'
|
||||
export default function (params) {
|
||||
const {
|
||||
id = uuid(),
|
||||
postId = 'p6',
|
||||
content = [
|
||||
faker.lorem.sentence(),
|
||||
faker.lorem.sentence()
|
||||
@ -12,12 +13,12 @@ export default function (params) {
|
||||
|
||||
return {
|
||||
mutation: `
|
||||
mutation($id: ID!, $content: String!) {
|
||||
CreateComment(id: $id, content: $content) {
|
||||
mutation($id: ID!, $postId: ID, $content: String!) {
|
||||
CreateComment(id: $id, postId: $postId, content: $content) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { id, content }
|
||||
variables: { id, postId, content }
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,45 +189,33 @@ import Factory from './factories'
|
||||
])
|
||||
|
||||
await Promise.all([
|
||||
f.create('Comment', { id: 'c1' }),
|
||||
f.create('Comment', { id: 'c2' }),
|
||||
f.create('Comment', { id: 'c3' }),
|
||||
f.create('Comment', { id: 'c4' }),
|
||||
f.create('Comment', { id: 'c5' }),
|
||||
f.create('Comment', { id: 'c6' }),
|
||||
f.create('Comment', { id: 'c7' }),
|
||||
f.create('Comment', { id: 'c8' }),
|
||||
f.create('Comment', { id: 'c9' }),
|
||||
f.create('Comment', { id: 'c10' }),
|
||||
f.create('Comment', { id: 'c11' }),
|
||||
f.create('Comment', { id: 'c12' })
|
||||
f.create('Comment', { id: 'c1', postId: 'p1' }),
|
||||
f.create('Comment', { id: 'c2', postId: 'p1' }),
|
||||
f.create('Comment', { id: 'c3', postId: 'p3' }),
|
||||
f.create('Comment', { id: 'c4', postId: 'p2' }),
|
||||
f.create('Comment', { id: 'c5', postId: 'p3' }),
|
||||
f.create('Comment', { id: 'c6', postId: 'p4' }),
|
||||
f.create('Comment', { id: 'c7', postId: 'p2' }),
|
||||
f.create('Comment', { id: 'c8', postId: 'p15' }),
|
||||
f.create('Comment', { id: 'c9', postId: 'p15' }),
|
||||
f.create('Comment', { id: 'c10', postId: 'p15' }),
|
||||
f.create('Comment', { id: 'c11', postId: 'p15' }),
|
||||
f.create('Comment', { id: 'c12', postId: 'p15' })
|
||||
])
|
||||
|
||||
await Promise.all([
|
||||
f.relate('Comment', 'Author', { from: 'u3', to: 'c1' }),
|
||||
f.relate('Comment', 'Post', { from: 'c1', to: 'p1' }),
|
||||
f.relate('Comment', 'Author', { from: 'u1', to: 'c2' }),
|
||||
f.relate('Comment', 'Post', { from: 'c2', to: 'p1' }),
|
||||
f.relate('Comment', 'Author', { from: 'u1', to: 'c3' }),
|
||||
f.relate('Comment', 'Post', { from: 'c3', to: 'p3' }),
|
||||
f.relate('Comment', 'Author', { from: 'u4', to: 'c4' }),
|
||||
f.relate('Comment', 'Post', { from: 'c4', to: 'p2' }),
|
||||
f.relate('Comment', 'Author', { from: 'u4', to: 'c5' }),
|
||||
f.relate('Comment', 'Post', { from: 'c5', to: 'p3' }),
|
||||
f.relate('Comment', 'Author', { from: 'u3', to: 'c6' }),
|
||||
f.relate('Comment', 'Post', { from: 'c6', to: 'p4' }),
|
||||
f.relate('Comment', 'Author', { from: 'u2', to: 'c7' }),
|
||||
f.relate('Comment', 'Post', { from: 'c7', to: 'p2' }),
|
||||
f.relate('Comment', 'Author', { from: 'u5', to: 'c8' }),
|
||||
f.relate('Comment', 'Post', { from: 'c8', to: 'p15' }),
|
||||
f.relate('Comment', 'Author', { from: 'u6', to: 'c9' }),
|
||||
f.relate('Comment', 'Post', { from: 'c9', to: 'p15' }),
|
||||
f.relate('Comment', 'Author', { from: 'u7', to: 'c10' }),
|
||||
f.relate('Comment', 'Post', { from: 'c10', to: 'p15' }),
|
||||
f.relate('Comment', 'Author', { from: 'u5', to: 'c11' }),
|
||||
f.relate('Comment', 'Post', { from: 'c11', to: 'p15' }),
|
||||
f.relate('Comment', 'Author', { from: 'u6', to: 'c12' }),
|
||||
f.relate('Comment', 'Post', { from: 'c12', to: 'p15' })
|
||||
f.relate('Comment', 'Author', { from: 'u6', to: 'c12' })
|
||||
])
|
||||
|
||||
const disableMutation = 'mutation($id: ID!) { disable(id: $id) }'
|
||||
|
||||
20
cypress/integration/common/post.js
Normal file
20
cypress/integration/common/post.js
Normal file
@ -0,0 +1,20 @@
|
||||
import { When, Then } from 'cypress-cucumber-preprocessor/steps'
|
||||
|
||||
Then('I click on the {string} button', text => {
|
||||
cy.get('button').contains(text).click()
|
||||
})
|
||||
|
||||
Then('my comment should be successfully created', () => {
|
||||
cy.get('.iziToast-message')
|
||||
.contains('Comment Submitted')
|
||||
})
|
||||
|
||||
Then('I should see my comment', () => {
|
||||
cy.get('div.comment p')
|
||||
.should('contain', 'Human Connection rocks')
|
||||
})
|
||||
|
||||
Then('the editor should be cleared', () => {
|
||||
cy.get('.ProseMirror p')
|
||||
.should('have.class', 'is-empty')
|
||||
})
|
||||
22
cypress/integration/post/Comment.feature
Normal file
22
cypress/integration/post/Comment.feature
Normal file
@ -0,0 +1,22 @@
|
||||
Feature: Post Comment
|
||||
As a user
|
||||
I want to comment on contributions of others
|
||||
To be able to express my thoughts and emotions about these, discuss, and add give further information.
|
||||
|
||||
Background:
|
||||
Given we have the following posts in our database:
|
||||
| id | title | slug |
|
||||
| bWBjpkTKZp | 101 Essays that will change the way you think | 101-essays |
|
||||
And I have a user account
|
||||
And I am logged in
|
||||
|
||||
Scenario: Comment creation
|
||||
Given I visit "post/bWBjpkTKZp/101-essays"
|
||||
And I type in the following text:
|
||||
"""
|
||||
Human Connection rocks
|
||||
"""
|
||||
And I click on the "Comment" button
|
||||
Then my comment should be successfully created
|
||||
And I should see my comment
|
||||
And the editor should be cleared
|
||||
119
webapp/components/CommentForm/index.vue
Normal file
119
webapp/components/CommentForm/index.vue
Normal file
@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<ds-form
|
||||
v-model="form"
|
||||
@submit="handleSubmit"
|
||||
>
|
||||
<template slot-scope="{ errors }">
|
||||
<ds-card>
|
||||
<no-ssr>
|
||||
<hc-editor
|
||||
ref="editor"
|
||||
:users="users"
|
||||
:value="form.content"
|
||||
@input="updateEditorContent"
|
||||
/>
|
||||
</no-ssr>
|
||||
<ds-space />
|
||||
<ds-flex :gutter="{ base: 'small', md: 'small', sm: 'x-large', xs: 'x-large' }">
|
||||
<ds-flex-item :width="{ base: '0%', md: '50%', sm: '0%', xs: '0%' }" />
|
||||
<ds-flex-item :width="{ base: '40%', md: '20%', sm: '30%', xs: '30%' }">
|
||||
<ds-button
|
||||
:disabled="disabled"
|
||||
ghost
|
||||
@click.prevent="clear"
|
||||
>
|
||||
{{ $t('actions.cancel') }}
|
||||
</ds-button>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '40%', md: '20%', sm: '40%', xs: '40%' }">
|
||||
<ds-button
|
||||
type="submit"
|
||||
:disabled="disabled || errors"
|
||||
primary
|
||||
>
|
||||
{{ $t('post.comment.submit') }}
|
||||
</ds-button>
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
</ds-card>
|
||||
</template>
|
||||
</ds-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import gql from 'graphql-tag'
|
||||
import HcEditor from '~/components/Editor'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HcEditor
|
||||
},
|
||||
props: {
|
||||
post: { type: Object, default: () => {} },
|
||||
comments: { type: Array, default: () => [] }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
disabled: true,
|
||||
form: {
|
||||
content: ''
|
||||
},
|
||||
users: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateEditorContent(value) {
|
||||
const content = value.replace(/<(?:.|\n)*?>/gm, '').trim()
|
||||
if (content.length < 1) {
|
||||
this.disabled = true
|
||||
} else {
|
||||
this.disabled = false
|
||||
}
|
||||
this.form.content = value
|
||||
},
|
||||
clear() {
|
||||
this.$refs.editor.clear()
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: gql`
|
||||
mutation($postId: ID, $content: String!) {
|
||||
CreateComment(postId: $postId, content: $content) {
|
||||
id
|
||||
content
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
postId: this.post.id,
|
||||
content: this.form.content
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
this.$emit('addComment', res.data.CreateComment)
|
||||
this.$refs.editor.clear()
|
||||
this.$toast.success(this.$t('post.comment.submitted'))
|
||||
})
|
||||
.catch(err => {
|
||||
this.$toast.error(err.message)
|
||||
})
|
||||
}
|
||||
},
|
||||
apollo: {
|
||||
User: {
|
||||
query() {
|
||||
return gql(`{
|
||||
User(orderBy: slug_asc) {
|
||||
id
|
||||
slug
|
||||
}
|
||||
}`)
|
||||
},
|
||||
result(result) {
|
||||
this.users = result.data.User
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -154,7 +154,10 @@
|
||||
</ds-button>
|
||||
</div>
|
||||
</editor-floating-menu>
|
||||
<editor-content :editor="editor" />
|
||||
<editor-content
|
||||
ref="editor"
|
||||
:editor="editor"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -224,7 +227,7 @@ export default {
|
||||
new ListItem(),
|
||||
new Placeholder({
|
||||
emptyNodeClass: 'is-empty',
|
||||
emptyNodeText: 'Schreib etwas inspirerendes…'
|
||||
emptyNodeText: this.$t('editor.placeholder')
|
||||
}),
|
||||
new History(),
|
||||
new Mention({
|
||||
@ -445,6 +448,9 @@ export default {
|
||||
// remove link
|
||||
command({ href: null })
|
||||
}
|
||||
},
|
||||
clear() {
|
||||
this.editor.clearContent(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,14 +9,19 @@ localVue.use(Styleguide)
|
||||
describe('Editor.vue', () => {
|
||||
let wrapper
|
||||
let propsData
|
||||
let mocks
|
||||
|
||||
beforeEach(() => {
|
||||
propsData = {}
|
||||
mocks = {
|
||||
$t: () => {}
|
||||
}
|
||||
})
|
||||
|
||||
describe('mount', () => {
|
||||
let Wrapper = () => {
|
||||
return (wrapper = mount(Editor, {
|
||||
mocks,
|
||||
propsData,
|
||||
localVue,
|
||||
sync: false,
|
||||
|
||||
13
webapp/graphql/CommentQuery.js
Normal file
13
webapp/graphql/CommentQuery.js
Normal file
@ -0,0 +1,13 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export default app => {
|
||||
return gql(`
|
||||
query CommentByPost($postId: ID!) {
|
||||
CommentByPost(postId: $postId) {
|
||||
id
|
||||
contentExcerpt
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
`)
|
||||
}
|
||||
@ -8,6 +8,9 @@
|
||||
"moreInfo": "Was ist Human Connection?",
|
||||
"hello": "Hallo"
|
||||
},
|
||||
"editor": {
|
||||
"placeholder": "Schreib etwas Inspirierendes..."
|
||||
},
|
||||
"profile": {
|
||||
"name": "Mein Profil",
|
||||
"memberSince": "Mitglied seit",
|
||||
@ -111,6 +114,10 @@
|
||||
},
|
||||
"takeAction": {
|
||||
"name": "Aktiv werden"
|
||||
},
|
||||
"comment": {
|
||||
"submit": "Kommentiere",
|
||||
"submitted": "Kommentar Gesendet"
|
||||
}
|
||||
},
|
||||
"quotes": {
|
||||
|
||||
@ -8,6 +8,9 @@
|
||||
"moreInfo": "What is Human Connection?",
|
||||
"hello": "Hello"
|
||||
},
|
||||
"editor": {
|
||||
"placeholder": "Leave your inspirational thoughts..."
|
||||
},
|
||||
"profile": {
|
||||
"name": "My Profile",
|
||||
"memberSince": "Member since",
|
||||
@ -111,6 +114,10 @@
|
||||
},
|
||||
"takeAction": {
|
||||
"name": "Take action"
|
||||
},
|
||||
"comment": {
|
||||
"submit": "Comment",
|
||||
"submitted": "Comment Submitted"
|
||||
}
|
||||
},
|
||||
"quotes": {
|
||||
|
||||
@ -96,26 +96,26 @@
|
||||
<ds-space margin="small" />
|
||||
<!-- Comments -->
|
||||
<ds-section slot="footer">
|
||||
<h3 style="margin-top: 0;">
|
||||
<h3 style="margin-top: -10px;">
|
||||
<span>
|
||||
<ds-icon name="comments" />
|
||||
<ds-tag
|
||||
v-if="post.comments"
|
||||
v-if="comments"
|
||||
style="margin-top: -4px; margin-left: -12px; position: absolute;"
|
||||
color="primary"
|
||||
size="small"
|
||||
round
|
||||
>{{ post.commentsCount }}</ds-tag> Comments
|
||||
>{{ comments.length }}</ds-tag> Comments
|
||||
</span>
|
||||
</h3>
|
||||
<ds-space margin-bottom="large" />
|
||||
<div
|
||||
v-if="post.comments"
|
||||
v-if="comments && comments.length"
|
||||
id="comments"
|
||||
class="comments"
|
||||
>
|
||||
<comment
|
||||
v-for="comment in post.comments"
|
||||
v-for="comment in comments"
|
||||
:key="comment.id"
|
||||
:comment="comment"
|
||||
/>
|
||||
@ -124,6 +124,11 @@
|
||||
v-else
|
||||
icon="messages"
|
||||
/>
|
||||
<ds-space margin-bottom="large" />
|
||||
<hc-comment-form
|
||||
:post="post"
|
||||
@addComment="addComment"
|
||||
/>
|
||||
</ds-section>
|
||||
</ds-card>
|
||||
</transition>
|
||||
@ -138,6 +143,7 @@ import ContentMenu from '~/components/ContentMenu'
|
||||
import HcUser from '~/components/User'
|
||||
import HcShoutButton from '~/components/ShoutButton.vue'
|
||||
import HcEmpty from '~/components/Empty.vue'
|
||||
import HcCommentForm from '~/components/CommentForm'
|
||||
import Comment from '~/components/Comment.vue'
|
||||
|
||||
export default {
|
||||
@ -152,7 +158,8 @@ export default {
|
||||
HcShoutButton,
|
||||
HcEmpty,
|
||||
Comment,
|
||||
ContentMenu
|
||||
ContentMenu,
|
||||
HcCommentForm
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
@ -162,6 +169,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
post: null,
|
||||
comments: null,
|
||||
ready: false,
|
||||
title: 'loading'
|
||||
}
|
||||
@ -170,6 +178,9 @@ export default {
|
||||
Post(post) {
|
||||
this.post = post[0] || {}
|
||||
this.title = this.post.title
|
||||
},
|
||||
CommentByPost(comments) {
|
||||
this.comments = comments || []
|
||||
}
|
||||
},
|
||||
async asyncData(context) {
|
||||
@ -278,6 +289,22 @@ export default {
|
||||
methods: {
|
||||
isAuthor(id) {
|
||||
return this.$store.getters['auth/user'].id === id
|
||||
},
|
||||
addComment(comment) {
|
||||
this.$apollo.queries.CommentByPost.refetch()
|
||||
}
|
||||
},
|
||||
apollo: {
|
||||
CommentByPost: {
|
||||
query() {
|
||||
return require('~/graphql/CommentQuery.js').default(this)
|
||||
},
|
||||
variables() {
|
||||
return {
|
||||
postId: this.post.id
|
||||
}
|
||||
},
|
||||
fetchPolicy: 'cache-and-network'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user