mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge branch 'master' of https://github.com/Human-Connection/Human-Connection into 1062-notification-about-comment-on-post
# Conflicts: # backend/src/middleware/index.js
This commit is contained in:
commit
df4f0627b5
@ -61,7 +61,7 @@
|
|||||||
"dotenv": "~8.1.0",
|
"dotenv": "~8.1.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"faker": "Marak/faker.js#master",
|
"faker": "Marak/faker.js#master",
|
||||||
"graphql": "~14.4.2",
|
"graphql": "^14.5.0",
|
||||||
"graphql-custom-directives": "~0.2.14",
|
"graphql-custom-directives": "~0.2.14",
|
||||||
"graphql-iso-date": "~3.6.1",
|
"graphql-iso-date": "~3.6.1",
|
||||||
"graphql-middleware": "~3.0.5",
|
"graphql-middleware": "~3.0.5",
|
||||||
@ -118,7 +118,7 @@
|
|||||||
"cucumber": "~5.1.0",
|
"cucumber": "~5.1.0",
|
||||||
"eslint": "~6.2.1",
|
"eslint": "~6.2.1",
|
||||||
"eslint-config-prettier": "~6.1.0",
|
"eslint-config-prettier": "~6.1.0",
|
||||||
"eslint-config-standard": "~14.0.0",
|
"eslint-config-standard": "~14.0.1",
|
||||||
"eslint-plugin-import": "~2.18.2",
|
"eslint-plugin-import": "~2.18.2",
|
||||||
"eslint-plugin-jest": "~22.15.2",
|
"eslint-plugin-jest": "~22.15.2",
|
||||||
"eslint-plugin-node": "~9.1.0",
|
"eslint-plugin-node": "~9.1.0",
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import CONFIG from './../config'
|
|||||||
// Generate an Access Token for the given User ID
|
// Generate an Access Token for the given User ID
|
||||||
export default function encode(user) {
|
export default function encode(user) {
|
||||||
const token = jwt.sign(user, CONFIG.JWT_SECRET, {
|
const token = jwt.sign(user, CONFIG.JWT_SECRET, {
|
||||||
expiresIn: 24 * 60 * 60 * 1000, // one day
|
expiresIn: '1d',
|
||||||
issuer: CONFIG.GRAPHQL_URI,
|
issuer: CONFIG.GRAPHQL_URI,
|
||||||
audience: CONFIG.CLIENT_URI,
|
audience: CONFIG.CLIENT_URI,
|
||||||
subject: user.id.toString(),
|
subject: user.id.toString(),
|
||||||
|
|||||||
@ -18,22 +18,20 @@ import sentry from './sentryMiddleware'
|
|||||||
|
|
||||||
export default schema => {
|
export default schema => {
|
||||||
const middlewares = {
|
const middlewares = {
|
||||||
permissions: permissions,
|
permissions,
|
||||||
sentry: sentry,
|
sentry,
|
||||||
activityPub: activityPub,
|
activityPub,
|
||||||
dateTime: dateTime,
|
dateTime,
|
||||||
validation: validation,
|
validation,
|
||||||
sluggify: sluggify,
|
sluggify,
|
||||||
excerpt: excerpt,
|
excerpt,
|
||||||
handleNotifications: handleNotifications,
|
handleNotifications,
|
||||||
xss: xss,
|
xss,
|
||||||
softDelete: softDelete,
|
softDelete,
|
||||||
user: user,
|
user,
|
||||||
includedFields: includedFields,
|
includedFields,
|
||||||
orderBy: orderBy,
|
orderBy,
|
||||||
email: email({
|
email: email({ isEnabled: CONFIG.SMTP_HOST && CONFIG.SMTP_PORT }),
|
||||||
isEnabled: CONFIG.SMTP_HOST && CONFIG.SMTP_PORT,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let order = [
|
let order = [
|
||||||
|
|||||||
@ -2,6 +2,8 @@ import { UserInputError } from 'apollo-server'
|
|||||||
|
|
||||||
const COMMENT_MIN_LENGTH = 1
|
const COMMENT_MIN_LENGTH = 1
|
||||||
const NO_POST_ERR_MESSAGE = 'Comment cannot be created without a post!'
|
const NO_POST_ERR_MESSAGE = 'Comment cannot be created without a post!'
|
||||||
|
const NO_CATEGORIES_ERR_MESSAGE =
|
||||||
|
'You cannot save a post without at least one category or more than three'
|
||||||
|
|
||||||
const validateCommentCreation = async (resolve, root, args, context, info) => {
|
const validateCommentCreation = async (resolve, root, args, context, info) => {
|
||||||
const content = args.content.replace(/<(?:.|\n)*?>/gm, '').trim()
|
const content = args.content.replace(/<(?:.|\n)*?>/gm, '').trim()
|
||||||
@ -19,6 +21,7 @@ const validateCommentCreation = async (resolve, root, args, context, info) => {
|
|||||||
postId,
|
postId,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
session.close()
|
||||||
const [post] = postQueryRes.records.map(record => {
|
const [post] = postQueryRes.records.map(record => {
|
||||||
return record.get('post')
|
return record.get('post')
|
||||||
})
|
})
|
||||||
@ -43,9 +46,33 @@ const validateUpdateComment = async (resolve, root, args, context, info) => {
|
|||||||
const validatePost = async (resolve, root, args, context, info) => {
|
const validatePost = async (resolve, root, args, context, info) => {
|
||||||
const { categoryIds } = args
|
const { categoryIds } = args
|
||||||
if (!Array.isArray(categoryIds) || !categoryIds.length || categoryIds.length > 3) {
|
if (!Array.isArray(categoryIds) || !categoryIds.length || categoryIds.length > 3) {
|
||||||
throw new UserInputError(
|
throw new UserInputError(NO_CATEGORIES_ERR_MESSAGE)
|
||||||
'You cannot save a post without at least one category or more than three',
|
}
|
||||||
|
return resolve(root, args, context, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
const validateUpdatePost = async (resolve, root, args, context, info) => {
|
||||||
|
const { id, categoryIds } = args
|
||||||
|
const session = context.driver.session()
|
||||||
|
const categoryQueryRes = await session.run(
|
||||||
|
`
|
||||||
|
MATCH (post:Post {id: $id})-[:CATEGORIZED]->(category:Category)
|
||||||
|
RETURN category`,
|
||||||
|
{ id },
|
||||||
)
|
)
|
||||||
|
session.close()
|
||||||
|
const [category] = categoryQueryRes.records.map(record => {
|
||||||
|
return record.get('category')
|
||||||
|
})
|
||||||
|
|
||||||
|
if (category) {
|
||||||
|
if (categoryIds && categoryIds.length > 3) {
|
||||||
|
throw new UserInputError(NO_CATEGORIES_ERR_MESSAGE)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!Array.isArray(categoryIds) || !categoryIds.length || categoryIds.length > 3) {
|
||||||
|
throw new UserInputError(NO_CATEGORIES_ERR_MESSAGE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return resolve(root, args, context, info)
|
return resolve(root, args, context, info)
|
||||||
}
|
}
|
||||||
@ -55,6 +82,6 @@ export default {
|
|||||||
CreateComment: validateCommentCreation,
|
CreateComment: validateCommentCreation,
|
||||||
UpdateComment: validateUpdateComment,
|
UpdateComment: validateUpdateComment,
|
||||||
CreatePost: validatePost,
|
CreatePost: validatePost,
|
||||||
UpdatePost: validatePost,
|
UpdatePost: validateUpdatePost,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,6 +75,12 @@ export default {
|
|||||||
delete params.categoryIds
|
delete params.categoryIds
|
||||||
params = await fileUpload(params, { file: 'imageUpload', url: 'image' })
|
params = await fileUpload(params, { file: 'imageUpload', url: 'image' })
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
|
|
||||||
|
let updatePostCypher = `MATCH (post:Post {id: $params.id})
|
||||||
|
SET post = $params
|
||||||
|
`
|
||||||
|
|
||||||
|
if (categoryIds && categoryIds.length) {
|
||||||
const cypherDeletePreviousRelations = `
|
const cypherDeletePreviousRelations = `
|
||||||
MATCH (post:Post { id: $params.id })-[previousRelations:CATEGORIZED]->(category:Category)
|
MATCH (post:Post { id: $params.id })-[previousRelations:CATEGORIZED]->(category:Category)
|
||||||
DELETE previousRelations
|
DELETE previousRelations
|
||||||
@ -83,14 +89,14 @@ export default {
|
|||||||
|
|
||||||
await session.run(cypherDeletePreviousRelations, { params })
|
await session.run(cypherDeletePreviousRelations, { params })
|
||||||
|
|
||||||
const updatePostCypher = `MATCH (post:Post {id: $params.id})
|
updatePostCypher += `WITH post
|
||||||
SET post = $params
|
|
||||||
WITH post
|
|
||||||
UNWIND $categoryIds AS categoryId
|
UNWIND $categoryIds AS categoryId
|
||||||
MATCH (category:Category {id: categoryId})
|
MATCH (category:Category {id: categoryId})
|
||||||
MERGE (post)-[:CATEGORIZED]->(category)
|
MERGE (post)-[:CATEGORIZED]->(category)
|
||||||
RETURN post`
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePostCypher += `RETURN post`
|
||||||
const updatePostVariables = { categoryIds, params }
|
const updatePostVariables = { categoryIds, params }
|
||||||
|
|
||||||
const transactionRes = await session.run(updatePostCypher, updatePostVariables)
|
const transactionRes = await session.run(updatePostCypher, updatePostVariables)
|
||||||
|
|||||||
@ -13,6 +13,7 @@ let client
|
|||||||
let userParams
|
let userParams
|
||||||
let authorParams
|
let authorParams
|
||||||
|
|
||||||
|
const postId = 'p3589'
|
||||||
const postTitle = 'I am a title'
|
const postTitle = 'I am a title'
|
||||||
const postContent = 'Some content'
|
const postContent = 'Some content'
|
||||||
const oldTitle = 'Old title'
|
const oldTitle = 'Old title'
|
||||||
@ -96,7 +97,7 @@ beforeEach(async () => {
|
|||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
createPostVariables = {
|
createPostVariables = {
|
||||||
id: 'p3589',
|
id: postId,
|
||||||
title: postTitle,
|
title: postTitle,
|
||||||
content: postContent,
|
content: postContent,
|
||||||
categoryIds,
|
categoryIds,
|
||||||
@ -218,7 +219,7 @@ describe('CreatePost', () => {
|
|||||||
Post: [
|
Post: [
|
||||||
{
|
{
|
||||||
title: postTitle,
|
title: postTitle,
|
||||||
id: 'p3589',
|
id: postId,
|
||||||
categories: expect.arrayContaining(categoryIdsArray),
|
categories: expect.arrayContaining(categoryIdsArray),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -246,17 +247,16 @@ describe('UpdatePost', () => {
|
|||||||
await asAuthor.create('User', authorParams)
|
await asAuthor.create('User', authorParams)
|
||||||
await asAuthor.authenticateAs(authorParams)
|
await asAuthor.authenticateAs(authorParams)
|
||||||
await asAuthor.create('Post', {
|
await asAuthor.create('Post', {
|
||||||
id: 'p1',
|
id: postId,
|
||||||
title: oldTitle,
|
title: oldTitle,
|
||||||
content: oldContent,
|
content: oldContent,
|
||||||
categoryIds,
|
categoryIds,
|
||||||
})
|
})
|
||||||
|
|
||||||
updatePostVariables = {
|
updatePostVariables = {
|
||||||
id: 'p1',
|
id: postId,
|
||||||
title: newTitle,
|
title: newTitle,
|
||||||
content: newContent,
|
content: newContent,
|
||||||
categoryIds: null,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -291,55 +291,96 @@ describe('UpdatePost', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('updates a post', async () => {
|
it('updates a post', async () => {
|
||||||
updatePostVariables.categoryIds = ['cat9']
|
updatePostVariables.categoryIds = ['cat27']
|
||||||
const expected = { UpdatePost: { id: 'p1', content: newContent } }
|
const expected = { UpdatePost: { id: postId, content: newContent } }
|
||||||
await expect(client.request(updatePostMutation, updatePostVariables)).resolves.toEqual(
|
await expect(client.request(updatePostMutation, updatePostVariables)).resolves.toEqual(
|
||||||
expected,
|
expected,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('categories', () => {
|
describe('categories', () => {
|
||||||
beforeEach(async () => {
|
it('allows a user to update other attributes without passing in categoryIds explicitly', async () => {
|
||||||
await client.request(createPostMutation, createPostVariables)
|
const expected = { UpdatePost: { id: postId, content: newContent } }
|
||||||
updatePostVariables = {
|
await expect(client.request(updatePostMutation, updatePostVariables)).resolves.toEqual(
|
||||||
id: 'p3589',
|
expected,
|
||||||
title: newTitle,
|
)
|
||||||
content: newContent,
|
|
||||||
categoryIds: ['cat27'],
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('allows a user to update the categories of a post', async () => {
|
it('allows a user to update the categories of a post', async () => {
|
||||||
|
updatePostVariables.categoryIds = ['cat27']
|
||||||
await client.request(updatePostMutation, updatePostVariables)
|
await client.request(updatePostMutation, updatePostVariables)
|
||||||
const expected = [{ id: 'cat27' }]
|
const expected = [{ id: 'cat27' }]
|
||||||
const postQueryWithCategoriesVariables = {
|
const postQueryWithCategoriesVariables = {
|
||||||
id: 'p3589',
|
id: postId,
|
||||||
}
|
}
|
||||||
await expect(
|
await expect(
|
||||||
client.request(postQueryWithCategories, postQueryWithCategoriesVariables),
|
client.request(postQueryWithCategories, postQueryWithCategoriesVariables),
|
||||||
).resolves.toEqual({ Post: [{ categories: expect.arrayContaining(expected) }] })
|
).resolves.toEqual({ Post: [{ categories: expect.arrayContaining(expected) }] })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws an error if categoryIds is not an array', async () => {
|
|
||||||
updatePostVariables.categoryIds = null
|
|
||||||
await expect(client.request(updatePostMutation, updatePostVariables)).rejects.toThrow(
|
|
||||||
postSaveError,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('requires at least one category for successful update', async () => {
|
|
||||||
updatePostVariables.categoryIds = []
|
|
||||||
await expect(client.request(updatePostMutation, updatePostVariables)).rejects.toThrow(
|
|
||||||
postSaveError,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('allows a maximum of three category for a successful update', async () => {
|
it('allows a maximum of three category for a successful update', async () => {
|
||||||
updatePostVariables.categoryIds = ['cat9', 'cat27', 'cat15', 'cat4']
|
updatePostVariables.categoryIds = ['cat9', 'cat27', 'cat15', 'cat4']
|
||||||
await expect(client.request(updatePostMutation, updatePostVariables)).rejects.toThrow(
|
await expect(client.request(updatePostMutation, updatePostVariables)).rejects.toThrow(
|
||||||
postSaveError,
|
postSaveError,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('post created without categories somehow', () => {
|
||||||
|
let ownerNode, owner, postMutationAction
|
||||||
|
beforeEach(async () => {
|
||||||
|
const postSomehowCreated = await instance.create('Post', {
|
||||||
|
id: 'how-was-this-created',
|
||||||
|
title: postTitle,
|
||||||
|
content: postContent,
|
||||||
|
})
|
||||||
|
ownerNode = await instance.create('User', {
|
||||||
|
id: 'author-of-post-without-category',
|
||||||
|
name: 'Hacker',
|
||||||
|
slug: 'hacker',
|
||||||
|
email: 'hacker@example.org',
|
||||||
|
password: '1234',
|
||||||
|
})
|
||||||
|
owner = await ownerNode.toJson()
|
||||||
|
await postSomehowCreated.relateTo(ownerNode, 'author')
|
||||||
|
postMutationAction = async (user, mutation, variables) => {
|
||||||
|
const { server } = createServer({
|
||||||
|
context: () => {
|
||||||
|
return {
|
||||||
|
user,
|
||||||
|
neode: instance,
|
||||||
|
driver,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const { mutate } = createTestClient(server)
|
||||||
|
|
||||||
|
return mutate({
|
||||||
|
mutation,
|
||||||
|
variables,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
updatePostVariables.id = 'how-was-this-created'
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws an error if categoryIds is not an array', async () => {
|
||||||
|
const mustAddCategoryToPost = await postMutationAction(
|
||||||
|
owner,
|
||||||
|
updatePostMutation,
|
||||||
|
updatePostVariables,
|
||||||
|
)
|
||||||
|
expect(mustAddCategoryToPost.errors[0]).toHaveProperty('message', postSaveError)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('requires at least one category for successful update', async () => {
|
||||||
|
updatePostVariables.categoryIds = []
|
||||||
|
const mustAddCategoryToPost = await postMutationAction(
|
||||||
|
owner,
|
||||||
|
updatePostMutation,
|
||||||
|
updatePostVariables,
|
||||||
|
)
|
||||||
|
expect(mustAddCategoryToPost.errors[0]).toHaveProperty('message', postSaveError)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -355,7 +396,7 @@ describe('DeletePost', () => {
|
|||||||
`
|
`
|
||||||
|
|
||||||
const variables = {
|
const variables = {
|
||||||
id: 'p1',
|
id: postId,
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@ -363,7 +404,7 @@ describe('DeletePost', () => {
|
|||||||
await asAuthor.create('User', authorParams)
|
await asAuthor.create('User', authorParams)
|
||||||
await asAuthor.authenticateAs(authorParams)
|
await asAuthor.authenticateAs(authorParams)
|
||||||
await asAuthor.create('Post', {
|
await asAuthor.create('Post', {
|
||||||
id: 'p1',
|
id: postId,
|
||||||
content: 'To be deleted',
|
content: 'To be deleted',
|
||||||
categoryIds,
|
categoryIds,
|
||||||
})
|
})
|
||||||
@ -396,7 +437,7 @@ describe('DeletePost', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('deletes a post', async () => {
|
it('deletes a post', async () => {
|
||||||
const expected = { DeletePost: { id: 'p1', content: 'To be deleted' } }
|
const expected = { DeletePost: { id: postId, content: 'To be deleted' } }
|
||||||
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
1711
backend/yarn.lock
1711
backend/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -23,7 +23,7 @@
|
|||||||
"codecov": "^3.5.0",
|
"codecov": "^3.5.0",
|
||||||
"cross-env": "^5.2.0",
|
"cross-env": "^5.2.0",
|
||||||
"cypress": "^3.4.1",
|
"cypress": "^3.4.1",
|
||||||
"cypress-cucumber-preprocessor": "^1.14.0",
|
"cypress-cucumber-preprocessor": "^1.14.1",
|
||||||
"cypress-file-upload": "^3.3.3",
|
"cypress-file-upload": "^3.3.3",
|
||||||
"cypress-plugin-retries": "^1.2.2",
|
"cypress-plugin-retries": "^1.2.2",
|
||||||
"dotenv": "^8.1.0",
|
"dotenv": "^8.1.0",
|
||||||
|
|||||||
@ -54,6 +54,7 @@ describe('FilterPosts.vue', () => {
|
|||||||
'postsFilter/TOGGLE_CATEGORY': jest.fn(),
|
'postsFilter/TOGGLE_CATEGORY': jest.fn(),
|
||||||
}
|
}
|
||||||
getters = {
|
getters = {
|
||||||
|
'postsFilter/isActive': () => false,
|
||||||
'auth/user': () => {
|
'auth/user': () => {
|
||||||
return { id: 'u34' }
|
return { id: 'u34' }
|
||||||
},
|
},
|
||||||
@ -63,7 +64,7 @@ describe('FilterPosts.vue', () => {
|
|||||||
const openFilterPosts = () => {
|
const openFilterPosts = () => {
|
||||||
const store = new Vuex.Store({ mutations, getters })
|
const store = new Vuex.Store({ mutations, getters })
|
||||||
const wrapper = mount(FilterPosts, { mocks, localVue, propsData, store })
|
const wrapper = mount(FilterPosts, { mocks, localVue, propsData, store })
|
||||||
menuToggle = wrapper.findAll('a').at(0)
|
menuToggle = wrapper.findAll('button').at(0)
|
||||||
menuToggle.trigger('click')
|
menuToggle.trigger('click')
|
||||||
return wrapper
|
return wrapper
|
||||||
}
|
}
|
||||||
@ -81,13 +82,13 @@ describe('FilterPosts.vue', () => {
|
|||||||
|
|
||||||
it('starts with all categories button active', () => {
|
it('starts with all categories button active', () => {
|
||||||
const wrapper = openFilterPosts()
|
const wrapper = openFilterPosts()
|
||||||
allCategoriesButton = wrapper.findAll('button').at(0)
|
allCategoriesButton = wrapper.findAll('button').at(1)
|
||||||
expect(allCategoriesButton.attributes().class).toContain('ds-button-primary')
|
expect(allCategoriesButton.attributes().class).toContain('ds-button-primary')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls TOGGLE_CATEGORY when clicked', () => {
|
it('calls TOGGLE_CATEGORY when clicked', () => {
|
||||||
const wrapper = openFilterPosts()
|
const wrapper = openFilterPosts()
|
||||||
environmentAndNatureButton = wrapper.findAll('button').at(1)
|
environmentAndNatureButton = wrapper.findAll('button').at(2)
|
||||||
environmentAndNatureButton.trigger('click')
|
environmentAndNatureButton.trigger('click')
|
||||||
expect(mutations['postsFilter/TOGGLE_CATEGORY']).toHaveBeenCalledWith({}, 'cat4')
|
expect(mutations['postsFilter/TOGGLE_CATEGORY']).toHaveBeenCalledWith({}, 'cat4')
|
||||||
})
|
})
|
||||||
@ -95,7 +96,7 @@ describe('FilterPosts.vue', () => {
|
|||||||
it('sets category button attribute `primary` when corresponding category is filtered', () => {
|
it('sets category button attribute `primary` when corresponding category is filtered', () => {
|
||||||
getters['postsFilter/filteredCategoryIds'] = jest.fn(() => ['cat9'])
|
getters['postsFilter/filteredCategoryIds'] = jest.fn(() => ['cat9'])
|
||||||
const wrapper = openFilterPosts()
|
const wrapper = openFilterPosts()
|
||||||
democracyAndPoliticsButton = wrapper.findAll('button').at(3)
|
democracyAndPoliticsButton = wrapper.findAll('button').at(4)
|
||||||
expect(democracyAndPoliticsButton.attributes().class).toContain('ds-button-primary')
|
expect(democracyAndPoliticsButton.attributes().class).toContain('ds-button-primary')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<dropdown ref="menu" :placement="placement" :offset="offset">
|
<dropdown ref="menu" :placement="placement" :offset="offset">
|
||||||
<a slot="default" slot-scope="{ toggleMenu }" href="#" @click.prevent="toggleMenu()">
|
<ds-button
|
||||||
<ds-icon style="margin: 12px 0px 0px 10px;" name="filter" size="large" />
|
slot="default"
|
||||||
<ds-icon style="margin: 7px 0px 0px 2px" size="xx-small" name="angle-down" />
|
icon="filter"
|
||||||
</a>
|
:primary="filterActive"
|
||||||
|
:ghost="!filterActive"
|
||||||
|
slot-scope="{ toggleMenu }"
|
||||||
|
@click.prevent="toggleMenu()"
|
||||||
|
>
|
||||||
|
<ds-icon size="xx-small" name="angle-down" />
|
||||||
|
</ds-button>
|
||||||
<template slot="popover">
|
<template slot="popover">
|
||||||
<filter-posts-menu-items :chunk="chunk" :user="currentUser" />
|
<filter-posts-menu-items :chunk="chunk" :user="currentUser" />
|
||||||
</template>
|
</template>
|
||||||
@ -28,6 +34,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
currentUser: 'auth/user',
|
currentUser: 'auth/user',
|
||||||
|
filterActive: 'postsFilter/isActive',
|
||||||
}),
|
}),
|
||||||
chunk() {
|
chunk() {
|
||||||
return _.chunk(this.categories, 2)
|
return _.chunk(this.categories, 2)
|
||||||
|
|||||||
@ -24,6 +24,7 @@ export default i18n => {
|
|||||||
badgesCount
|
badgesCount
|
||||||
shoutedCount
|
shoutedCount
|
||||||
commentedCount
|
commentedCount
|
||||||
|
contributionsCount
|
||||||
followingCount
|
followingCount
|
||||||
following(first: 7) {
|
following(first: 7) {
|
||||||
id
|
id
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
<div class="main-navigation">
|
<div class="main-navigation">
|
||||||
<ds-container class="main-navigation-container" style="padding: 10px 10px;">
|
<ds-container class="main-navigation-container" style="padding: 10px 10px;">
|
||||||
<div>
|
<div>
|
||||||
<ds-flex class="main-navigation-flex">
|
<ds-flex class="main-navigation-flex" centered>
|
||||||
<ds-flex-item :width="{ lg: '3.5%' }" />
|
<ds-flex-item :width="{ lg: '3.5%' }" />
|
||||||
<ds-flex-item :width="{ base: '80%', sm: '80%', md: '80%', lg: '15%' }">
|
<ds-flex-item :width="{ base: '80%', sm: '80%', md: '80%', lg: '15%' }">
|
||||||
<a @click="redirectToRoot">
|
<a @click="redirectToRoot">
|
||||||
@ -293,6 +293,11 @@ export default {
|
|||||||
padding-top: 6rem;
|
padding-top: 6rem;
|
||||||
padding-bottom: 5rem;
|
padding-bottom: 5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.main-navigation-flex {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.main-navigation {
|
.main-navigation {
|
||||||
a {
|
a {
|
||||||
color: $text-color-soft;
|
color: $text-color-soft;
|
||||||
|
|||||||
@ -271,7 +271,7 @@ module.exports = {
|
|||||||
apollo: {
|
apollo: {
|
||||||
tokenName: 'human-connection-token', // optional, default: apollo-token
|
tokenName: 'human-connection-token', // optional, default: apollo-token
|
||||||
cookieAttributes: {
|
cookieAttributes: {
|
||||||
expires: 3, // optional, default: 7 (days)
|
expires: 1, // optional, default: 7 (days)
|
||||||
},
|
},
|
||||||
// includeNodeModules: true, // optional, default: false (this includes graphql-tag for node_modules folder)
|
// includeNodeModules: true, // optional, default: false (this includes graphql-tag for node_modules folder)
|
||||||
|
|
||||||
|
|||||||
@ -63,7 +63,7 @@
|
|||||||
"cross-env": "~5.2.0",
|
"cross-env": "~5.2.0",
|
||||||
"date-fns": "2.0.0",
|
"date-fns": "2.0.0",
|
||||||
"express": "~4.17.1",
|
"express": "~4.17.1",
|
||||||
"graphql": "~14.4.2",
|
"graphql": "~14.5.0",
|
||||||
"isemail": "^3.2.0",
|
"isemail": "^3.2.0",
|
||||||
"jsonwebtoken": "~8.5.1",
|
"jsonwebtoken": "~8.5.1",
|
||||||
"linkify-it": "~2.2.0",
|
"linkify-it": "~2.2.0",
|
||||||
@ -78,7 +78,7 @@
|
|||||||
"v-tooltip": "~2.0.2",
|
"v-tooltip": "~2.0.2",
|
||||||
"vue-count-to": "~1.0.13",
|
"vue-count-to": "~1.0.13",
|
||||||
"vue-infinite-scroll": "^2.0.2",
|
"vue-infinite-scroll": "^2.0.2",
|
||||||
"vue-izitoast": "roschaefer/vue-izitoast#patch-1",
|
"vue-izitoast": "^1.2.0",
|
||||||
"vue-sweetalert-icons": "~4.2.0",
|
"vue-sweetalert-icons": "~4.2.0",
|
||||||
"vuex-i18n": "~1.13.1",
|
"vuex-i18n": "~1.13.1",
|
||||||
"zxcvbn": "^4.4.2"
|
"zxcvbn": "^4.4.2"
|
||||||
|
|||||||
@ -144,10 +144,4 @@ export const actions = {
|
|||||||
commit('SET_TOKEN', null)
|
commit('SET_TOKEN', null)
|
||||||
return this.app.$apolloHelpers.onLogout()
|
return this.app.$apolloHelpers.onLogout()
|
||||||
},
|
},
|
||||||
|
|
||||||
register({ dispatch, commit }, { email, password, inviteCode, invitedByUserId }) {},
|
|
||||||
async patch({ state, commit, dispatch }, data) {},
|
|
||||||
resendVerifySignup({ state, dispatch }) {},
|
|
||||||
resetPassword({ state }, data) {},
|
|
||||||
setNewPassword({ state }, data) {},
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,11 +2,16 @@ import get from 'lodash/get'
|
|||||||
import update from 'lodash/update'
|
import update from 'lodash/update'
|
||||||
import xor from 'lodash/xor'
|
import xor from 'lodash/xor'
|
||||||
import isEmpty from 'lodash/isEmpty'
|
import isEmpty from 'lodash/isEmpty'
|
||||||
|
import isEqual from 'lodash/isEqual'
|
||||||
import clone from 'lodash/clone'
|
import clone from 'lodash/clone'
|
||||||
|
|
||||||
|
const defaultFilter = {}
|
||||||
|
|
||||||
export const state = () => {
|
export const state = () => {
|
||||||
return {
|
return {
|
||||||
filter: {},
|
filter: {
|
||||||
|
...defaultFilter,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,6 +43,9 @@ export const mutations = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getters = {
|
export const getters = {
|
||||||
|
isActive(state) {
|
||||||
|
return !isEqual(state.filter, defaultFilter)
|
||||||
|
},
|
||||||
postsFilter(state) {
|
postsFilter(state) {
|
||||||
return state.filter
|
return state.filter
|
||||||
},
|
},
|
||||||
|
|||||||
@ -4,6 +4,15 @@ let state
|
|||||||
let testAction
|
let testAction
|
||||||
|
|
||||||
describe('getters', () => {
|
describe('getters', () => {
|
||||||
|
describe('isActive', () => {
|
||||||
|
it('returns true if filter differs from default setting', () => {
|
||||||
|
state = { filter: {} }
|
||||||
|
expect(getters.isActive(state)).toEqual(false)
|
||||||
|
state = { filter: { categories_some: { id_in: [24] } } }
|
||||||
|
expect(getters.isActive(state)).toEqual(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('filteredCategoryIds', () => {
|
describe('filteredCategoryIds', () => {
|
||||||
it('returns category ids if filter is set', () => {
|
it('returns category ids if filter is set', () => {
|
||||||
state = { filter: { categories_some: { id_in: [24] } } }
|
state = { filter: { categories_some: { id_in: [24] } } }
|
||||||
|
|||||||
@ -7624,10 +7624,10 @@ graphql-upload@^8.0.2:
|
|||||||
http-errors "^1.7.2"
|
http-errors "^1.7.2"
|
||||||
object-path "^0.11.4"
|
object-path "^0.11.4"
|
||||||
|
|
||||||
"graphql@14.0.2 - 14.2.0 || ^14.3.1", graphql@^14.4.0, graphql@~14.4.2:
|
"graphql@14.0.2 - 14.2.0 || ^14.3.1", graphql@^14.4.0, graphql@~14.5.0:
|
||||||
version "14.4.2"
|
version "14.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-14.4.2.tgz#553a7d546d524663eda49ed6df77577be3203ae3"
|
resolved "https://registry.yarnpkg.com/graphql/-/graphql-14.5.0.tgz#4801e6460942c9c591944617f6dd224a9e531520"
|
||||||
integrity sha512-6uQadiRgnpnSS56hdZUSvFrVcQ6OF9y6wkxJfKquFtHlnl7+KSuWwSJsdwiK1vybm1HgcdbpGkCpvhvsVQ0UZQ==
|
integrity sha512-wnGcTD181L2xPnIwHHjx/moV4ulxA2Kms9zcUY+B/SIrK+2N+iOC6WNgnR2zVTmg1Z8P+CZq5KXibTnatg3WUw==
|
||||||
dependencies:
|
dependencies:
|
||||||
iterall "^1.2.2"
|
iterall "^1.2.2"
|
||||||
|
|
||||||
@ -8851,7 +8851,7 @@ iterall@^1.1.3, iterall@^1.2.1, iterall@^1.2.2:
|
|||||||
resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.2.2.tgz#92d70deb8028e0c39ff3164fdbf4d8b088130cd7"
|
resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.2.2.tgz#92d70deb8028e0c39ff3164fdbf4d8b088130cd7"
|
||||||
integrity sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA==
|
integrity sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA==
|
||||||
|
|
||||||
izitoast@^1.3.0:
|
izitoast@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/izitoast/-/izitoast-1.4.0.tgz#1aa4e3408b7159fba743506af66d8be54fd929fb"
|
resolved "https://registry.yarnpkg.com/izitoast/-/izitoast-1.4.0.tgz#1aa4e3408b7159fba743506af66d8be54fd929fb"
|
||||||
integrity sha512-Oc1X2wiQtPp39i5VpIjf3GJf5sfCtHKXZ5szx7RareyEeFLUlcEW0FSfBni28+Ul6KNKZRKzhVuWzSP4Xngh0w==
|
integrity sha512-Oc1X2wiQtPp39i5VpIjf3GJf5sfCtHKXZ5szx7RareyEeFLUlcEW0FSfBni28+Ul6KNKZRKzhVuWzSP4Xngh0w==
|
||||||
@ -15159,11 +15159,12 @@ vue-infinite-scroll@^2.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/vue-infinite-scroll/-/vue-infinite-scroll-2.0.2.tgz#ca37a91fe92ee0ad3b74acf8682c00917144b711"
|
resolved "https://registry.yarnpkg.com/vue-infinite-scroll/-/vue-infinite-scroll-2.0.2.tgz#ca37a91fe92ee0ad3b74acf8682c00917144b711"
|
||||||
integrity sha512-n+YghR059YmciANGJh9SsNWRi1YZEBVlODtmnb/12zI+4R72QZSWd+EuZ5mW6auEo/yaJXgxzwsuhvALVnm73A==
|
integrity sha512-n+YghR059YmciANGJh9SsNWRi1YZEBVlODtmnb/12zI+4R72QZSWd+EuZ5mW6auEo/yaJXgxzwsuhvALVnm73A==
|
||||||
|
|
||||||
vue-izitoast@roschaefer/vue-izitoast#patch-1:
|
vue-izitoast@^1.2.0:
|
||||||
version "1.1.2"
|
version "1.2.0"
|
||||||
resolved "https://codeload.github.com/roschaefer/vue-izitoast/tar.gz/ba6b03eb24c7c04c299e64a9703e101bf158ae50"
|
resolved "https://registry.yarnpkg.com/vue-izitoast/-/vue-izitoast-1.2.0.tgz#55b7434a391c6eb64dd10c0de211e99ba7e486e2"
|
||||||
|
integrity sha512-Jqxfid12SUBIySJxgyPpu6gZ1ssMcbKtCvu9uMQPNM8RUnd3RKC4nyxkncdYe5L6XPU+SaznjYRudnvtclY4wA==
|
||||||
dependencies:
|
dependencies:
|
||||||
izitoast "^1.3.0"
|
izitoast "^1.4.0"
|
||||||
|
|
||||||
vue-jest@~3.0.4:
|
vue-jest@~3.0.4:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
|
|||||||
@ -1847,10 +1847,10 @@ cucumber@^4.2.1:
|
|||||||
util-arity "^1.0.2"
|
util-arity "^1.0.2"
|
||||||
verror "^1.9.0"
|
verror "^1.9.0"
|
||||||
|
|
||||||
cypress-cucumber-preprocessor@^1.14.0:
|
cypress-cucumber-preprocessor@^1.14.1:
|
||||||
version "1.14.0"
|
version "1.14.1"
|
||||||
resolved "https://registry.yarnpkg.com/cypress-cucumber-preprocessor/-/cypress-cucumber-preprocessor-1.14.0.tgz#79fa9d4da72e2cb56bf511c17c77f8200b3279f1"
|
resolved "https://registry.yarnpkg.com/cypress-cucumber-preprocessor/-/cypress-cucumber-preprocessor-1.14.1.tgz#47a6ddd28ca1b0a4ec24707b20ca5c0d7ffc7875"
|
||||||
integrity sha512-eo79d7XCX4JG5wWGQJ2tuONTEgQfZHNGvuBMwTNjvZcz+qEVy04kdiBzbQGB7uy2Lf9LAkabt1hKUjwewmMioA==
|
integrity sha512-72RN3sUeq0r4aNiFfztQD6Uvi58fifkfc2R4pCyX6oTEZLfHfcudkNQLd98ByCyiWXNXPHlGGihE6+EPCDK59A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cypress/browserify-preprocessor" "^1.1.2"
|
"@cypress/browserify-preprocessor" "^1.1.2"
|
||||||
chai "^4.1.2"
|
chai "^4.1.2"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user