Merge branch 'master' into cropper-enhancements

This commit is contained in:
Brent Vardy 2019-11-02 09:42:38 +00:00
commit daf40bbb7c
76 changed files with 18171 additions and 2239 deletions

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,7 @@
* [Maintenance](deployment/human-connection/maintenance/README.md) * [Maintenance](deployment/human-connection/maintenance/README.md)
* [Volumes](deployment/volumes/README.md) * [Volumes](deployment/volumes/README.md)
* [Neo4J Offline-Backups](deployment/volumes/neo4j-offline-backup/README.md) * [Neo4J Offline-Backups](deployment/volumes/neo4j-offline-backup/README.md)
* [Neo4J Online-Backups](deployment/volumes/neo4j-online-backup/README.md)
* [Volume Snapshots](deployment/volumes/volume-snapshots/README.md) * [Volume Snapshots](deployment/volumes/volume-snapshots/README.md)
* [Reclaim Policy](deployment/volumes/reclaim-policy/README.md) * [Reclaim Policy](deployment/volumes/reclaim-policy/README.md)
* [Velero](deployment/volumes/velero/README.md) * [Velero](deployment/volumes/velero/README.md)

View File

@ -1 +1 @@
0.1.3 0.1.8

13258
backend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -42,19 +42,19 @@
}, },
"dependencies": { "dependencies": {
"@hapi/joi": "^16.1.7", "@hapi/joi": "^16.1.7",
"@sentry/node": "^5.7.0", "@sentry/node": "^5.7.1",
"apollo-cache-inmemory": "~1.6.3", "apollo-cache-inmemory": "~1.6.3",
"apollo-client": "~2.6.4", "apollo-client": "~2.6.4",
"apollo-link-context": "~1.0.19", "apollo-link-context": "~1.0.19",
"apollo-link-http": "~1.5.16", "apollo-link-http": "~1.5.16",
"apollo-server": "~2.9.6", "apollo-server": "~2.9.7",
"apollo-server-express": "^2.9.6", "apollo-server-express": "^2.9.7",
"babel-plugin-transform-runtime": "^6.23.0", "babel-plugin-transform-runtime": "^6.23.0",
"bcryptjs": "~2.4.3", "bcryptjs": "~2.4.3",
"cheerio": "~1.0.0-rc.3", "cheerio": "~1.0.0-rc.3",
"cors": "~2.8.5", "cors": "~2.8.5",
"cross-env": "~6.0.3", "cross-env": "~6.0.3",
"date-fns": "2.5.0", "date-fns": "2.6.0",
"debug": "~4.1.1", "debug": "~4.1.1",
"dotenv": "~8.2.0", "dotenv": "~8.2.0",
"express": "^4.17.1", "express": "^4.17.1",
@ -64,33 +64,33 @@
"graphql-iso-date": "~3.6.1", "graphql-iso-date": "~3.6.1",
"graphql-middleware": "~4.0.1", "graphql-middleware": "~4.0.1",
"graphql-middleware-sentry": "^3.2.1", "graphql-middleware-sentry": "^3.2.1",
"graphql-shield": "~6.1.0", "graphql-shield": "~7.0.0",
"graphql-tag": "~2.10.1", "graphql-tag": "~2.10.1",
"helmet": "~3.21.1", "helmet": "~3.21.2",
"jsonwebtoken": "~8.5.1", "jsonwebtoken": "~8.5.1",
"linkifyjs": "~2.1.8", "linkifyjs": "~2.1.8",
"lodash": "~4.17.14", "lodash": "~4.17.14",
"merge-graphql-schemas": "^1.7.0", "merge-graphql-schemas": "^1.7.0",
"metascraper": "^4.10.3", "metascraper": "^4.10.3",
"metascraper-audio": "^5.7.6", "metascraper-audio": "^5.7.17",
"metascraper-author": "^5.7.6", "metascraper-author": "^5.7.17",
"metascraper-clearbit-logo": "^5.3.0", "metascraper-clearbit-logo": "^5.3.0",
"metascraper-date": "^5.7.6", "metascraper-date": "^5.7.17",
"metascraper-description": "^5.7.6", "metascraper-description": "^5.7.17",
"metascraper-image": "^5.7.6", "metascraper-image": "^5.7.17",
"metascraper-lang": "^5.7.6", "metascraper-lang": "^5.7.17",
"metascraper-lang-detector": "^4.8.5", "metascraper-lang-detector": "^4.8.5",
"metascraper-logo": "^5.7.6", "metascraper-logo": "^5.7.17",
"metascraper-publisher": "^5.7.6", "metascraper-publisher": "^5.7.17",
"metascraper-soundcloud": "^5.7.6", "metascraper-soundcloud": "^5.7.17",
"metascraper-title": "^5.7.6", "metascraper-title": "^5.7.17",
"metascraper-url": "^5.7.6", "metascraper-url": "^5.7.17",
"metascraper-video": "^5.7.6", "metascraper-video": "^5.7.17",
"metascraper-youtube": "^5.7.6", "metascraper-youtube": "^5.7.17",
"minimatch": "^3.0.4", "minimatch": "^3.0.4",
"mustache": "^3.1.0", "mustache": "^3.1.0",
"neo4j-driver": "~1.7.6", "neo4j-driver": "~1.7.6",
"neo4j-graphql-js": "^2.7.2", "neo4j-graphql-js": "^2.8.0",
"neode": "^0.3.3", "neode": "^0.3.3",
"node-fetch": "~2.6.0", "node-fetch": "~2.6.0",
"nodemailer": "^6.3.1", "nodemailer": "^6.3.1",
@ -111,17 +111,17 @@
"@babel/plugin-proposal-throw-expressions": "^7.2.0", "@babel/plugin-proposal-throw-expressions": "^7.2.0",
"@babel/preset-env": "~7.6.3", "@babel/preset-env": "~7.6.3",
"@babel/register": "~7.6.2", "@babel/register": "~7.6.2",
"apollo-server-testing": "~2.9.6", "apollo-server-testing": "~2.9.7",
"babel-core": "~7.0.0-0", "babel-core": "~7.0.0-0",
"babel-eslint": "~10.0.3", "babel-eslint": "~10.0.3",
"babel-jest": "~24.9.0", "babel-jest": "~24.9.0",
"chai": "~4.2.0", "chai": "~4.2.0",
"cucumber": "~6.0.2", "cucumber": "~6.0.3",
"eslint": "~6.5.1", "eslint": "~6.6.0",
"eslint-config-prettier": "~6.4.0", "eslint-config-prettier": "~6.5.0",
"eslint-config-standard": "~14.1.0", "eslint-config-standard": "~14.1.0",
"eslint-plugin-import": "~2.18.2", "eslint-plugin-import": "~2.18.2",
"eslint-plugin-jest": "~22.19.0", "eslint-plugin-jest": "~23.0.2",
"eslint-plugin-node": "~10.0.0", "eslint-plugin-node": "~10.0.0",
"eslint-plugin-prettier": "~3.1.1", "eslint-plugin-prettier": "~3.1.1",
"eslint-plugin-promise": "~4.2.1", "eslint-plugin-promise": "~4.2.1",

View File

@ -24,7 +24,7 @@
<h1 <h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;"> style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hallo!</h1> Hallo!</h1>
<p style="margin: 0;">Du hast bei uns ein neues Password angefordert leider haben wir aber keinen <p style="margin: 0;">Du hast bei uns ein neues Passwort angefordert leider haben wir aber keinen
Account mit Deiner E-Mailadresse gefunden. Kann es sein, dass Du mit einer anderen Adresse bei uns Account mit Deiner E-Mailadresse gefunden. Kann es sein, dass Du mit einer anderen Adresse bei uns
angemeldet bist?</p> angemeldet bist?</p>
</td> </td>

View File

@ -1,5 +1,21 @@
import extractMentionedUsers from './mentions/extractMentionedUsers' import extractMentionedUsers from './mentions/extractMentionedUsers'
const postAuthorOfComment = async (comment, { context }) => {
const session = context.driver.session()
const cypherFindUser = `
MATCH (user: User)-[:WROTE]->(:Post)<-[:COMMENTS]-(:Comment { id: $commentId })
RETURN user { .id }
`
const result = await session.run(cypherFindUser, {
commentId: comment.id,
})
session.close()
const [postAuthor] = await result.records.map(record => {
return record.get('user')
})
return postAuthor
}
const notifyUsers = async (label, id, idsOfUsers, reason, context) => { const notifyUsers = async (label, id, idsOfUsers, reason, context) => {
if (!idsOfUsers.length) return if (!idsOfUsers.length) return
@ -90,11 +106,13 @@ const handleContentDataOfPost = async (resolve, root, args, context, resolveInfo
} }
const handleContentDataOfComment = async (resolve, root, args, context, resolveInfo) => { const handleContentDataOfComment = async (resolve, root, args, context, resolveInfo) => {
const idsOfUsers = extractMentionedUsers(args.content) let idsOfUsers = extractMentionedUsers(args.content)
const comment = await resolve(root, args, context, resolveInfo) const comment = await resolve(root, args, context, resolveInfo)
if (comment) { if (comment) {
const postAuthor = await postAuthorOfComment(comment, { context })
idsOfUsers = idsOfUsers.filter(id => id !== postAuthor.id)
await notifyUsers('Comment', comment.id, idsOfUsers, 'mentioned_in_comment', context) await notifyUsers('Comment', comment.id, idsOfUsers, 'mentioned_in_comment', context)
} }

View File

@ -105,6 +105,7 @@ describe('notifications', () => {
let title let title
let postContent let postContent
let postAuthor let postAuthor
const createPostAction = async () => { const createPostAction = async () => {
authenticatedUser = await postAuthor.toJson() authenticatedUser = await postAuthor.toJson()
await mutate({ await mutate({
@ -239,6 +240,7 @@ describe('notifications', () => {
describe('mentions me in a post', () => { describe('mentions me in a post', () => {
beforeEach(async () => { beforeEach(async () => {
title = 'Mentioning Al Capone' title = 'Mentioning Al Capone'
postContent = postContent =
'Hey <a class="mention" data-mention-id="you" href="/profile/you/al-capone">@al-capone</a> how do you do?' 'Hey <a class="mention" data-mention-id="you" href="/profile/you/al-capone">@al-capone</a> how do you do?'
}) })
@ -439,7 +441,15 @@ describe('notifications', () => {
}) })
}) })
it('sends a notification', async () => { it('sends only one notification with reason mentioned_in_comment', async () => {
postAuthor = await instance.create('User', {
id: 'MrPostAuthor',
name: 'Mr Author',
slug: 'mr-author',
email: 'post-author@example.org',
password: '1234',
})
await createCommentOnPostAction() await createCommentOnPostAction()
const expected = expect.objectContaining({ const expected = expect.objectContaining({
data: { data: {
@ -467,6 +477,40 @@ describe('notifications', () => {
}), }),
).resolves.toEqual(expected) ).resolves.toEqual(expected)
}) })
beforeEach(async () => {
title = "Post where I'm the author and I get mentioned in a comment"
postContent = 'Content of post where I get mentioned in a comment.'
postAuthor = notifiedUser
})
it('sends only one notification with reason commented_on_post, no notification with reason mentioned_in_comment', async () => {
await createCommentOnPostAction()
const expected = expect.objectContaining({
data: {
notifications: [
{
read: false,
createdAt: expect.any(String),
reason: 'commented_on_post',
from: {
__typename: 'Comment',
id: 'c47',
content: commentContent,
},
},
],
},
})
const { query } = createTestClient(server)
await expect(
query({
query: notificationQuery,
variables: {
read: false,
},
}),
).resolves.toEqual(expected)
})
}) })
describe('but the author of the post blocked me', () => { describe('but the author of the post blocked me', () => {

View File

@ -134,6 +134,7 @@ const permissions = shield(
PostsEmotionsByCurrentUser: isAuthenticated, PostsEmotionsByCurrentUser: isAuthenticated,
blockedUsers: isAuthenticated, blockedUsers: isAuthenticated,
notifications: isAuthenticated, notifications: isAuthenticated,
profilePagePosts: or(onlyEnabledContent, isModerator),
}, },
Mutation: { Mutation: {
'*': deny, '*': deny,
@ -174,6 +175,8 @@ const permissions = shield(
markAsRead: isAuthenticated, markAsRead: isAuthenticated,
AddEmailAddress: isAuthenticated, AddEmailAddress: isAuthenticated,
VerifyEmailAddress: isAuthenticated, VerifyEmailAddress: isAuthenticated,
pinPost: isAdmin,
unpinPost: isAdmin,
}, },
User: { User: {
email: or(isMyOwn, isAdmin), email: or(isMyOwn, isAdmin),

View File

@ -32,6 +32,7 @@ export default {
Post: setDefaultFilters, Post: setDefaultFilters,
Comment: setDefaultFilters, Comment: setDefaultFilters,
User: setDefaultFilters, User: setDefaultFilters,
profilePagePosts: setDefaultFilters,
}, },
Mutation: async (resolve, root, args, context, info) => { Mutation: async (resolve, root, args, context, info) => {
args.disabled = false args.disabled = false

View File

@ -28,12 +28,18 @@ module.exports = {
relationship: 'FOLLOWS', relationship: 'FOLLOWS',
target: 'User', target: 'User',
direction: 'out', direction: 'out',
properties: {
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
},
}, },
followedBy: { followedBy: {
type: 'relationship', type: 'relationship',
relationship: 'FOLLOWS', relationship: 'FOLLOWS',
target: 'User', target: 'User',
direction: 'in', direction: 'in',
properties: {
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
},
}, },
friends: { type: 'relationship', relationship: 'FRIENDS', target: 'User', direction: 'both' }, friends: { type: 'relationship', relationship: 'FRIENDS', target: 'User', direction: 'both' },
disabledBy: { disabledBy: {
@ -98,6 +104,9 @@ module.exports = {
relationship: 'SHOUTED', relationship: 'SHOUTED',
target: 'Post', target: 'Post',
direction: 'out', direction: 'out',
properties: {
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
},
}, },
isIn: { isIn: {
type: 'relationship', type: 'relationship',
@ -105,8 +114,21 @@ module.exports = {
target: 'Location', target: 'Location',
direction: 'out', direction: 'out',
}, },
pinned: {
type: 'relationship',
relationship: 'PINNED',
target: 'Post',
direction: 'out',
properties: {
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
},
},
allowEmbedIframes: { allowEmbedIframes: {
type: 'boolean', type: 'boolean',
default: false, default: false,
}, },
locale: {
type: 'string',
allow: [null],
},
} }

View File

@ -131,6 +131,21 @@ describe('follow', () => {
}) })
}) })
test('adds `createdAt` to `FOLLOW` relationship', async () => {
await mutate({
mutation: mutationFollowUser,
variables,
})
const relation = await neode.cypher(
'MATCH (user:User {id: {id}})-[relationship:FOLLOWS]->(followed:User) WHERE relationship.createdAt IS NOT NULL RETURN relationship',
{ id: 'u1' },
)
const relationshipProperties = relation.records.map(
record => record.get('relationship').properties.createdAt,
)
expect(relationshipProperties[0]).toEqual(expect.any(String))
})
test('I can`t follow myself', async () => { test('I can`t follow myself', async () => {
variables.id = user1.id variables.id = user1.id
await expect(mutate({ mutation: mutationFollowUser, variables })).resolves.toMatchObject({ await expect(mutate({ mutation: mutationFollowUser, variables })).resolves.toMatchObject({
@ -155,6 +170,7 @@ describe('follow', () => {
}) })
}) })
}) })
describe('unfollow user', () => { describe('unfollow user', () => {
beforeEach(async () => { beforeEach(async () => {
variables = { id: user2.id } variables = { id: user2.id }

View File

@ -86,6 +86,7 @@ export default function Resolver(type, options = {}) {
} }
return resolvers return resolvers
} }
const result = { const result = {
...undefinedToNullResolver(undefinedToNull), ...undefinedToNullResolver(undefinedToNull),
...booleanResolver(boolean), ...booleanResolver(boolean),

View File

@ -2,10 +2,9 @@ import uuid from 'uuid/v4'
import { neo4jgraphql } from 'neo4j-graphql-js' import { neo4jgraphql } from 'neo4j-graphql-js'
import fileUpload from './fileUpload' import fileUpload from './fileUpload'
import { getBlockedUsers, getBlockedByUsers } from './users.js' import { getBlockedUsers, getBlockedByUsers } from './users.js'
import { mergeWith, isArray } from 'lodash' import { mergeWith, isArray, isEmpty } from 'lodash'
import { UserInputError } from 'apollo-server' import { UserInputError } from 'apollo-server'
import Resolver from './helpers/Resolver' import Resolver from './helpers/Resolver'
const filterForBlockedUsers = async (params, context) => { const filterForBlockedUsers = async (params, context) => {
if (!context.user) return params if (!context.user) return params
const [blockedUsers, blockedByUsers] = await Promise.all([ const [blockedUsers, blockedByUsers] = await Promise.all([
@ -29,16 +28,31 @@ const filterForBlockedUsers = async (params, context) => {
return params return params
} }
const maintainPinnedPosts = params => {
const pinnedPostFilter = { pinnedBy_in: { role_in: ['admin'] } }
if (isEmpty(params.filter)) {
params.filter = { OR: [pinnedPostFilter, {}] }
} else {
params.filter = { OR: [pinnedPostFilter, { ...params.filter }] }
}
return params
}
export default { export default {
Query: { Query: {
Post: async (object, params, context, resolveInfo) => { 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, false) return neo4jgraphql(object, params, context, resolveInfo, false)
}, },
findPosts: async (object, params, context, resolveInfo) => { findPosts: async (object, params, context, resolveInfo) => {
params = await filterForBlockedUsers(params, context) params = await filterForBlockedUsers(params, context)
return neo4jgraphql(object, params, context, resolveInfo, false) return neo4jgraphql(object, params, context, resolveInfo, false)
}, },
profilePagePosts: async (object, params, context, resolveInfo) => {
params = await filterForBlockedUsers(params, context)
return neo4jgraphql(object, params, context, resolveInfo, false)
},
PostsEmotionsCountByEmotion: async (object, params, context, resolveInfo) => { PostsEmotionsCountByEmotion: async (object, params, context, resolveInfo) => {
const session = context.driver.session() const session = context.driver.session()
const { postId, data } = params const { postId, data } = params
@ -115,10 +129,10 @@ 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}) let updatePostCypher = `MATCH (post:Post {id: $params.id})
SET post += $params SET post += $params
SET post.updatedAt = toString(datetime()) SET post.updatedAt = toString(datetime())
WITH post
` `
if (categoryIds && categoryIds.length) { if (categoryIds && categoryIds.length) {
@ -131,10 +145,10 @@ export default {
await session.run(cypherDeletePreviousRelations, { params }) await session.run(cypherDeletePreviousRelations, { params })
updatePostCypher += ` updatePostCypher += `
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)
WITH post
` `
} }
@ -211,10 +225,78 @@ export default {
}) })
return emoted return emoted
}, },
pinPost: async (_parent, params, context, _resolveInfo) => {
let pinnedPostWithNestedAttributes
const { driver, user } = context
const session = driver.session()
const { id: userId } = user
let writeTxResultPromise = session.writeTransaction(async transaction => {
const deletePreviousRelationsResponse = await transaction.run(
`
MATCH (:User)-[previousRelations:PINNED]->(post:Post)
REMOVE post.pinned
DELETE previousRelations
RETURN post
`,
)
return deletePreviousRelationsResponse.records.map(record => record.get('post').properties)
})
await writeTxResultPromise
writeTxResultPromise = session.writeTransaction(async transaction => {
const pinPostTransactionResponse = await transaction.run(
`
MATCH (user:User {id: $userId}) WHERE user.role = 'admin'
MATCH (post:Post {id: $params.id})
MERGE (user)-[pinned:PINNED {createdAt: toString(datetime())}]->(post)
SET post.pinned = true
RETURN post, pinned.createdAt as pinnedAt
`,
{ userId, params },
)
return pinPostTransactionResponse.records.map(record => ({
pinnedPost: record.get('post').properties,
pinnedAt: record.get('pinnedAt'),
}))
})
try {
const [transactionResult] = await writeTxResultPromise
const { pinnedPost, pinnedAt } = transactionResult
pinnedPostWithNestedAttributes = {
...pinnedPost,
pinnedAt,
}
} finally {
session.close()
}
return pinnedPostWithNestedAttributes
},
unpinPost: async (_parent, params, context, _resolveInfo) => {
let unpinnedPost
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async transaction => {
const unpinPostTransactionResponse = await transaction.run(
`
MATCH (:User)-[previousRelations:PINNED]->(post:Post {id: $params.id})
REMOVE post.pinned
DELETE previousRelations
RETURN post
`,
{ params },
)
return unpinPostTransactionResponse.records.map(record => record.get('post').properties)
})
try {
;[unpinnedPost] = await writeTxResultPromise
} finally {
session.close()
}
return unpinnedPost
},
}, },
Post: { Post: {
...Resolver('Post', { ...Resolver('Post', {
undefinedToNull: ['activityId', 'objectId', 'image', 'language'], undefinedToNull: ['activityId', 'objectId', 'image', 'language', 'pinnedAt', 'pinned'],
hasMany: { hasMany: {
tags: '-[:TAGGED]->(related:Tag)', tags: '-[:TAGGED]->(related:Tag)',
categories: '-[:CATEGORIZED]->(related:Category)', categories: '-[:CATEGORIZED]->(related:Category)',
@ -225,6 +307,7 @@ export default {
hasOne: { hasOne: {
author: '<-[:WROTE]-(related:User)', author: '<-[:WROTE]-(related:User)',
disabledBy: '<-[:DISABLED]-(related:User)', disabledBy: '<-[:DISABLED]-(related:User)',
pinnedBy: '<-[:PINNED]-(related:User)',
}, },
count: { count: {
commentsCount: commentsCount:

View File

@ -39,7 +39,8 @@ const createPostMutation = gql`
} }
` `
beforeAll(() => { beforeAll(async () => {
await factory.cleanDatabase()
const { server } = createServer({ const { server } = createServer({
context: () => { context: () => {
return { return {
@ -269,7 +270,10 @@ describe('CreatePost', () => {
}) })
it('creates a post', async () => { it('creates a post', async () => {
const expected = { data: { CreatePost: { title: 'I am a title', content: 'Some content' } } } const expected = {
data: { CreatePost: { title: 'I am a title', content: 'Some content' } },
errors: undefined,
}
await expect(mutate({ mutation: createPostMutation, variables })).resolves.toMatchObject( await expect(mutate({ mutation: createPostMutation, variables })).resolves.toMatchObject(
expected, expected,
) )
@ -285,6 +289,7 @@ describe('CreatePost', () => {
}, },
}, },
}, },
errors: undefined,
} }
await expect(mutate({ mutation: createPostMutation, variables })).resolves.toMatchObject( await expect(mutate({ mutation: createPostMutation, variables })).resolves.toMatchObject(
expected, expected,
@ -366,7 +371,12 @@ describe('UpdatePost', () => {
mutation($id: ID!, $title: String!, $content: String!, $categoryIds: [ID]) { mutation($id: ID!, $title: String!, $content: String!, $categoryIds: [ID]) {
UpdatePost(id: $id, title: $title, content: $content, categoryIds: $categoryIds) { UpdatePost(id: $id, title: $title, content: $content, categoryIds: $categoryIds) {
id id
title
content content
author {
name
slug
}
categories { categories {
id id
} }
@ -386,7 +396,6 @@ describe('UpdatePost', () => {
}) })
variables = { variables = {
...variables,
id: 'p9876', id: 'p9876',
title: 'New title', title: 'New title',
content: 'New content', content: 'New content',
@ -395,8 +404,11 @@ describe('UpdatePost', () => {
describe('unauthenticated', () => { describe('unauthenticated', () => {
it('throws authorization error', async () => { it('throws authorization error', async () => {
const { errors } = await mutate({ mutation: updatePostMutation, variables }) authenticatedUser = null
expect(errors[0]).toHaveProperty('message', 'Not Authorised!') expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorised!' }],
data: { UpdatePost: null },
})
}) })
}) })
@ -550,6 +562,399 @@ describe('UpdatePost', () => {
}) })
}) })
}) })
describe('pin posts', () => {
const pinPostMutation = gql`
mutation($id: ID!) {
pinPost(id: $id) {
id
title
content
author {
name
slug
}
pinnedBy {
id
name
role
}
createdAt
updatedAt
pinnedAt
pinned
}
}
`
beforeEach(async () => {
variables = { ...variables }
})
describe('unauthenticated', () => {
it('throws authorization error', async () => {
authenticatedUser = null
await expect(mutate({ mutation: pinPostMutation, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorised!' }],
data: { pinPost: null },
})
})
})
describe('ordinary users', () => {
it('throws authorization error', async () => {
await expect(mutate({ mutation: pinPostMutation, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorised!' }],
data: { pinPost: null },
})
})
})
describe('moderators', () => {
let moderator
beforeEach(async () => {
moderator = await user.update({ role: 'moderator', updatedAt: new Date().toISOString() })
authenticatedUser = await moderator.toJson()
})
it('throws authorization error', async () => {
await expect(mutate({ mutation: pinPostMutation, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorised!' }],
data: { pinPost: null },
})
})
})
describe('admins', () => {
let admin
beforeEach(async () => {
admin = await user.update({
role: 'admin',
name: 'Admin',
updatedAt: new Date().toISOString(),
})
authenticatedUser = await admin.toJson()
})
describe('are allowed to pin posts', () => {
beforeEach(async () => {
await factory.create('Post', {
id: 'created-and-pinned-by-same-admin',
author: admin,
})
variables = { ...variables, id: 'created-and-pinned-by-same-admin' }
})
it('responds with the updated Post', async () => {
const expected = {
data: {
pinPost: {
id: 'created-and-pinned-by-same-admin',
author: {
name: 'Admin',
},
pinnedBy: {
id: 'current-user',
name: 'Admin',
role: 'admin',
},
},
},
errors: undefined,
}
await expect(mutate({ mutation: pinPostMutation, variables })).resolves.toMatchObject(
expected,
)
})
it('sets createdAt date for PINNED', async () => {
const expected = {
data: {
pinPost: {
id: 'created-and-pinned-by-same-admin',
pinnedAt: expect.any(String),
},
},
errors: undefined,
}
await expect(mutate({ mutation: pinPostMutation, variables })).resolves.toMatchObject(
expected,
)
})
it('sets redundant `pinned` property for performant ordering', async () => {
variables = { ...variables, id: 'created-and-pinned-by-same-admin' }
const expected = {
data: { pinPost: { pinned: true } },
errors: undefined,
}
await expect(mutate({ mutation: pinPostMutation, variables })).resolves.toMatchObject(
expected,
)
})
})
describe('post created by another admin', () => {
let otherAdmin
beforeEach(async () => {
otherAdmin = await factory.create('User', {
role: 'admin',
name: 'otherAdmin',
})
authenticatedUser = await otherAdmin.toJson()
await factory.create('Post', {
id: 'created-by-one-admin-pinned-by-different-one',
author: otherAdmin,
})
})
it('responds with the updated Post', async () => {
authenticatedUser = await admin.toJson()
variables = { ...variables, id: 'created-by-one-admin-pinned-by-different-one' }
const expected = {
data: {
pinPost: {
id: 'created-by-one-admin-pinned-by-different-one',
author: {
name: 'otherAdmin',
},
pinnedBy: {
id: 'current-user',
name: 'Admin',
role: 'admin',
},
},
},
errors: undefined,
}
await expect(mutate({ mutation: pinPostMutation, variables })).resolves.toMatchObject(
expected,
)
})
})
describe('post created by another user', () => {
it('responds with the updated Post', async () => {
const expected = {
data: {
pinPost: {
id: 'p9876',
author: {
slug: 'the-author',
},
pinnedBy: {
id: 'current-user',
name: 'Admin',
role: 'admin',
},
},
},
errors: undefined,
}
await expect(mutate({ mutation: pinPostMutation, variables })).resolves.toMatchObject(
expected,
)
})
})
describe('pinned post already exists', () => {
let pinnedPost
beforeEach(async () => {
await factory.create('Post', {
id: 'only-pinned-post',
author: admin,
})
await mutate({ mutation: pinPostMutation, variables })
})
it('removes previous `pinned` attribute', async () => {
const cypher = 'MATCH (post:Post) WHERE post.pinned IS NOT NULL RETURN post'
pinnedPost = await neode.cypher(cypher)
expect(pinnedPost.records).toHaveLength(1)
variables = { ...variables, id: 'only-pinned-post' }
await mutate({ mutation: pinPostMutation, variables })
pinnedPost = await neode.cypher(cypher)
expect(pinnedPost.records).toHaveLength(1)
})
it('removes previous PINNED relationship', async () => {
variables = { ...variables, id: 'only-pinned-post' }
await mutate({ mutation: pinPostMutation, variables })
pinnedPost = await neode.cypher(
`MATCH (:User)-[pinned:PINNED]->(post:Post) RETURN post, pinned`,
)
expect(pinnedPost.records).toHaveLength(1)
})
})
describe('PostOrdering', () => {
let pinnedPost, admin
beforeEach(async () => {
;[pinnedPost] = await Promise.all([
neode.create('Post', {
id: 'im-a-pinned-post',
pinned: true,
}),
neode.create('Post', {
id: 'i-was-created-after-pinned-post',
createdAt: '2019-10-22T17:26:29.070Z', // this should always be 3rd
}),
])
admin = await user.update({
role: 'admin',
name: 'Admin',
updatedAt: new Date().toISOString(),
})
await admin.relateTo(pinnedPost, 'pinned')
})
it('pinned post appear first even when created before other posts', async () => {
const postOrderingQuery = gql`
query($orderBy: [_PostOrdering]) {
Post(orderBy: $orderBy) {
id
pinnedAt
}
}
`
const expected = {
data: {
Post: [
{
id: 'im-a-pinned-post',
pinnedAt: expect.any(String),
},
{
id: 'p9876',
pinnedAt: null,
},
{
id: 'i-was-created-after-pinned-post',
pinnedAt: null,
},
],
},
}
variables = { orderBy: ['pinned_desc', 'createdAt_desc'] }
await expect(query({ query: postOrderingQuery, variables })).resolves.toMatchObject(
expected,
)
})
})
})
})
describe('unpin posts', () => {
const unpinPostMutation = gql`
mutation($id: ID!) {
unpinPost(id: $id) {
id
title
content
author {
name
slug
}
pinnedBy {
id
name
role
}
createdAt
updatedAt
pinned
pinnedAt
}
}
`
beforeEach(async () => {
variables = { ...variables }
})
describe('unauthenticated', () => {
it('throws authorization error', async () => {
authenticatedUser = null
await expect(mutate({ mutation: unpinPostMutation, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorised!' }],
data: { unpinPost: null },
})
})
})
describe('users cannot unpin posts', () => {
it('throws authorization error', async () => {
await expect(mutate({ mutation: unpinPostMutation, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorised!' }],
data: { unpinPost: null },
})
})
})
describe('moderators cannot unpin posts', () => {
let moderator
beforeEach(async () => {
moderator = await user.update({ role: 'moderator', updatedAt: new Date().toISOString() })
authenticatedUser = await moderator.toJson()
})
it('throws authorization error', async () => {
await expect(mutate({ mutation: unpinPostMutation, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorised!' }],
data: { unpinPost: null },
})
})
})
describe('admin can unpin posts', () => {
let admin, pinnedPost
beforeEach(async () => {
pinnedPost = await factory.create('Post', { id: 'post-to-be-unpinned' })
admin = await user.update({
role: 'admin',
name: 'Admin',
updatedAt: new Date().toISOString(),
})
authenticatedUser = await admin.toJson()
await admin.relateTo(pinnedPost, 'pinned', { createdAt: new Date().toISOString() })
variables = { ...variables, id: 'post-to-be-unpinned' }
})
it('responds with the unpinned Post', async () => {
authenticatedUser = await admin.toJson()
const expected = {
data: {
unpinPost: {
id: 'post-to-be-unpinned',
pinnedBy: null,
pinnedAt: null,
},
},
errors: undefined,
}
await expect(mutate({ mutation: unpinPostMutation, variables })).resolves.toMatchObject(
expected,
)
})
it('unsets `pinned` property', async () => {
const expected = {
data: {
unpinPost: {
id: 'post-to-be-unpinned',
pinned: null,
},
},
errors: undefined,
}
await expect(mutate({ mutation: unpinPostMutation, variables })).resolves.toMatchObject(
expected,
)
})
})
})
}) })
describe('DeletePost', () => { describe('DeletePost', () => {

View File

@ -381,6 +381,7 @@ describe('SignupVerification', () => {
$nonce: String! $nonce: String!
$about: String $about: String
$termsAndConditionsAgreedVersion: String! $termsAndConditionsAgreedVersion: String!
$locale: String
) { ) {
SignupVerification( SignupVerification(
name: $name name: $name
@ -389,6 +390,7 @@ describe('SignupVerification', () => {
nonce: $nonce nonce: $nonce
about: $about about: $about
termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
locale: $locale
) { ) {
id id
termsAndConditionsAgreedVersion termsAndConditionsAgreedVersion
@ -405,6 +407,7 @@ describe('SignupVerification', () => {
password: '123', password: '123',
email: 'john@example.org', email: 'john@example.org',
termsAndConditionsAgreedVersion: '0.1.0', termsAndConditionsAgreedVersion: '0.1.0',
locale: 'en',
} }
}) })

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ export default {
const transactionRes = await session.run( const transactionRes = await session.run(
`MATCH (node {id: $id})<-[:WROTE]-(userWritten:User), (user:User {id: $userId}) `MATCH (node {id: $id})<-[:WROTE]-(userWritten:User), (user:User {id: $userId})
WHERE $type IN labels(node) AND NOT userWritten.id = $userId WHERE $type IN labels(node) AND NOT userWritten.id = $userId
MERGE (user)-[relation:SHOUTED]->(node) MERGE (user)-[relation:SHOUTED{createdAt:toString(datetime())}]->(node)
RETURN COUNT(relation) > 0 as isShouted`, RETURN COUNT(relation) > 0 as isShouted`,
{ {
id, id,

View File

@ -102,6 +102,22 @@ describe('shout and unshout posts', () => {
}) })
}) })
it('adds `createdAt` to `SHOUT` relationship', async () => {
variables = { id: 'another-user-post-id' }
await mutate({ mutation: mutationShoutPost, variables })
const relation = await instance.cypher(
'MATCH (user:User {id: $userId1})-[relationship:SHOUTED]->(node {id: $userId2}) WHERE relationship.createdAt IS NOT NULL RETURN relationship',
{
userId1: 'current-user-id',
userId2: 'another-user-post-id',
},
)
const relationshipProperties = relation.records.map(
record => record.get('relationship').properties.createdAt,
)
expect(relationshipProperties[0]).toEqual(expect.any(String))
})
it('can not shout my own post', async () => { it('can not shout my own post', async () => {
variables = { id: 'current-user-post-id' } variables = { id: 'current-user-post-id' }
await expect(mutate({ mutation: mutationShoutPost, variables })).resolves.toMatchObject({ await expect(mutate({ mutation: mutationShoutPost, variables })).resolves.toMatchObject({

View File

@ -19,6 +19,7 @@ type Mutation {
locationName: String locationName: String
about: String about: String
termsAndConditionsAgreedVersion: String! termsAndConditionsAgreedVersion: String!
locale: String
): User ): User
AddEmailAddress(email: String!): EmailAddress AddEmailAddress(email: String!): EmailAddress
VerifyEmailAddress( VerifyEmailAddress(

View File

@ -1,3 +1,37 @@
enum _PostOrdering {
id_asc
id_desc
activityId_asc
activityId_desc
objectId_asc
objectId_desc
title_asc
title_desc
slug_asc
slug_desc
content_asc
content_desc
contentExcerpt_asc
contentExcerpt_desc
image_asc
image_desc
visibility_asc
visibility_desc
deleted_asc
deleted_desc
disabled_asc
disabled_desc
createdAt_asc
createdAt_desc
updatedAt_asc
updatedAt_desc
language_asc
language_desc
pinned_asc
pinned_desc
}
type Post { type Post {
id: ID! id: ID!
activityId: String activityId: String
@ -12,10 +46,15 @@ type Post {
visibility: Visibility visibility: Visibility
deleted: Boolean deleted: Boolean
disabled: Boolean disabled: Boolean
pinned: Boolean
disabledBy: User @relation(name: "DISABLED", direction: "IN") disabledBy: User @relation(name: "DISABLED", direction: "IN")
createdAt: String createdAt: String
updatedAt: String updatedAt: String
language: String language: String
pinnedAt: String @cypher(
statement: "MATCH (this)<-[pinned:PINNED]-(:User) WHERE NOT this.deleted = true AND NOT this.disabled = true RETURN pinned.createdAt"
)
pinnedBy: User @relation(name:"PINNED", direction: "IN")
relatedContributions: [Post]! relatedContributions: [Post]!
@cypher( @cypher(
statement: """ statement: """
@ -40,7 +79,7 @@ type Post {
@cypher( @cypher(
statement: "MATCH (this)<-[:SHOUTED]-(r:User) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)" statement: "MATCH (this)<-[:SHOUTED]-(r:User) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)"
) )
# Has the currently logged in user shouted that post? # Has the currently logged in user shouted that post?
shoutedByCurrentUser: Boolean! shoutedByCurrentUser: Boolean!
@cypher( @cypher(
@ -84,9 +123,12 @@ type Mutation {
DeletePost(id: ID!): Post DeletePost(id: ID!): Post
AddPostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED AddPostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED
RemovePostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED RemovePostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED
pinPost(id: ID!): Post
unpinPost(id: ID!): Post
} }
type Query { type Query {
PostsEmotionsCountByEmotion(postId: ID!, data: _EMOTEDInput!): Int! PostsEmotionsCountByEmotion(postId: ID!, data: _EMOTEDInput!): Int!
PostsEmotionsByCurrentUser(postId: ID!): [String] PostsEmotionsByCurrentUser(postId: ID!): [String]
profilePagePosts(filter: _PostFilter, first: Int, offset: Int, orderBy: [_PostOrdering]): [Post]
} }

View File

@ -1,179 +1,181 @@
type User { type User {
id: ID! id: ID!
actorId: String actorId: String
name: String name: String
email: String! @cypher(statement: "MATCH (this)-[: PRIMARY_EMAIL]->(e: EmailAddress) RETURN e.email") email: String! @cypher(statement: "MATCH (this)-[: PRIMARY_EMAIL]->(e: EmailAddress) RETURN e.email")
slug: String! slug: String!
avatar: String avatar: String
coverImg: String coverImg: String
deleted: Boolean deleted: Boolean
disabled: Boolean disabled: Boolean
disabledBy: User @relation(name: "DISABLED", direction: "IN") disabledBy: User @relation(name: "DISABLED", direction: "IN")
role: UserGroup! role: UserGroup!
publicKey: String publicKey: String
invitedBy: User @relation(name: "INVITED", direction: "IN") invitedBy: User @relation(name: "INVITED", direction: "IN")
invited: [User] @relation(name: "INVITED", direction: "OUT") invited: [User] @relation(name: "INVITED", direction: "OUT")
location: Location @cypher(statement: "MATCH (this)-[: IS_IN]->(l: Location) RETURN l") location: Location @cypher(statement: "MATCH (this)-[: IS_IN]->(l: Location) RETURN l")
locationName: String locationName: String
about: String about: String
socialMedia: [SocialMedia]! @relation(name: "OWNED_BY", direction: "IN") socialMedia: [SocialMedia]! @relation(name: "OWNED_BY", direction: "IN")
# createdAt: DateTime # createdAt: DateTime
# updatedAt: DateTime # updatedAt: DateTime
createdAt: String createdAt: String
updatedAt: String updatedAt: String
termsAndConditionsAgreedVersion: String termsAndConditionsAgreedVersion: String
termsAndConditionsAgreedAt: String termsAndConditionsAgreedAt: String
allowEmbedIframes: Boolean allowEmbedIframes: Boolean
locale: String
friends: [User]! @relation(name: "FRIENDS", direction: "BOTH")
friendsCount: Int! @cypher(statement: "MATCH (this)<-[: FRIENDS]->(r: User) RETURN COUNT(DISTINCT r)")
friends: [User]! @relation(name: "FRIENDS", direction: "BOTH") following: [User]! @relation(name: "FOLLOWS", direction: "OUT")
friendsCount: Int! @cypher(statement: "MATCH (this)<-[: FRIENDS]->(r: User) RETURN COUNT(DISTINCT r)") followingCount: Int! @cypher(statement: "MATCH (this)-[: FOLLOWS]->(r: User) RETURN COUNT(DISTINCT r)")
following: [User]! @relation(name: "FOLLOWS", direction: "OUT") followedBy: [User]! @relation(name: "FOLLOWS", direction: "IN")
followingCount: Int! @cypher(statement: "MATCH (this)-[: FOLLOWS]->(r: User) RETURN COUNT(DISTINCT r)") followedByCount: Int! @cypher(statement: "MATCH (this)<-[: FOLLOWS]-(r: User) RETURN COUNT(DISTINCT r)")
followedBy: [User]! @relation(name: "FOLLOWS", direction: "IN") # Is the currently logged in user following that user?
followedByCount: Int! @cypher(statement: "MATCH (this)<-[: FOLLOWS]-(r: User) RETURN COUNT(DISTINCT r)") followedByCurrentUser: Boolean! @cypher(
statement: """
MATCH (this)<-[: FOLLOWS]-(u: User { id: $cypherParams.currentUserId})
RETURN COUNT(u) >= 1
"""
)
isBlocked: Boolean! @cypher(
statement: """
MATCH (this)<-[: BLOCKED]-(u: User { id: $cypherParams.currentUserId})
RETURN COUNT(u) >= 1
"""
)
# Is the currently logged in user following that user? # contributions: [WrittenPost]!
followedByCurrentUser: Boolean! @cypher( # contributions2(first: Int = 10, offset: Int = 0): [WrittenPost2]!
statement: """ # @cypher(
MATCH (this)<-[: FOLLOWS]-(u: User { id: $cypherParams.currentUserId}) # statement: "MATCH (this)-[w:WROTE]->(p:Post) RETURN p as Post, w.timestamp as timestamp"
RETURN COUNT(u) >= 1 # )
""" contributions: [Post]! @relation(name: "WROTE", direction: "OUT")
) contributionsCount: Int! @cypher(
isBlocked: Boolean! @cypher( statement: """
statement: """ MATCH (this)-[: WROTE]->(r: Post)
MATCH (this)<-[: BLOCKED]-(u: User { id: $cypherParams.currentUserId}) WHERE NOT r.deleted = true AND NOT r.disabled = true
RETURN COUNT(u) >= 1 RETURN COUNT(r)
""" """
) )
# contributions: [WrittenPost]! comments: [Comment]! @relation(name: "WROTE", direction: "OUT")
# contributions2(first: Int = 10, offset: Int = 0): [WrittenPost2]! commentedCount: Int! @cypher(statement: "MATCH (this)-[: WROTE]->(: Comment)-[: COMMENTS]->(p: Post) WHERE NOT p.deleted = true AND NOT p.disabled = true RETURN COUNT(DISTINCT(p))")
# @cypher(
# statement: "MATCH (this)-[w:WROTE]->(p:Post) RETURN p as Post, w.timestamp as timestamp"
# )
contributions: [Post]! @relation(name: "WROTE", direction: "OUT")
contributionsCount: Int! @cypher(
statement: """
MATCH (this)-[: WROTE]->(r: Post)
WHERE NOT r.deleted = true AND NOT r.disabled = true
RETURN COUNT(r)
"""
)
comments: [Comment]! @relation(name: "WROTE", direction: "OUT") shouted: [Post]! @relation(name: "SHOUTED", direction: "OUT")
commentedCount: Int! @cypher(statement: "MATCH (this)-[: WROTE]->(: Comment)-[: COMMENTS]->(p: Post) WHERE NOT p.deleted = true AND NOT p.disabled = true RETURN COUNT(DISTINCT(p))") shoutedCount: Int! @cypher(statement: "MATCH (this)-[: SHOUTED]->(r: Post) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)")
shouted: [Post]! @relation(name: "SHOUTED", direction: "OUT") categories: [Category]! @relation(name: "CATEGORIZED", direction: "OUT")
shoutedCount: Int! @cypher(statement: "MATCH (this)-[: SHOUTED]->(r: Post) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)")
categories: [Category]! @relation(name: "CATEGORIZED", direction: "OUT") badges: [Badge]! @relation(name: "REWARDED", direction: "IN")
badgesCount: Int! @cypher(statement: "MATCH (this)<-[: REWARDED]-(r: Badge) RETURN COUNT(r)")
badges: [Badge]! @relation(name: "REWARDED", direction: "IN") emotions: [EMOTED]
badgesCount: Int! @cypher(statement: "MATCH (this)<-[: REWARDED]-(r: Badge) RETURN COUNT(r)")
emotions: [EMOTED]
} }
input _UserFilter { input _UserFilter {
AND: [_UserFilter!] AND: [_UserFilter!]
OR: [_UserFilter!] OR: [_UserFilter!]
name_contains: String name_contains: String
about_contains: String about_contains: String
slug_contains: String slug_contains: String
id: ID id: ID
id_not: ID id_not: ID
id_in: [ID!] id_in: [ID!]
id_not_in: [ID!] id_not_in: [ID!]
id_contains: ID id_contains: ID
id_not_contains: ID id_not_contains: ID
id_starts_with: ID id_starts_with: ID
id_not_starts_with: ID id_not_starts_with: ID
id_ends_with: ID id_ends_with: ID
id_not_ends_with: ID id_not_ends_with: ID
friends: _UserFilter friends: _UserFilter
friends_not: _UserFilter friends_not: _UserFilter
friends_in: [_UserFilter!] friends_in: [_UserFilter!]
friends_not_in: [_UserFilter!] friends_not_in: [_UserFilter!]
friends_some: _UserFilter friends_some: _UserFilter
friends_none: _UserFilter friends_none: _UserFilter
friends_single: _UserFilter friends_single: _UserFilter
friends_every: _UserFilter friends_every: _UserFilter
following: _UserFilter following: _UserFilter
following_not: _UserFilter following_not: _UserFilter
following_in: [_UserFilter!] following_in: [_UserFilter!]
following_not_in: [_UserFilter!] following_not_in: [_UserFilter!]
following_some: _UserFilter following_some: _UserFilter
following_none: _UserFilter following_none: _UserFilter
following_single: _UserFilter following_single: _UserFilter
following_every: _UserFilter following_every: _UserFilter
followedBy: _UserFilter followedBy: _UserFilter
followedBy_not: _UserFilter followedBy_not: _UserFilter
followedBy_in: [_UserFilter!] followedBy_in: [_UserFilter!]
followedBy_not_in: [_UserFilter!] followedBy_not_in: [_UserFilter!]
followedBy_some: _UserFilter followedBy_some: _UserFilter
followedBy_none: _UserFilter followedBy_none: _UserFilter
followedBy_single: _UserFilter followedBy_single: _UserFilter
followedBy_every: _UserFilter followedBy_every: _UserFilter
role_in: [UserGroup!]
} }
type Query { type Query {
User( User(
id: ID id: ID
email: String email: String
actorId: String actorId: String
name: String name: String
slug: String slug: String
avatar: String avatar: String
coverImg: String coverImg: String
role: UserGroup role: UserGroup
locationName: String locationName: String
about: String about: String
createdAt: String createdAt: String
updatedAt: String updatedAt: String
friendsCount: Int friendsCount: Int
followingCount: Int followingCount: Int
followedByCount: Int followedByCount: Int
followedByCurrentUser: Boolean followedByCurrentUser: Boolean
contributionsCount: Int contributionsCount: Int
commentedCount: Int commentedCount: Int
shoutedCount: Int shoutedCount: Int
badgesCount: Int badgesCount: Int
first: Int first: Int
offset: Int offset: Int
orderBy: [_UserOrdering] orderBy: [_UserOrdering]
filter: _UserFilter filter: _UserFilter
): [User] ): [User]
blockedUsers: [User] blockedUsers: [User]
currentUser: User currentUser: User
} }
type Mutation { type Mutation {
UpdateUser ( UpdateUser (
id: ID! id: ID!
name: String name: String
email: String email: String
slug: String slug: String
avatar: String avatar: String
coverImg: String coverImg: String
avatarUpload: Upload avatarUpload: Upload
locationName: String locationName: String
about: String about: String
termsAndConditionsAgreedVersion: String termsAndConditionsAgreedVersion: String
termsAndConditionsAgreedAt: String termsAndConditionsAgreedAt: String
allowEmbedIframes: Boolean allowEmbedIframes: Boolean
): User locale: String
): User
DeleteUser(id: ID!, resource: [Deletable]): User DeleteUser(id: ID!, resource: [Deletable]): User
block(id: ID!): User block(id: ID!): User
unblock(id: ID!): User unblock(id: ID!): User
} }

View File

@ -17,6 +17,7 @@ export default function create() {
termsAndConditionsAgreedVersion: '0.0.1', termsAndConditionsAgreedVersion: '0.0.1',
termsAndConditionsAgreedAt: '2019-08-01T10:47:19.212Z', termsAndConditionsAgreedAt: '2019-08-01T10:47:19.212Z',
allowEmbedIframes: false, allowEmbedIframes: false,
locale: 'en',
} }
defaults.slug = slugify(defaults.name, { lower: true }) defaults.slug = slugify(defaults.name, { lower: true })
args = { args = {

View File

@ -963,10 +963,10 @@
url-regex "~4.1.1" url-regex "~4.1.1"
video-extensions "~1.1.0" video-extensions "~1.1.0"
"@metascraper/helpers@^5.7.6": "@metascraper/helpers@^5.7.14", "@metascraper/helpers@^5.7.17":
version "5.7.6" version "5.7.17"
resolved "https://registry.yarnpkg.com/@metascraper/helpers/-/helpers-5.7.6.tgz#84007215d3b31525995fd85cf0d28bf6a12bf7bb" resolved "https://registry.yarnpkg.com/@metascraper/helpers/-/helpers-5.7.17.tgz#401897c7239090ca7149b83e581712845bbb3709"
integrity sha512-AD2VTQmMWl/KCUXl9h0fP84VacoiTI/8y8CBgErmYZnm+sliKGedQrDZO3JmzNg73Z5z08GQTjME1WHIDiIQDw== integrity sha512-t21LqfDpaIrWg2JaivXG6mVzUsIVW05cAsKySA5Tj9Hgi9oZXxaaNes5XipOzk6P242RI48SDo7CkSbYiio7Tw==
dependencies: dependencies:
audio-extensions "0.0.0" audio-extensions "0.0.0"
chrono-node "~1.3.11" chrono-node "~1.3.11"
@ -980,7 +980,7 @@
iso-639-3 "~1.2.0" iso-639-3 "~1.2.0"
isostring "0.0.1" isostring "0.0.1"
lodash "~4.17.15" lodash "~4.17.15"
mem "~5.1.1" memoize-one "~5.1.1"
mime-types "~2.1.24" mime-types "~2.1.24"
normalize-url "~4.5.0" normalize-url "~4.5.0"
smartquotes "~2.3.1" smartquotes "~2.3.1"
@ -1042,60 +1042,60 @@
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=
"@sentry/core@5.7.0": "@sentry/core@5.7.1":
version "5.7.0" version "5.7.1"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.7.0.tgz#c2aa5341e703ec7cf2acc69e51971a0b1f7d102a" resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.7.1.tgz#3eb2b7662cac68245931ee939ec809bf7a639d0e"
integrity sha512-gQel0d7LBSWJGHc7gfZllYAu+RRGD9GcYGmkRfemurmDyDGQDf/sfjiBi8f9QxUc2iFTHnvIR5nMTyf0U3yl3Q== integrity sha512-AOn3k3uVWh2VyajcHbV9Ta4ieDIeLckfo7UMLM+CTk2kt7C89SayDGayJMSsIrsZlL4qxBoLB9QY4W2FgAGJrg==
dependencies: dependencies:
"@sentry/hub" "5.7.0" "@sentry/hub" "5.7.1"
"@sentry/minimal" "5.7.0" "@sentry/minimal" "5.7.1"
"@sentry/types" "5.7.0" "@sentry/types" "5.7.1"
"@sentry/utils" "5.7.0" "@sentry/utils" "5.7.1"
tslib "^1.9.3" tslib "^1.9.3"
"@sentry/hub@5.7.0": "@sentry/hub@5.7.1":
version "5.7.0" version "5.7.1"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.7.0.tgz#f7c356202a9db1daae82ce7f48ebf1139e4e9d02" resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.7.1.tgz#a52acd9fead7f3779d96e9965c6978aecc8b9cad"
integrity sha512-qNdYheJ6j4P9Sk0eqIINpJohImmu/+trCwFb4F8BGLQth5iGMVQD6D0YUrgjf4ZaQwfhw9tv4W6VEfF5tyASoA== integrity sha512-evGh323WR073WSBCg/RkhlUmCQyzU0xzBzCZPscvcoy5hd4SsLE6t9Zin+WACHB9JFsRQIDwNDn+D+pj3yKsig==
dependencies: dependencies:
"@sentry/types" "5.7.0" "@sentry/types" "5.7.1"
"@sentry/utils" "5.7.0" "@sentry/utils" "5.7.1"
tslib "^1.9.3" tslib "^1.9.3"
"@sentry/minimal@5.7.0": "@sentry/minimal@5.7.1":
version "5.7.0" version "5.7.1"
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.7.0.tgz#832d26bcd862c6ea628d48ad199ac7f966a2d907" resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.7.1.tgz#56afc537737586929e25349765e37a367958c1e1"
integrity sha512-0sizE2prS9nmfLyVUKmVzFFFqRNr9iorSCCejwnlRe3crqKqjf84tuRSzm6NkZjIyYj9djuuo9l9XN12NLQ/4A== integrity sha512-nS/Dg+jWAZtcxQW8wKbkkw4dYvF6uyY/vDiz/jFCaux0LX0uhgXAC9gMOJmgJ/tYBLJ64l0ca5LzpZa7BMJQ0g==
dependencies: dependencies:
"@sentry/hub" "5.7.0" "@sentry/hub" "5.7.1"
"@sentry/types" "5.7.0" "@sentry/types" "5.7.1"
tslib "^1.9.3" tslib "^1.9.3"
"@sentry/node@^5.7.0": "@sentry/node@^5.7.1":
version "5.7.0" version "5.7.1"
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.7.0.tgz#153777f06b2fcd346edbff9adbb6b231c7e5fa0a" resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.7.1.tgz#94e2fbac94f6cc061be3bc14b22813536c59698d"
integrity sha512-iqQbGAJDBlpQkp1rl9RkDCIfnukr4cOtHPgJPmLY19m/KXIHD2cdKhvbqoCvIPBTIAeSGQIvDT9jD5zT46eoqQ== integrity sha512-hVM10asFStrOhYZzMqFM7V1lrHkr1ydc2n/SFG0ZmIQxfTjCVElyXV/BJASIdqadM1fFIvvtD/EfgkTcZmub1g==
dependencies: dependencies:
"@sentry/core" "5.7.0" "@sentry/core" "5.7.1"
"@sentry/hub" "5.7.0" "@sentry/hub" "5.7.1"
"@sentry/types" "5.7.0" "@sentry/types" "5.7.1"
"@sentry/utils" "5.7.0" "@sentry/utils" "5.7.1"
cookie "^0.3.1" cookie "^0.3.1"
https-proxy-agent "^3.0.0" https-proxy-agent "^3.0.0"
lru_map "^0.3.3" lru_map "^0.3.3"
tslib "^1.9.3" tslib "^1.9.3"
"@sentry/types@5.7.0": "@sentry/types@5.7.1":
version "5.7.0" version "5.7.1"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.7.0.tgz#e8677e57b40c2c63cad42c02add12b238e647c10" resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.7.1.tgz#4c4c1d4d891b6b8c2c3c7b367d306a8b1350f090"
integrity sha512-bFRVortg713dE2yJXNFgNe6sNBVVSkpoELLkGPatdVQi0dYc6OggIIX4UZZvkynFx72GwYqO1NOrtUcJY2gmMg== integrity sha512-tbUnTYlSliXvnou5D4C8Zr+7/wJrHLbpYX1YkLXuIJRU0NSi81bHMroAuHWILcQKWhVjaV/HZzr7Y/hhWtbXVQ==
"@sentry/utils@5.7.0": "@sentry/utils@5.7.1":
version "5.7.0" version "5.7.1"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.7.0.tgz#a6850aa4f5476fa26517cd5c6248f871d8d9939b" resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.7.1.tgz#cf37ad55f78e317665cd8680f202d307fa77f1d0"
integrity sha512-XmwQpLqea9mj8x1N7P/l4JvnEb0Rn5Py5OtBgl0ctk090W+GB1uM8rl9mkMf6698o1s1Z8T/tI/QY0yFA5uZXg== integrity sha512-nhirUKj/qFLsR1i9kJ5BRvNyzdx/E2vorIsukuDrbo8e3iZ11JMgCOVrmC8Eq9YkHBqgwX4UnrPumjFyvGMZ2Q==
dependencies: dependencies:
"@sentry/types" "5.7.0" "@sentry/types" "5.7.1"
tslib "^1.9.3" tslib "^1.9.3"
"@sindresorhus/is@^0.14.0": "@sindresorhus/is@^0.14.0":
@ -1351,32 +1351,35 @@
dependencies: dependencies:
"@types/yargs-parser" "*" "@types/yargs-parser" "*"
"@types/yup@0.26.23": "@types/yup@0.26.24":
version "0.26.23" version "0.26.24"
resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.26.23.tgz#00721a3b675e7609e5bcccb94234e86b754bcd04" resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.26.24.tgz#c24170b3a5c867b4fabd49fcc42fe45f780cb153"
integrity sha512-+tipAL6prdInS/avA6QityIFBDvHnqk1Tv9L5JMEws5IZC6agymBGAoDsrPyYp42wGcktyQtYKv9kvGPEKd4Qg== integrity sha512-x0bhHnYjH5mZit4HivUYbTMO4LouOTGwp/LLxSL1mbJYVwNJtHYESH0ed2bwM1lkI2yDmsoCDYJnWEgHeJDACg==
"@types/zen-observable@^0.8.0": "@types/zen-observable@^0.8.0":
version "0.8.0" version "0.8.0"
resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d" resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d"
integrity sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg== integrity sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg==
"@typescript-eslint/experimental-utils@^1.13.0": "@typescript-eslint/experimental-utils@^2.5.0":
version "1.13.0" version "2.6.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz#b08c60d780c0067de2fb44b04b432f540138301e" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.6.0.tgz#ed70bef72822bff54031ff0615fc888b9e2b6e8a"
integrity sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg== integrity sha512-34BAFpNOwHXeqT+AvdalLxOvcPYnCxA5JGmBAFL64RGMdP0u65rXjii7l/nwpgk5aLEE1LaqF+SsCU0/Cb64xA==
dependencies: dependencies:
"@types/json-schema" "^7.0.3" "@types/json-schema" "^7.0.3"
"@typescript-eslint/typescript-estree" "1.13.0" "@typescript-eslint/typescript-estree" "2.6.0"
eslint-scope "^4.0.0" eslint-scope "^5.0.0"
"@typescript-eslint/typescript-estree@1.13.0": "@typescript-eslint/typescript-estree@2.6.0":
version "1.13.0" version "2.6.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz#8140f17d0f60c03619798f1d628b8434913dc32e" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.6.0.tgz#d3e9d8e001492e2b9124c4d4bd4e7f03c0fd7254"
integrity sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw== integrity sha512-A3lSBVIdj2Gp0lFEL6in2eSPqJ33uAc3Ko+Y4brhjkxzjbzLnwBH22CwsW2sCo+iwogfIyvb56/AJri15H0u5Q==
dependencies: dependencies:
debug "^4.1.1"
glob "^7.1.4"
is-glob "^4.0.1"
lodash.unescape "4.0.1" lodash.unescape "4.0.1"
semver "5.5.0" semver "^6.3.0"
"@wry/context@^0.4.0": "@wry/context@^0.4.0":
version "0.4.4" version "0.4.4"
@ -1419,10 +1422,10 @@ acorn-globals@^4.1.0:
acorn "^6.0.1" acorn "^6.0.1"
acorn-walk "^6.0.1" acorn-walk "^6.0.1"
acorn-jsx@^5.0.2: acorn-jsx@^5.1.0:
version "5.0.2" version "5.1.0"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.2.tgz#84b68ea44b373c4f8686023a551f61a21b7c4a4f" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384"
integrity sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw== integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==
acorn-walk@^6.0.1: acorn-walk@^6.0.1:
version "6.2.0" version "6.2.0"
@ -1439,10 +1442,10 @@ acorn@^6.0.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e"
integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA== integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==
acorn@^7.0.0: acorn@^7.1.0:
version "7.0.0" version "7.1.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.0.0.tgz#26b8d1cd9a9b700350b71c0905546f64d1284e7a" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c"
integrity sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ== integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==
agent-base@^4.3.0: agent-base@^4.3.0:
version "4.3.0" version "4.3.0"
@ -1660,10 +1663,10 @@ apollo-server-caching@^0.5.0:
dependencies: dependencies:
lru-cache "^5.0.0" lru-cache "^5.0.0"
apollo-server-core@^2.9.6: apollo-server-core@^2.9.7:
version "2.9.6" version "2.9.7"
resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.9.6.tgz#b6dc087200633f47ca4f08244d3e606b4d616320" resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.9.7.tgz#0f32344af90dec445ac780be95350bfa736fc416"
integrity sha512-2tHAWQxP7HrETI/BZvg2fem6YlahF9HUp4Y6SSL95WP3uNMOJBlN12yM1y+O2u5K5e4jwdPNaLjoL2A/26XrLw== integrity sha512-EqKyROy+21sM93YHjGpy6wlnzK/vH0fnZh7RCf3uB69aQ3OjgdP4AQ5oWRQ62NDN+aoic7OLhChSDJeDonq/NQ==
dependencies: dependencies:
"@apollographql/apollo-tools" "^0.4.0" "@apollographql/apollo-tools" "^0.4.0"
"@apollographql/graphql-playground-html" "1.6.24" "@apollographql/graphql-playground-html" "1.6.24"
@ -1674,7 +1677,7 @@ apollo-server-core@^2.9.6:
apollo-engine-reporting "^1.4.7" apollo-engine-reporting "^1.4.7"
apollo-server-caching "^0.5.0" apollo-server-caching "^0.5.0"
apollo-server-env "^2.4.3" apollo-server-env "^2.4.3"
apollo-server-errors "^2.3.3" apollo-server-errors "^2.3.4"
apollo-server-plugin-base "^0.6.5" apollo-server-plugin-base "^0.6.5"
apollo-server-types "^0.2.5" apollo-server-types "^0.2.5"
apollo-tracing "^0.8.5" apollo-tracing "^0.8.5"
@ -1695,15 +1698,15 @@ apollo-server-env@^2.4.3:
node-fetch "^2.1.2" node-fetch "^2.1.2"
util.promisify "^1.0.0" util.promisify "^1.0.0"
apollo-server-errors@^2.3.3: apollo-server-errors@^2.3.4:
version "2.3.3" version "2.3.4"
resolved "https://registry.yarnpkg.com/apollo-server-errors/-/apollo-server-errors-2.3.3.tgz#83763b00352c10dc68fbb0d41744ade66de549ff" resolved "https://registry.yarnpkg.com/apollo-server-errors/-/apollo-server-errors-2.3.4.tgz#b70ef01322f616cbcd876f3e0168a1a86b82db34"
integrity sha512-MO4oJ129vuCcbqwr5ZwgxqGGiLz3hCyowz0bstUF7MR+vNGe4oe3DWajC9lv4CxrhcqUHQOeOPViOdIo1IxE3g== integrity sha512-Y0PKQvkrb2Kd18d1NPlHdSqmlr8TgqJ7JQcNIfhNDgdb45CnqZlxL1abuIRhr8tiw8OhVOcFxz2KyglBi8TKdA==
apollo-server-express@^2.9.6: apollo-server-express@^2.9.7:
version "2.9.6" version "2.9.7"
resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-2.9.6.tgz#eec2ec43b829b059278e14994d06bd23e43266f9" resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-2.9.7.tgz#54fbaf93b68f0123ecb1dead26cbfda5b15bd10e"
integrity sha512-j80azBeXvLvyZsbqCnus7GH+w8vk+2IOnYzROZu/f0D2roDZtsu1XZkn+aplDJZXMcEXtqB6t4qNpyvV4zY0XQ== integrity sha512-+DuJk1oq34Zx0bLYzgBgJH/eXS0JNxw2JycHQvV0+PAQ0Qi01oomJRA2r1S5isnfnSAnHb2E9jyBTptoHdw3MQ==
dependencies: dependencies:
"@apollographql/graphql-playground-html" "1.6.24" "@apollographql/graphql-playground-html" "1.6.24"
"@types/accepts" "^1.3.5" "@types/accepts" "^1.3.5"
@ -1711,7 +1714,7 @@ apollo-server-express@^2.9.6:
"@types/cors" "^2.8.4" "@types/cors" "^2.8.4"
"@types/express" "4.17.1" "@types/express" "4.17.1"
accepts "^1.3.5" accepts "^1.3.5"
apollo-server-core "^2.9.6" apollo-server-core "^2.9.7"
apollo-server-types "^0.2.5" apollo-server-types "^0.2.5"
body-parser "^1.18.3" body-parser "^1.18.3"
cors "^2.8.4" cors "^2.8.4"
@ -1729,12 +1732,12 @@ apollo-server-plugin-base@^0.6.5:
dependencies: dependencies:
apollo-server-types "^0.2.5" apollo-server-types "^0.2.5"
apollo-server-testing@~2.9.6: apollo-server-testing@~2.9.7:
version "2.9.6" version "2.9.7"
resolved "https://registry.yarnpkg.com/apollo-server-testing/-/apollo-server-testing-2.9.6.tgz#1cae51c93a8865b85e877e2c9927964cf32625e6" resolved "https://registry.yarnpkg.com/apollo-server-testing/-/apollo-server-testing-2.9.7.tgz#8d05058ddda4a715fac2fefb2b8e973e409a7672"
integrity sha512-pbURQD5VjNFk4GMVVxyCds9rY4/NIqjvjE4tyf1k89RHwMdk+zuVggt/DGudteorZtqAqtsOIHWojMBU4s2klA== integrity sha512-yy18ceSyX2a9UYcs6X7K0xFZwcS1riEh99zdWU0XB/yzzTIdGZkFYeJmV/zjpGL3CFyXF7Va/muo6otl4nDOsA==
dependencies: dependencies:
apollo-server-core "^2.9.6" apollo-server-core "^2.9.7"
apollo-server-types@^0.2.5: apollo-server-types@^0.2.5:
version "0.2.5" version "0.2.5"
@ -1745,13 +1748,13 @@ apollo-server-types@^0.2.5:
apollo-server-caching "^0.5.0" apollo-server-caching "^0.5.0"
apollo-server-env "^2.4.3" apollo-server-env "^2.4.3"
apollo-server@~2.9.6: apollo-server@~2.9.7:
version "2.9.6" version "2.9.7"
resolved "https://registry.yarnpkg.com/apollo-server/-/apollo-server-2.9.6.tgz#11b6f1128ddb674d2651bb289e0c0fc28aa18653" resolved "https://registry.yarnpkg.com/apollo-server/-/apollo-server-2.9.7.tgz#aab337b75c04ddea0fa9b171b30c4e91932c04d8"
integrity sha512-sDvrGpMQsTGQ9FTkFm3xracrSUi8nFoh3svlD98pe6qb75UDDrXAZgxwQCSOwZ3BkaJ7UkdndfhnruhFstTeMw== integrity sha512-maGGCsK4Ft5ucox5ZJf6oaKhgPvzHY3jXWbA1F/mn0/EYX8e1RVO3Qtj8aQQ0/vCKx8r4vYgj+ctqBVaN/nr4A==
dependencies: dependencies:
apollo-server-core "^2.9.6" apollo-server-core "^2.9.7"
apollo-server-express "^2.9.6" apollo-server-express "^2.9.7"
express "^4.0.0" express "^4.0.0"
graphql-subscriptions "^1.0.0" graphql-subscriptions "^1.0.0"
graphql-tools "^4.0.0" graphql-tools "^4.0.0"
@ -2101,10 +2104,10 @@ boolbase@~1.0.0:
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
bowser@^2.6.1: bowser@^2.7.0:
version "2.6.1" version "2.7.0"
resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.6.1.tgz#196599588af6f0413449c79ab3bf7a5a1bb3384f" resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.7.0.tgz#96eab1fa07fab08c1ec4c75977a7c8ddf8e0fe1f"
integrity sha512-hySGUuLhi0KetfxPZpuJOsjM0kRvCiCgPBygBkzGzJNsq/nbJmaO8QJc6xlWfeFFnMvtd/LeKkhDJGVrmVobUA== integrity sha512-aIlMvstvu8x+34KEiOHD3AsBgdrzg6sxALYiukOWhFvGMbQI6TRP/iY0LMhUrHs56aD6P1G0Z7h45PUJaa5m9w==
boxen@^1.2.1: boxen@^1.2.1:
version "1.3.0" version "1.3.0"
@ -2720,10 +2723,10 @@ cucumber-tag-expressions@^2.0.2:
resolved "https://registry.yarnpkg.com/cucumber-tag-expressions/-/cucumber-tag-expressions-2.0.2.tgz#aac27aae3690818ec15235bd056282dad8a2d2b8" resolved "https://registry.yarnpkg.com/cucumber-tag-expressions/-/cucumber-tag-expressions-2.0.2.tgz#aac27aae3690818ec15235bd056282dad8a2d2b8"
integrity sha512-DohmT4X641KX/sb96bdb7J2kXNcQBPrYmf3Oc5kiHCLfzFMWx/o2kB4JvjvQPZnYuA9lRt6pqtArM5gvUn4uzw== integrity sha512-DohmT4X641KX/sb96bdb7J2kXNcQBPrYmf3Oc5kiHCLfzFMWx/o2kB4JvjvQPZnYuA9lRt6pqtArM5gvUn4uzw==
cucumber@~6.0.2: cucumber@~6.0.3:
version "6.0.2" version "6.0.3"
resolved "https://registry.yarnpkg.com/cucumber/-/cucumber-6.0.2.tgz#3c4fbf1f76e60ddee79ab58f137a62c897a4d7f0" resolved "https://registry.yarnpkg.com/cucumber/-/cucumber-6.0.3.tgz#bf69ecc992772e580dabe265b2ed06ddab13d076"
integrity sha512-yEwPYGvgS2KG6ODdUXQwWcxjyr/l31dmpGJsZSkJIXNLNNmieKVefTpf8zLj6+0V2TCPwkmUZt4+OIXv97duEw== integrity sha512-FSx7xdAQfFjcxp/iRBAuCFSXp2iJP1tF2Q5k/a67YgHiYbnwsD9F+UNv9ZG90LFHNsNQhb+67AmVxHkp4JRDpg==
dependencies: dependencies:
assertion-error-formatter "^3.0.0" assertion-error-formatter "^3.0.0"
bluebird "^3.4.1" bluebird "^3.4.1"
@ -2782,10 +2785,10 @@ data-urls@^1.0.0:
whatwg-mimetype "^2.2.0" whatwg-mimetype "^2.2.0"
whatwg-url "^7.0.0" whatwg-url "^7.0.0"
date-fns@2.5.0: date-fns@2.6.0:
version "2.5.0" version "2.6.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.5.0.tgz#b939f17c2902ce81cffe449702ba22c0781b38ec" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.6.0.tgz#a5bc82e6a4c3995ae124b0ba1a71aec7b8cbd666"
integrity sha512-I6Tkis01//nRcmvMQw/MRE1HAtcuA5Ie6jGPb8bJZJub7494LGOObqkV3ParnsSVviAjk5C8mNKDqYVBzCopWg== integrity sha512-F55YxqRdEfP/eYQmQjLN798v0AwLjmZ8nMBjdQvNwEE3N/zWVrlkkqT+9seBlPlsbkybG4JmWg3Ee3dIV9BcGQ==
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
version "2.6.9" version "2.6.9"
@ -3206,10 +3209,10 @@ escodegen@^1.9.1:
optionalDependencies: optionalDependencies:
source-map "~0.6.1" source-map "~0.6.1"
eslint-config-prettier@~6.4.0: eslint-config-prettier@~6.5.0:
version "6.4.0" version "6.5.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.4.0.tgz#0a04f147e31d33c6c161b2dd0971418ac52d0477" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.5.0.tgz#aaf9a495e2a816865e541bfdbb73a65cc162b3eb"
integrity sha512-YrKucoFdc7SEko5Sxe4r6ixqXPDP1tunGw91POeZTTRKItf/AMFYt/YLEQtZMkR2LVpAVhcAcZgcWpm1oGPW7w== integrity sha512-cjXp8SbO9VFGW/Z7mbTydqS9to8Z58E5aYhj3e1+Hx7lS9s6gL5ILKNpCqZAFOVYRcSkWPFYljHrEh8QFEK5EQ==
dependencies: dependencies:
get-stdin "^6.0.0" get-stdin "^6.0.0"
@ -3259,12 +3262,12 @@ eslint-plugin-import@~2.18.2:
read-pkg-up "^2.0.0" read-pkg-up "^2.0.0"
resolve "^1.11.0" resolve "^1.11.0"
eslint-plugin-jest@~22.19.0: eslint-plugin-jest@~23.0.2:
version "22.19.0" version "23.0.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.19.0.tgz#0cf90946a8c927d40a2c64458c89bb635d0f2a0b" resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-23.0.2.tgz#54a59bfe77245186afe13711a297067aefefff0a"
integrity sha512-4zUc3rh36ds0SXdl2LywT4YWA3zRe8sfLhz8bPp8qQPIKvynTTkNGwmSCMpl5d9QiZE2JxSinGF+WD8yU+O0Lg== integrity sha512-fkxcvOJm0hC/jbJqYJjtuC9mvpTJqXd0Nixx7joVQvJoBQuXk/ws3+MtRYzD/4TcKSgvr21uuSLdwSxKJKC2cg==
dependencies: dependencies:
"@typescript-eslint/experimental-utils" "^1.13.0" "@typescript-eslint/experimental-utils" "^2.5.0"
eslint-plugin-node@~10.0.0: eslint-plugin-node@~10.0.0:
version "10.0.0" version "10.0.0"
@ -3295,14 +3298,6 @@ eslint-plugin-standard@~4.0.1:
resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz#ff0519f7ffaff114f76d1bd7c3996eef0f6e20b4" resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz#ff0519f7ffaff114f76d1bd7c3996eef0f6e20b4"
integrity sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ== integrity sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==
eslint-scope@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"
integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==
dependencies:
esrecurse "^4.1.0"
estraverse "^4.1.1"
eslint-scope@^5.0.0: eslint-scope@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9"
@ -3311,22 +3306,22 @@ eslint-scope@^5.0.0:
esrecurse "^4.1.0" esrecurse "^4.1.0"
estraverse "^4.1.1" estraverse "^4.1.1"
eslint-utils@^1.4.2: eslint-utils@^1.4.2, eslint-utils@^1.4.3:
version "1.4.2" version "1.4.3"
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f"
integrity sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q== integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==
dependencies: dependencies:
eslint-visitor-keys "^1.0.0" eslint-visitor-keys "^1.1.0"
eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2"
integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==
eslint@~6.5.1: eslint@~6.6.0:
version "6.5.1" version "6.6.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.5.1.tgz#828e4c469697d43bb586144be152198b91e96ed6" resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.6.0.tgz#4a01a2fb48d32aacef5530ee9c5a78f11a8afd04"
integrity sha512-32h99BoLYStT1iq1v2P9uwpyznQ4M2jRiFB6acitKz52Gqn+vPaMDUTB1bYi1WN4Nquj2w+t+bimYUG83DC55A== integrity sha512-PpEBq7b6qY/qrOmpYQ/jTMDYfuQMELR4g4WI1M/NaSDDD/bdcMb+dj4Hgks7p41kW2caXsPsEZAEAyAgjVVC0g==
dependencies: dependencies:
"@babel/code-frame" "^7.0.0" "@babel/code-frame" "^7.0.0"
ajv "^6.10.0" ajv "^6.10.0"
@ -3335,9 +3330,9 @@ eslint@~6.5.1:
debug "^4.0.1" debug "^4.0.1"
doctrine "^3.0.0" doctrine "^3.0.0"
eslint-scope "^5.0.0" eslint-scope "^5.0.0"
eslint-utils "^1.4.2" eslint-utils "^1.4.3"
eslint-visitor-keys "^1.1.0" eslint-visitor-keys "^1.1.0"
espree "^6.1.1" espree "^6.1.2"
esquery "^1.0.1" esquery "^1.0.1"
esutils "^2.0.2" esutils "^2.0.2"
file-entry-cache "^5.0.1" file-entry-cache "^5.0.1"
@ -3347,7 +3342,7 @@ eslint@~6.5.1:
ignore "^4.0.6" ignore "^4.0.6"
import-fresh "^3.0.0" import-fresh "^3.0.0"
imurmurhash "^0.1.4" imurmurhash "^0.1.4"
inquirer "^6.4.1" inquirer "^7.0.0"
is-glob "^4.0.0" is-glob "^4.0.0"
js-yaml "^3.13.1" js-yaml "^3.13.1"
json-stable-stringify-without-jsonify "^1.0.1" json-stable-stringify-without-jsonify "^1.0.1"
@ -3366,13 +3361,13 @@ eslint@~6.5.1:
text-table "^0.2.0" text-table "^0.2.0"
v8-compile-cache "^2.0.3" v8-compile-cache "^2.0.3"
espree@^6.1.1: espree@^6.1.2:
version "6.1.1" version "6.1.2"
resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.1.tgz#7f80e5f7257fc47db450022d723e356daeb1e5de" resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d"
integrity sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ== integrity sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==
dependencies: dependencies:
acorn "^7.0.0" acorn "^7.1.0"
acorn-jsx "^5.0.2" acorn-jsx "^5.1.0"
eslint-visitor-keys "^1.1.0" eslint-visitor-keys "^1.1.0"
esprima@^3.1.3: esprima@^3.1.3:
@ -3594,7 +3589,8 @@ extsprintf@^1.2.0:
faker@Marak/faker.js#master: faker@Marak/faker.js#master:
version "4.1.0" version "4.1.0"
resolved "https://codeload.github.com/Marak/faker.js/tar.gz/10bfb9f467b0ac2b8912ffc15690b50ef3244f09" uid "9fd8d7d37b398842d0784a116a340f7aa6afb89b"
resolved "https://codeload.github.com/Marak/faker.js/tar.gz/9fd8d7d37b398842d0784a116a340f7aa6afb89b"
fast-deep-equal@^2.0.1: fast-deep-equal@^2.0.1:
version "2.0.1" version "2.0.1"
@ -3918,6 +3914,18 @@ glob@7.1.4, glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3:
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
glob@^7.1.4:
version "7.1.5"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.5.tgz#6714c69bee20f3c3e64c4dd905553e532b40cdc0"
integrity sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
global-dirs@^0.1.0: global-dirs@^0.1.0:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445"
@ -4029,14 +4037,14 @@ graphql-request@~1.8.2:
dependencies: dependencies:
cross-fetch "2.2.2" cross-fetch "2.2.2"
graphql-shield@~6.1.0: graphql-shield@~7.0.0:
version "6.1.0" version "7.0.0"
resolved "https://registry.yarnpkg.com/graphql-shield/-/graphql-shield-6.1.0.tgz#7298af72167e7c9fd19a36fac9b425b94025a393" resolved "https://registry.yarnpkg.com/graphql-shield/-/graphql-shield-7.0.0.tgz#0cdca5c16af4ba7dd4fbcce6df279c5c8e463452"
integrity sha512-dIZ6ABnUn3XQtIzw9/9f8wFmZoY5XZlsHgkxSKF+N/oXmKvQoi11J5/y/jxJTBmKYi/2JZ12C1JjDn5TOopn+w== integrity sha512-hr0PR6t/UXAO5+BMOOD2W3zTIKbtO/u8twjLn8hw4646E08NeLrIxDAmUFKKlLhyTe5JzlH4nNflP6SRtL6Q2A==
dependencies: dependencies:
"@types/yup" "0.26.23" "@types/yup" "0.26.24"
lightercollective "^0.3.0" lightercollective "^0.3.0"
object-hash "^1.3.1" object-hash "^2.0.0"
yup "^0.27.0" yup "^0.27.0"
graphql-subscriptions@^1.0.0: graphql-subscriptions@^1.0.0:
@ -4207,20 +4215,20 @@ helmet-crossdomain@0.4.0:
resolved "https://registry.yarnpkg.com/helmet-crossdomain/-/helmet-crossdomain-0.4.0.tgz#5f1fe5a836d0325f1da0a78eaa5fd8429078894e" resolved "https://registry.yarnpkg.com/helmet-crossdomain/-/helmet-crossdomain-0.4.0.tgz#5f1fe5a836d0325f1da0a78eaa5fd8429078894e"
integrity sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA== integrity sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA==
helmet-csp@2.9.2: helmet-csp@2.9.4:
version "2.9.2" version "2.9.4"
resolved "https://registry.yarnpkg.com/helmet-csp/-/helmet-csp-2.9.2.tgz#bec0adaf370b0f2e77267c9d8b6e33b34159c1e5" resolved "https://registry.yarnpkg.com/helmet-csp/-/helmet-csp-2.9.4.tgz#801382bac98f2f88706dc5c89d95c7e31af3a4a9"
integrity sha512-Lt5WqNfbNjEJ6ysD4UNpVktSyjEKfU9LVJ1LaFmPfYseg/xPealPfgHhtqdAdjPDopp5zbg/VWCyp4cluMIckw== integrity sha512-qUgGx8+yk7Xl8XFEGI4MFu1oNmulxhQVTlV8HP8tV3tpfslCs30OZz/9uQqsWPvDISiu/NwrrCowsZBhFADYqg==
dependencies: dependencies:
bowser "^2.6.1" bowser "^2.7.0"
camelize "1.0.0" camelize "1.0.0"
content-security-policy-builder "2.1.0" content-security-policy-builder "2.1.0"
dasherize "2.0.0" dasherize "2.0.0"
helmet@~3.21.1: helmet@~3.21.2:
version "3.21.1" version "3.21.2"
resolved "https://registry.yarnpkg.com/helmet/-/helmet-3.21.1.tgz#b0ab7c63fc30df2434be27e7e292a9523b3147e9" resolved "https://registry.yarnpkg.com/helmet/-/helmet-3.21.2.tgz#7e2a19d5f6d898a77b5d2858e8e4bb2cda59f19f"
integrity sha512-IC/54Lxvvad2YiUdgLmPlNFKLhNuG++waTF5KPYq/Feo3NNhqMFbcLAlbVkai+9q0+4uxjxGPJ9bNykG+3zZNg== integrity sha512-okUo+MeWgg00cKB8Csblu8EXgcIoDyb5ZS/3u0W4spCimeVuCUvVZ6Vj3O2VJ1Sxpyb8jCDvzu0L1KKT11pkIg==
dependencies: dependencies:
depd "2.0.0" depd "2.0.0"
dns-prefetch-control "0.2.0" dns-prefetch-control "0.2.0"
@ -4229,7 +4237,7 @@ helmet@~3.21.1:
feature-policy "0.3.0" feature-policy "0.3.0"
frameguard "3.1.0" frameguard "3.1.0"
helmet-crossdomain "0.4.0" helmet-crossdomain "0.4.0"
helmet-csp "2.9.2" helmet-csp "2.9.4"
hide-powered-by "1.1.0" hide-powered-by "1.1.0"
hpkp "2.0.0" hpkp "2.0.0"
hsts "2.2.0" hsts "2.2.0"
@ -4443,10 +4451,10 @@ ini@^1.3.4, ini@~1.3.0:
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
inquirer@^6.4.1: inquirer@^7.0.0:
version "6.5.1" version "7.0.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.1.tgz#8bfb7a5ac02dac6ff641ac4c5ff17da112fcdb42" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.0.tgz#9e2b032dde77da1db5db804758b8fea3a970519a"
integrity sha512-uxNHBeQhRXIoHWTSNYUFhQVrHYFThIt6IVo2fFmSe8aBwdR3/w6b58hJpiL/fMukFkvGzjg+hSxFtwvVmKZmXw== integrity sha512-rSdC7zelHdRQFkWnhsMu2+2SO41mpv2oF2zy4tMhmiLWkcKbOAs87fWAJhVXttKVwhdZvymvnuM95EyEXg2/tQ==
dependencies: dependencies:
ansi-escapes "^4.2.1" ansi-escapes "^4.2.1"
chalk "^2.4.2" chalk "^2.4.2"
@ -5668,13 +5676,6 @@ makeerror@1.0.x:
dependencies: dependencies:
tmpl "1.0.x" tmpl "1.0.x"
map-age-cleaner@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a"
integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==
dependencies:
p-defer "^1.0.0"
map-cache@^0.2.2: map-cache@^0.2.2:
version "0.2.2" version "0.2.2"
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
@ -5692,15 +5693,6 @@ media-typer@0.3.0:
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
mem@~5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/mem/-/mem-5.1.1.tgz#7059b67bf9ac2c924c9f1cff7155a064394adfb3"
integrity sha512-qvwipnozMohxLXG1pOqoLiZKNkC4r4qqRucSoDwXowsNGDSULiqFTRUF05vcZWnwJSG22qTsynQhxbaMtnX9gw==
dependencies:
map-age-cleaner "^0.1.3"
mimic-fn "^2.1.0"
p-is-promise "^2.1.0"
memoize-one@~5.1.1: memoize-one@~5.1.1:
version "5.1.1" version "5.1.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
@ -5729,19 +5721,19 @@ merge-stream@^2.0.0:
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
metascraper-audio@^5.7.6: metascraper-audio@^5.7.17:
version "5.7.6" version "5.7.17"
resolved "https://registry.yarnpkg.com/metascraper-audio/-/metascraper-audio-5.7.6.tgz#05f3a732e8316eb80c0ee58d5981d053baff6cec" resolved "https://registry.yarnpkg.com/metascraper-audio/-/metascraper-audio-5.7.17.tgz#b8e78a797deb155b02f30bcbe39da554bf1bf898"
integrity sha512-1CXw5+2WIxF5O0sJ1Hp4Zt8nSg4zXJXa9AUPMhnyhovLJ4cqGBdNVUbsxduuEHfYPiG1PtZyrtgDq+OQqiS5kA== integrity sha512-g11lRNVor5Pu4D1j3tL7aakSQM51CUl2Evp8QgFKcuYGjF+a1RiGq6veojiTf/9nWcKX8dUSTUJkQSIzdoJrFQ==
dependencies: dependencies:
"@metascraper/helpers" "^5.7.6" "@metascraper/helpers" "^5.7.17"
metascraper-author@^5.7.6: metascraper-author@^5.7.17:
version "5.7.6" version "5.7.17"
resolved "https://registry.yarnpkg.com/metascraper-author/-/metascraper-author-5.7.6.tgz#ccef7c987f433ebe00a444ff9d1bcd1c3f65c825" resolved "https://registry.yarnpkg.com/metascraper-author/-/metascraper-author-5.7.17.tgz#0403eaa4d1992152246f01616fac1d52b0583c8a"
integrity sha512-kxUrQIZVZUWzA7GInasT/InTuRZ6VPE3QCWNqrha6p89+nxHfRMpfL1YTgdQWs1Y8MGKETt1uXV20tkKQVbPuw== integrity sha512-vaMAn6glCr9f2PGvNObqMI7ECtQ7+CMkXSxKyn3fyxRVKnV95fBR+xi4+UJ2DWqTvVQ6t7gZwlzFWA4CwxfniQ==
dependencies: dependencies:
"@metascraper/helpers" "^5.7.6" "@metascraper/helpers" "^5.7.17"
lodash "~4.17.15" lodash "~4.17.15"
metascraper-clearbit-logo@^5.3.0: metascraper-clearbit-logo@^5.3.0:
@ -5751,26 +5743,26 @@ metascraper-clearbit-logo@^5.3.0:
dependencies: dependencies:
got "~9.6.0" got "~9.6.0"
metascraper-date@^5.7.6: metascraper-date@^5.7.17:
version "5.7.6" version "5.7.17"
resolved "https://registry.yarnpkg.com/metascraper-date/-/metascraper-date-5.7.6.tgz#6d2e2b39f0a43374abf6f8639017b2500b821a54" resolved "https://registry.yarnpkg.com/metascraper-date/-/metascraper-date-5.7.17.tgz#8777bc5deaccce1235ed0b2eb8f0746c981ee245"
integrity sha512-ikTNuOrKk9nA78/dxeTydkO4kajaFEzR6IAi1GVXTKzhMTaH9A8HA8ra/LndD8KYZMAEmJaIFvefi8vGVVkcUw== integrity sha512-OPKXu7S+S6JoZNVV9Dox6OIG2x5hzDx2J3IzMwzQwVdKzulMPSFMLCcJU8zLZ03dajSOszRf8aL1eSBfZscpIw==
dependencies: dependencies:
"@metascraper/helpers" "^5.7.6" "@metascraper/helpers" "^5.7.17"
metascraper-description@^5.7.6: metascraper-description@^5.7.17:
version "5.7.6" version "5.7.17"
resolved "https://registry.yarnpkg.com/metascraper-description/-/metascraper-description-5.7.6.tgz#3a26b8bb8f325b1e959864f2c983ad4ef6050c24" resolved "https://registry.yarnpkg.com/metascraper-description/-/metascraper-description-5.7.17.tgz#b0daa54d0345546ececcc033065790402aabb5ec"
integrity sha512-DtcIRTwI2RFdy2NBSYCUpekNSqMH4BVNjAJNpWWYsDJZbk5rw6w2gOgzJH4HUM4eYKDkd0/tVd+HZYz57xxYBQ== integrity sha512-cQfg9Spl3FLK2x8O7DvecwSYEBUmRjtdZW2y1EVqHsOKwT13SeUy1kp+lZa8+8vFh4o8oJPzXHxgbLhAfAmVqQ==
dependencies: dependencies:
"@metascraper/helpers" "^5.7.6" "@metascraper/helpers" "^5.7.17"
metascraper-image@^5.7.6: metascraper-image@^5.7.17:
version "5.7.6" version "5.7.17"
resolved "https://registry.yarnpkg.com/metascraper-image/-/metascraper-image-5.7.6.tgz#77c45dfd28f6f0903417545196c129ec64cdbe6d" resolved "https://registry.yarnpkg.com/metascraper-image/-/metascraper-image-5.7.17.tgz#186b29979cb8aefc6c21d0342c386a8fef80be55"
integrity sha512-SIztcREe0m1p1wCUNh+mFVfXPbFQfOu6dZVhU1BIxb6+0km+pUOtPi6Kxnax10ZH4k0ZlFDmLr98yV/ydYdgvw== integrity sha512-bwAUJrJibJ+fJGxL8T789Ki1z+8sqsz0sqb3W+mfR/ZLkhCu+jWLYqPVtMgTPM9Zaqqqxg5uTQs1uAVrnguKDA==
dependencies: dependencies:
"@metascraper/helpers" "^5.7.6" "@metascraper/helpers" "^5.7.17"
metascraper-lang-detector@^4.8.5: metascraper-lang-detector@^4.8.5:
version "4.10.2" version "4.10.2"
@ -5781,65 +5773,65 @@ metascraper-lang-detector@^4.8.5:
franc "~4.0.0" franc "~4.0.0"
iso-639-3 "~1.1.0" iso-639-3 "~1.1.0"
metascraper-lang@^5.7.6: metascraper-lang@^5.7.17:
version "5.7.6" version "5.7.17"
resolved "https://registry.yarnpkg.com/metascraper-lang/-/metascraper-lang-5.7.6.tgz#9d0ac51ed29a99b5864b2291f89b309d08ffe6af" resolved "https://registry.yarnpkg.com/metascraper-lang/-/metascraper-lang-5.7.17.tgz#3952db650bcd909fff0308d1d2254e954a0c0028"
integrity sha512-RLeAB0Vzz8M3V98unF9VD3Q43I8HqMAx4rpy3Zml4ysvdZAwKsZZUT8IVVWOXhnfWCgjZDstjNhVbgQpsSsthQ== integrity sha512-G/XqySeDpZmoV1rgWeMs/hmX1NFX0IN2w4viNdgdMRXB+lhqeyk5Z20x9ssPAqiJ4Ab6tyR274NkgYa0ZNRMDw==
dependencies: dependencies:
"@metascraper/helpers" "^5.7.6" "@metascraper/helpers" "^5.7.17"
metascraper-logo@^5.7.6: metascraper-logo@^5.7.17:
version "5.7.6" version "5.7.17"
resolved "https://registry.yarnpkg.com/metascraper-logo/-/metascraper-logo-5.7.6.tgz#92680c8e839c6c357ecb4d7fe0445f46233d0f09" resolved "https://registry.yarnpkg.com/metascraper-logo/-/metascraper-logo-5.7.17.tgz#b26e2fb38e94cfe9ec9dfc7e28d8da26a0a0689d"
integrity sha512-0pMHxua4dNUcWLCk4WGCqBcuMoUoMAr3kFT34tJZTAd345iCagtwNNs2iAcNLdpNqyXzKyGIKcZPNkbWByVcCQ== integrity sha512-S4aqxN4Qi3UXDLN4HhinEuQHUopYXbFw0Y5Cwj9TbGKfESeQ1n6Jm4eOgGifEYyyZMSeRR9li189EK3YPnYcFg==
dependencies: dependencies:
"@metascraper/helpers" "^5.7.6" "@metascraper/helpers" "^5.7.17"
metascraper-publisher@^5.7.6: metascraper-publisher@^5.7.17:
version "5.7.6" version "5.7.17"
resolved "https://registry.yarnpkg.com/metascraper-publisher/-/metascraper-publisher-5.7.6.tgz#d0b04ae2f260a5bf0536fad29b3617fb9b6df296" resolved "https://registry.yarnpkg.com/metascraper-publisher/-/metascraper-publisher-5.7.17.tgz#38455e035d8d34c42eff529316ee15f31726d641"
integrity sha512-RVvyNbzJnWutA2rSbK0CytokBXu1SVfsnME+IeKOGmtiSnKtGx5Q0lALHNVHt+bIShEYgba0UlVO/mPE37dU8g== integrity sha512-BxiweB0vxXX0UF2YVxzwC7Y8X0A5mU+eaa6TsTrTGHPBWeZCUJaLJ2Ge35c00SIC+USgdu8KFyzF6+pJBObwvQ==
dependencies: dependencies:
"@metascraper/helpers" "^5.7.6" "@metascraper/helpers" "^5.7.17"
metascraper-soundcloud@^5.7.6: metascraper-soundcloud@^5.7.17:
version "5.7.6" version "5.7.17"
resolved "https://registry.yarnpkg.com/metascraper-soundcloud/-/metascraper-soundcloud-5.7.6.tgz#80c725e8746d94c992b5bdd07ac6bd987d09944d" resolved "https://registry.yarnpkg.com/metascraper-soundcloud/-/metascraper-soundcloud-5.7.17.tgz#925fc91505b69f1e3e7f0c535567c7918f8afbd9"
integrity sha512-fBxX5mYPFf8rWhhEX2XZD5QrmvtUI5IIPzryGuwEWsbPuMGuUkvFA9JjHJiC46uYXoi6UuKLXwSmYHcAACG3Jg== integrity sha512-yllxXR0AHQmJLXCua+CJtjzmNr9I+mU/H23ED+S2t9Yd07xQDmqL8pkkuD8DAAy7aC6oIL0qghQPwk8qdM97Ug==
dependencies: dependencies:
"@metascraper/helpers" "^5.7.6" "@metascraper/helpers" "^5.7.17"
memoize-one "~5.1.1" memoize-one "~5.1.1"
tldts "~5.5.0" tldts "~5.6.1"
metascraper-title@^5.7.6: metascraper-title@^5.7.17:
version "5.7.6" version "5.7.17"
resolved "https://registry.yarnpkg.com/metascraper-title/-/metascraper-title-5.7.6.tgz#346c637e735a040f299af5f67715eb8ed0016850" resolved "https://registry.yarnpkg.com/metascraper-title/-/metascraper-title-5.7.17.tgz#5b947635361bfb4d7557eadcb623489c812322e6"
integrity sha512-DF6TeMODzzLgJMLyUtN6wLPrz9/3JcRKyUIfWpuuw+WFC3Kx6ON8nWldTRh1yUu9xbSAOleae//f/dn+JhYlCw== integrity sha512-YCEbiU2MbPMLulXmLbSBN/N7ti9tBVr45yqMKSuFsWiNJ98bFsM1IQp1LN5KqRQmNkOg+8JsYgK+R9vqYwaGjg==
dependencies: dependencies:
"@metascraper/helpers" "^5.7.6" "@metascraper/helpers" "^5.7.17"
lodash "~4.17.15" lodash "~4.17.15"
metascraper-url@^5.7.6: metascraper-url@^5.7.17:
version "5.7.6" version "5.7.17"
resolved "https://registry.yarnpkg.com/metascraper-url/-/metascraper-url-5.7.6.tgz#2f35b50e12ed14e2e6062285fd10cd3f37ec1bd8" resolved "https://registry.yarnpkg.com/metascraper-url/-/metascraper-url-5.7.17.tgz#e8ba40a17a59b54139f42d6e3cf430dc6f32e7d7"
integrity sha512-V0ddB/UKsWOXcO5cQVdiX5IHPkC7wpWnVj6sc7NkHWImzq8GAQR6jWaPQ9t8uhQuLdqiXaW9l+a6x6zX3LC/hw== integrity sha512-7OOhCXpxdMiJatrbxa9rqLmUT/t/s34PDgtknoE/2FfmZY7X/xyORamcuqUHjV37sOpCPTun+GcJL4l3ddCi3Q==
dependencies: dependencies:
"@metascraper/helpers" "^5.7.6" "@metascraper/helpers" "^5.7.17"
metascraper-video@^5.7.6: metascraper-video@^5.7.17:
version "5.7.6" version "5.7.17"
resolved "https://registry.yarnpkg.com/metascraper-video/-/metascraper-video-5.7.6.tgz#ae149d6804ba026155d4c71e59b4d2ffb95b3062" resolved "https://registry.yarnpkg.com/metascraper-video/-/metascraper-video-5.7.17.tgz#414d4641fbea667e73c42fe3706d673ee4c4aec5"
integrity sha512-9Ak7QI3Je21h0+3i09SruGn1sLWUSB7ATLPtiVd7DfRb5O164LWhhmvMBxaMHhoMnvWgU9xuMkzQfI8kY66rBw== integrity sha512-lftJGynCVNfC15eyMW7tN3QWJl9T2sVNCgP0dZsW8OC1hWQM7WY3PW8yYd2PP6nUuwOTjNLL1F4oWNhldWrE8A==
dependencies: dependencies:
"@metascraper/helpers" "^5.7.6" "@metascraper/helpers" "^5.7.17"
lodash "~4.17.15" lodash "~4.17.15"
metascraper-youtube@^5.7.6: metascraper-youtube@^5.7.17:
version "5.7.6" version "5.7.17"
resolved "https://registry.yarnpkg.com/metascraper-youtube/-/metascraper-youtube-5.7.6.tgz#4add25d3e86752d0429cf195ef9a130c5f0d1a4a" resolved "https://registry.yarnpkg.com/metascraper-youtube/-/metascraper-youtube-5.7.17.tgz#a3bdf06bbc9aa3766f08a779fa880d8a3fda9f8c"
integrity sha512-GLjnYeOELQJ7upu6ji6yILpF835i7cttZZwIrPxKWwTJKyAUJ5Ydp0wiqm/xkmMBIsQ6isgRIVSxOb6g9SnH+w== integrity sha512-CZX03wX8ui8fjx+iBZCiAGdSKy4dMFiDrVSPmTMK2W8sn2guYv2QQ41g8gruFJgrF+m+mCOUG6KYgy3B/v5LdQ==
dependencies: dependencies:
"@metascraper/helpers" "^5.7.6" "@metascraper/helpers" "^5.7.17"
get-video-id "~3.1.4" get-video-id "~3.1.4"
is-reachable "~4.0.0" is-reachable "~4.0.0"
memoize-one "~5.1.1" memoize-one "~5.1.1"
@ -6069,13 +6061,14 @@ neo4j-driver@^1.7.3, neo4j-driver@^1.7.5, neo4j-driver@~1.7.6:
text-encoding-utf-8 "^1.0.2" text-encoding-utf-8 "^1.0.2"
uri-js "^4.2.2" uri-js "^4.2.2"
neo4j-graphql-js@^2.7.2: neo4j-graphql-js@^2.8.0:
version "2.7.2" version "2.8.0"
resolved "https://registry.yarnpkg.com/neo4j-graphql-js/-/neo4j-graphql-js-2.7.2.tgz#6a56c63874bc41e678cb83580c6c7647e6f61ccf" resolved "https://registry.yarnpkg.com/neo4j-graphql-js/-/neo4j-graphql-js-2.8.0.tgz#58035b9213656e17b6ed4c6cbf4dfe1c56a8a219"
integrity sha512-nrhSmNAkiYgksNabNuHyMHYYaLloYZaVXRiYGrRVUcf84TLiwJdg5k9hIQVH9O6QQOvnK3lwBDlE7T0phpAIpg== integrity sha512-nDuzmi6W/YGIIVm+GAXCr/8CLABsU/RfeLebLH32vqeKViFATMfm4eT66aOq/GwHJ0838+o20yCbIFdx5rTP/A==
dependencies: dependencies:
"@babel/runtime" "^7.5.5" "@babel/runtime" "^7.5.5"
"@babel/runtime-corejs2" "^7.5.5" "@babel/runtime-corejs2" "^7.5.5"
debug "^4.1.1"
graphql "^14.2.1" graphql "^14.2.1"
graphql-auth-directives "^2.1.0" graphql-auth-directives "^2.1.0"
lodash "^4.17.15" lodash "^4.17.15"
@ -6341,10 +6334,10 @@ object-copy@^0.1.0:
define-property "^0.2.5" define-property "^0.2.5"
kind-of "^3.0.3" kind-of "^3.0.3"
object-hash@^1.3.1: object-hash@^2.0.0:
version "1.3.1" version "2.0.0"
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.0.tgz#7c4cc341eb8b53367312a7c546142f00c9e0ea20"
integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== integrity sha512-I7zGBH0rDKwVGeGZpZoFaDhIwvJa3l1CZE+8VchylXbInNiCj7sxxea9P5dTM4ftKR5//nrqxrdeGSTWL2VpBA==
object-keys@^1.0.11, object-keys@^1.0.12: object-keys@^1.0.11, object-keys@^1.0.12:
version "1.1.1" version "1.1.1"
@ -6492,11 +6485,6 @@ p-cancelable@^2.0.0:
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e"
integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg== integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==
p-defer@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c"
integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=
p-each-series@^1.0.0: p-each-series@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71" resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71"
@ -6509,11 +6497,6 @@ p-finally@^1.0.0:
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
p-is-promise@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e"
integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==
p-limit@^1.1.0: p-limit@^1.1.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
@ -7412,11 +7395,6 @@ semver-diff@^2.0.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
semver@5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==
semver@^6.0.0, semver@^6.1.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: semver@^6.0.0, semver@^6.1.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0:
version "6.3.0" version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
@ -8026,17 +8004,17 @@ tlds@^1.187.0, tlds@^1.203.0:
resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.203.1.tgz#4dc9b02f53de3315bc98b80665e13de3edfc1dfc" resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.203.1.tgz#4dc9b02f53de3315bc98b80665e13de3edfc1dfc"
integrity sha512-7MUlYyGJ6rSitEZ3r1Q1QNV8uSIzapS8SmmhSusBuIc7uIxPPwsKllEP0GRp1NS6Ik6F+fRZvnjDWm3ecv2hDw== integrity sha512-7MUlYyGJ6rSitEZ3r1Q1QNV8uSIzapS8SmmhSusBuIc7uIxPPwsKllEP0GRp1NS6Ik6F+fRZvnjDWm3ecv2hDw==
tldts-core@^5.5.0: tldts-core@^5.6.1:
version "5.5.0" version "5.6.1"
resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-5.5.0.tgz#ae22afe586541ac5ecacc520038068639b3420b4" resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-5.6.1.tgz#943fd020b564018fae308c12ec2435e53101c257"
integrity sha512-o0JzahqioihXz8wj7/1OYtefyhXz/PwLno7VRm5MTwQitEOPpvMPZpj2yjXtjgOMKbi3A5OHvvJwhFf0Hutzng== integrity sha512-ikhUCHoiRu0QzQpba0f0q1Km5YBnn4qsBzGlYCzT3y3wSCGG2GlV0xeEOcXTzp2pRne6bQaHRry4TINMZpDFKQ==
tldts@~5.5.0: tldts@~5.6.1:
version "5.5.0" version "5.6.1"
resolved "https://registry.yarnpkg.com/tldts/-/tldts-5.5.0.tgz#12ea124593bc5abebd12107c6223986f97972bc1" resolved "https://registry.yarnpkg.com/tldts/-/tldts-5.6.1.tgz#36f4ac97505b9202f2872f6246f326589f49d78b"
integrity sha512-CZ/d7Y4k8onxwerMWz/mTCeKJtX3VAMiL+ajXVFnxsKhH4BV+QavjnZ1Mb9OeCHo3jX0S3Dw6ERNRXqOMVsDvw== integrity sha512-I+imSP592J9GUYApIoiDdJk3KlroHY4zmDmpAp+TlIDZZAPxx192yOUViMB2QmlcRtZUz5XLEM3cS2F0V7P1Fw==
dependencies: dependencies:
tldts-core "^5.5.0" tldts-core "^5.6.1"
tmp@^0.0.33: tmp@^0.0.33:
version "0.0.33" version "0.0.33"

View File

@ -0,0 +1,2 @@
// please change also version in file "webapp/constants/terms-and-conditions-version.js"
export const VERSION = '0.0.3'

View File

@ -1,4 +1,5 @@
import { Given, When, Then } from 'cypress-cucumber-preprocessor/steps' import { Given, When, Then } from 'cypress-cucumber-preprocessor/steps'
import { VERSION } from '../../constants/terms-and-conditions-version.js'
/* global cy */ /* global cy */
@ -31,7 +32,7 @@ Given('I am logged in with a {string} role', role => {
cy.factory().create('User', { cy.factory().create('User', {
email: `${role}@example.org`, email: `${role}@example.org`,
password: '1234', password: '1234',
termsAndConditionsAgreedVersion: "0.0.2", termsAndConditionsAgreedVersion: VERSION,
role role
}) })
cy.login({ cy.login({

View File

@ -4,6 +4,7 @@ import {
Then Then
} from "cypress-cucumber-preprocessor/steps"; } from "cypress-cucumber-preprocessor/steps";
import helpers from "../../support/helpers"; import helpers from "../../support/helpers";
import { VERSION } from '../../constants/terms-and-conditions-version.js'
/* global cy */ /* global cy */
@ -14,7 +15,7 @@ let loginCredentials = {
password: "1234" password: "1234"
}; };
const termsAndConditionsAgreedVersion = { const termsAndConditionsAgreedVersion = {
termsAndConditionsAgreedVersion: "0.0.2" termsAndConditionsAgreedVersion: VERSION
}; };
const narratorParams = { const narratorParams = {
id: 'id-of-peter-pan', id: 'id-of-peter-pan',

View File

@ -1,4 +1,4 @@
FROM neo4j:3.5.11-enterprise FROM neo4j:3.5.12-enterprise
LABEL Description="Neo4J database of the Social Network Human-Connection.org with preinstalled database constraints and indices" Vendor="Human Connection gGmbH" Version="0.0.1" Maintainer="Human Connection gGmbH (developer@human-connection.org)" LABEL Description="Neo4J database of the Social Network Human-Connection.org with preinstalled database constraints and indices" Vendor="Human Connection gGmbH" Version="0.0.1" Maintainer="Human Connection gGmbH (developer@human-connection.org)"
ARG BUILD_COMMIT ARG BUILD_COMMIT

View File

@ -1,6 +1,6 @@
{ {
"name": "nitro-cypress", "name": "nitro-cypress",
"version": "1.0.0", "version": "0.1.8",
"description": "Fullstack tests with cypress for Human Connection", "description": "Fullstack tests with cypress for Human Connection",
"author": "Human Connection gGmbh", "author": "Human Connection gGmbh",
"license": "MIT", "license": "MIT",
@ -16,18 +16,20 @@
"cypress:setup": "run-p cypress:backend:* cypress:webapp", "cypress:setup": "run-p cypress:backend:* cypress:webapp",
"cypress:run": "cypress run --browser chromium", "cypress:run": "cypress run --browser chromium",
"cypress:open": "cypress open --browser chromium", "cypress:open": "cypress open --browser chromium",
"test:jest": "cd webapp && yarn test && cd ../backend && yarn test:jest && codecov" "test:jest": "cd webapp && yarn test && cd ../backend && yarn test:jest && codecov",
"version": "auto-changelog -p"
}, },
"devDependencies": { "devDependencies": {
"auto-changelog": "^1.16.2",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"codecov": "^3.6.1", "codecov": "^3.6.1",
"cross-env": "^6.0.3", "cross-env": "^6.0.3",
"cypress": "^3.4.1", "cypress": "^3.6.0",
"cypress-cucumber-preprocessor": "^1.16.2", "cypress-cucumber-preprocessor": "^1.16.2",
"cypress-file-upload": "^3.3.4", "cypress-file-upload": "^3.4.0",
"cypress-plugin-retries": "^1.3.0", "cypress-plugin-retries": "^1.4.0",
"date-fns": "^2.5.0", "date-fns": "^2.6.0",
"dotenv": "^8.1.0", "dotenv": "^8.2.0",
"faker": "Marak/faker.js#master", "faker": "Marak/faker.js#master",
"graphql-request": "^1.8.2", "graphql-request": "^1.8.2",
"neo4j-driver": "^1.7.6", "neo4j-driver": "^1.7.6",

View File

@ -1,4 +1,4 @@
FROM node:12.12.0-alpine as base FROM node:13.0.1-alpine as base
LABEL Description="Web Frontend of the Social Network Human-Connection.org" Vendor="Human-Connection gGmbH" Version="0.0.1" Maintainer="Human-Connection gGmbH (developer@human-connection.org)" LABEL Description="Web Frontend of the Social Network Human-Connection.org" Vendor="Human-Connection gGmbH" Version="0.0.1" Maintainer="Human-Connection gGmbH (developer@human-connection.org)"
EXPOSE 3000 EXPOSE 3000

View File

@ -1,5 +1,5 @@
FROM node:12.12.0-alpine as build FROM node:13.0.1-alpine as build
LABEL Description="Web Frontend of the Social Network Human-Connection.org" Vendor="Human-Connection gGmbH" Version="0.0.1" Maintainer="Human-Connection gGmbH (developer@human-connection.org)" LABEL Description="Maintenance page of the Social Network Human-Connection.org" Vendor="Human-Connection gGmbH" Version="0.0.1" Maintainer="Human-Connection gGmbH (developer@human-connection.org)"
EXPOSE 3000 EXPOSE 3000
CMD ["yarn", "run", "start"] CMD ["yarn", "run", "start"]

View File

@ -149,6 +149,7 @@ export default {
}, },
editCommentMenu(showMenu) { editCommentMenu(showMenu) {
this.openEditCommentMenu = showMenu this.openEditCommentMenu = showMenu
this.$emit('toggleNewCommentForm', !showMenu)
}, },
updateComment(comment) { updateComment(comment) {
this.$emit('updateComment', comment) this.$emit('updateComment', comment)

View File

@ -0,0 +1,45 @@
import { storiesOf } from '@storybook/vue'
import { withA11y } from '@storybook/addon-a11y'
import HcCommentList from './CommentList.vue'
import helpers from '~/storybook/helpers'
import faker from 'faker'
helpers.init()
const commentMock = fields => {
return {
id: faker.random.uuid(),
title: faker.lorem.sentence(),
content: faker.lorem.paragraph(),
createdAt: faker.date.past(),
updatedAt: faker.date.recent(),
deleted: false,
disabled: false,
...fields,
}
}
const comments = [
commentMock(),
commentMock(),
commentMock(),
commentMock(),
commentMock(),
commentMock(),
commentMock(),
commentMock(),
commentMock(),
commentMock(),
]
storiesOf('CommentList', module)
.addDecorator(withA11y)
.addDecorator(helpers.layout)
.add('given 10 comments', () => ({
components: { HcCommentList },
store: helpers.store,
data: () => ({
post: { comments },
}),
template: `<hc-comment-list :post="post" />`,
}))

View File

@ -25,6 +25,7 @@
:routeHash="routeHash" :routeHash="routeHash"
@deleteComment="updateCommentList" @deleteComment="updateCommentList"
@updateComment="updateCommentList" @updateComment="updateCommentList"
@toggleNewCommentForm="toggleNewCommentForm"
/> />
</div> </div>
</div> </div>
@ -51,6 +52,9 @@ export default {
return comment.id === updatedComment.id ? updatedComment : comment return comment.id === updatedComment.id ? updatedComment : comment
}) })
}, },
toggleNewCommentForm(showNewCommentForm) {
this.$emit('toggleNewCommentForm', showNewCommentForm)
},
}, },
} }
</script> </script>

View File

@ -55,24 +55,46 @@ export default {
routes() { routes() {
let routes = [] let routes = []
if (this.isOwner && this.resourceType === 'contribution') { if (this.resourceType === 'contribution') {
routes.push({ if (this.isOwner) {
name: this.$t(`post.menu.edit`), routes.push({
path: this.$router.resolve({ name: this.$t(`post.menu.edit`),
name: 'post-edit-id', path: this.$router.resolve({
params: { name: 'post-edit-id',
id: this.resource.id, params: {
id: this.resource.id,
},
}).href,
icon: 'edit',
})
routes.push({
name: this.$t(`post.menu.delete`),
callback: () => {
this.openModal('delete')
}, },
}).href, icon: 'trash',
icon: 'edit', })
}) }
routes.push({
name: this.$t(`post.menu.delete`), if (this.isAdmin) {
callback: () => { if (!this.resource.pinnedBy) {
this.openModal('delete') routes.push({
}, name: this.$t(`post.menu.pin`),
icon: 'trash', callback: () => {
}) this.$emit('pinPost', this.resource)
},
icon: 'link',
})
} else {
routes.push({
name: this.$t(`post.menu.unpin`),
callback: () => {
this.$emit('unpinPost', this.resource)
},
icon: 'unlink',
})
}
}
} }
if (this.isOwner && this.resourceType === 'comment') { if (this.isOwner && this.resourceType === 'comment') {
@ -155,6 +177,9 @@ export default {
isModerator() { isModerator() {
return this.$store.getters['auth/isModerator'] return this.$store.getters['auth/isModerator']
}, },
isAdmin() {
return this.$store.getters['auth/isAdmin']
},
}, },
methods: { methods: {
openItem(route, toggleMenu) { openItem(route, toggleMenu) {

View File

@ -10,7 +10,9 @@
</hc-teaser-image> </hc-teaser-image>
<ds-card> <ds-card>
<ds-space /> <ds-space />
<hc-user :user="currentUser" :trunc="35" /> <client-only>
<hc-user :user="currentUser" :trunc="35" />
</client-only>
<ds-space /> <ds-space />
<ds-input <ds-input
model="title" model="title"

View File

@ -67,13 +67,13 @@ export default {
}, },
computed: { computed: {
...mapGetters({ ...mapGetters({
filteredCategoryIds: 'postsFilter/filteredCategoryIds', filteredCategoryIds: 'posts/filteredCategoryIds',
}), }),
}, },
methods: { methods: {
...mapMutations({ ...mapMutations({
resetCategories: 'postsFilter/RESET_CATEGORIES', resetCategories: 'posts/RESET_CATEGORIES',
toggleCategory: 'postsFilter/TOGGLE_CATEGORY', toggleCategory: 'posts/TOGGLE_CATEGORY',
}), }),
}, },
} }

View File

@ -50,20 +50,20 @@ describe('FilterPosts.vue', () => {
describe('mount', () => { describe('mount', () => {
mutations = { mutations = {
'postsFilter/TOGGLE_FILTER_BY_FOLLOWED': jest.fn(), 'posts/TOGGLE_FILTER_BY_FOLLOWED': jest.fn(),
'postsFilter/RESET_CATEGORIES': jest.fn(), 'posts/RESET_CATEGORIES': jest.fn(),
'postsFilter/TOGGLE_CATEGORY': jest.fn(), 'posts/TOGGLE_CATEGORY': jest.fn(),
'postsFilter/TOGGLE_EMOTION': jest.fn(), 'posts/TOGGLE_EMOTION': jest.fn(),
} }
getters = { getters = {
'postsFilter/isActive': () => false, 'posts/isActive': () => false,
'auth/isModerator': () => false, 'auth/isModerator': () => false,
'auth/user': () => { 'auth/user': () => {
return { id: 'u34' } return { id: 'u34' }
}, },
'postsFilter/filteredCategoryIds': jest.fn(() => []), 'posts/filteredCategoryIds': jest.fn(() => []),
'postsFilter/filteredByUsersFollowed': jest.fn(), 'posts/filteredByUsersFollowed': jest.fn(),
'postsFilter/filteredByEmotions': jest.fn(() => []), 'posts/filteredByEmotions': jest.fn(() => []),
} }
const openFilterPosts = () => { const openFilterPosts = () => {
const store = new Vuex.Store({ mutations, getters }) const store = new Vuex.Store({ mutations, getters })
@ -94,18 +94,18 @@ describe('FilterPosts.vue', () => {
const wrapper = openFilterPosts() const wrapper = openFilterPosts()
environmentAndNatureButton = wrapper.findAll('button').at(2) environmentAndNatureButton = wrapper.findAll('button').at(2)
environmentAndNatureButton.trigger('click') environmentAndNatureButton.trigger('click')
expect(mutations['postsFilter/TOGGLE_CATEGORY']).toHaveBeenCalledWith({}, 'cat4') expect(mutations['posts/TOGGLE_CATEGORY']).toHaveBeenCalledWith({}, 'cat4')
}) })
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['posts/filteredCategoryIds'] = jest.fn(() => ['cat9'])
const wrapper = openFilterPosts() const wrapper = openFilterPosts()
democracyAndPoliticsButton = wrapper.findAll('button').at(4) democracyAndPoliticsButton = wrapper.findAll('button').at(4)
expect(democracyAndPoliticsButton.attributes().class).toContain('ds-button-primary') expect(democracyAndPoliticsButton.attributes().class).toContain('ds-button-primary')
}) })
it('sets "filter-by-followed-authors-only" button attribute `primary`', () => { it('sets "filter-by-followed-authors-only" button attribute `primary`', () => {
getters['postsFilter/filteredByUsersFollowed'] = jest.fn(() => true) getters['posts/filteredByUsersFollowed'] = jest.fn(() => true)
const wrapper = openFilterPosts() const wrapper = openFilterPosts()
expect( expect(
wrapper.find({ name: 'filter-by-followed-authors-only' }).classes('ds-button-primary'), wrapper.find({ name: 'filter-by-followed-authors-only' }).classes('ds-button-primary'),
@ -120,7 +120,7 @@ describe('FilterPosts.vue', () => {
}) })
it('calls TOGGLE_FILTER_BY_FOLLOWED', () => { it('calls TOGGLE_FILTER_BY_FOLLOWED', () => {
expect(mutations['postsFilter/TOGGLE_FILTER_BY_FOLLOWED']).toHaveBeenCalledWith({}, 'u34') expect(mutations['posts/TOGGLE_FILTER_BY_FOLLOWED']).toHaveBeenCalledWith({}, 'u34')
}) })
}) })
@ -129,11 +129,11 @@ describe('FilterPosts.vue', () => {
const wrapper = openFilterPosts() const wrapper = openFilterPosts()
happyEmotionButton = wrapper.findAll('button.emotions-buttons').at(1) happyEmotionButton = wrapper.findAll('button.emotions-buttons').at(1)
happyEmotionButton.trigger('click') happyEmotionButton.trigger('click')
expect(mutations['postsFilter/TOGGLE_EMOTION']).toHaveBeenCalledWith({}, 'happy') expect(mutations['posts/TOGGLE_EMOTION']).toHaveBeenCalledWith({}, 'happy')
}) })
it('sets the attribute `src` to colorized image', () => { it('sets the attribute `src` to colorized image', () => {
getters['postsFilter/filteredByEmotions'] = jest.fn(() => ['happy']) getters['posts/filteredByEmotions'] = jest.fn(() => ['happy'])
const wrapper = openFilterPosts() const wrapper = openFilterPosts()
happyEmotionButton = wrapper.findAll('button.emotions-buttons').at(1) happyEmotionButton = wrapper.findAll('button.emotions-buttons').at(1)
const happyEmotionButtonImage = happyEmotionButton.find('img') const happyEmotionButtonImage = happyEmotionButton.find('img')

View File

@ -39,7 +39,7 @@ export default {
computed: { computed: {
...mapGetters({ ...mapGetters({
currentUser: 'auth/user', currentUser: 'auth/user',
filterActive: 'postsFilter/isActive', filterActive: 'posts/isActive',
}), }),
chunk() { chunk() {
return chunk(this.categories, 2) return chunk(this.categories, 2)

View File

@ -68,14 +68,14 @@ export default {
}, },
computed: { computed: {
...mapGetters({ ...mapGetters({
filteredByUsersFollowed: 'postsFilter/filteredByUsersFollowed', filteredByUsersFollowed: 'posts/filteredByUsersFollowed',
filteredByEmotions: 'postsFilter/filteredByEmotions', filteredByEmotions: 'posts/filteredByEmotions',
}), }),
}, },
methods: { methods: {
...mapMutations({ ...mapMutations({
toggleFilteredByFollowed: 'postsFilter/TOGGLE_FILTER_BY_FOLLOWED', toggleFilteredByFollowed: 'posts/TOGGLE_FILTER_BY_FOLLOWED',
toogleFilteredByEmotions: 'postsFilter/TOGGLE_EMOTION', toogleFilteredByEmotions: 'posts/TOGGLE_EMOTION',
}), }),
iconPath(emotion) { iconPath(emotion) {
if (this.filteredByEmotions.includes(emotion)) { if (this.filteredByEmotions.includes(emotion)) {

View File

@ -2,26 +2,43 @@ import { mount, createLocalVue } from '@vue/test-utils'
import Styleguide from '@human-connection/styleguide' import Styleguide from '@human-connection/styleguide'
import VTooltip from 'v-tooltip' import VTooltip from 'v-tooltip'
import LocaleSwitch from './LocaleSwitch.vue' import LocaleSwitch from './LocaleSwitch.vue'
import Vuex from 'vuex'
const localVue = createLocalVue() const localVue = createLocalVue()
localVue.use(Styleguide) localVue.use(Styleguide)
localVue.use(VTooltip) localVue.use(VTooltip)
localVue.use(Vuex)
describe('LocaleSwitch.vue', () => { describe('LocaleSwitch.vue', () => {
let wrapper let wrapper, mocks, computed, deutschLanguageItem, getters
let mocks
let computed
let deutschLanguageItem
beforeEach(() => { beforeEach(() => {
mocks = { mocks = {
$i18n: { $i18n: {
locale: () => 'de', locale: () => 'en',
set: jest.fn(), set: jest.fn(locale => locale),
}, },
$t: jest.fn(), $t: jest.fn(),
$toast: {
success: jest.fn(a => a),
error: jest.fn(a => a),
},
setPlaceholderText: jest.fn(), setPlaceholderText: jest.fn(),
$apollo: {
mutate: jest
.fn()
.mockResolvedValueOnce({
data: {
UpdateUser: {
locale: 'de',
},
},
})
.mockRejectedValueOnce({
message: 'Please log in!',
}),
},
} }
computed = { computed = {
current: () => { current: () => {
@ -40,12 +57,21 @@ describe('LocaleSwitch.vue', () => {
] ]
}, },
} }
getters = {
'auth/user': () => {
return { id: 'u35' }
},
}
}) })
describe('mount', () => { const Wrapper = () => {
const Wrapper = () => { const store = new Vuex.Store({
return mount(LocaleSwitch, { mocks, localVue, computed }) getters,
} })
return mount(LocaleSwitch, { mocks, localVue, computed, store })
}
describe('with current user', () => {
beforeEach(() => { beforeEach(() => {
wrapper = Wrapper() wrapper = Wrapper()
wrapper.find('.locale-menu').trigger('click') wrapper.find('.locale-menu').trigger('click')
@ -53,8 +79,30 @@ describe('LocaleSwitch.vue', () => {
deutschLanguageItem.trigger('click') deutschLanguageItem.trigger('click')
}) })
it("changes a user's locale", () => { it("sets a user's locale", () => {
expect(mocks.$i18n.set).toHaveBeenCalledTimes(1) expect(mocks.$i18n.set).toHaveBeenCalledTimes(1)
}) })
it("updates the user's locale in the database", () => {
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
})
})
describe('no current user', () => {
beforeEach(() => {
getters = {
'auth/user': () => {
return null
},
}
wrapper = Wrapper()
wrapper.find('.locale-menu').trigger('click')
deutschLanguageItem = wrapper.findAll('li').at(1)
deutschLanguageItem.trigger('click')
})
it('does not send a UpdateUser mutation', () => {
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
}) })
}) })

View File

@ -33,10 +33,12 @@
</template> </template>
<script> <script>
import gql from 'graphql-tag'
import Dropdown from '~/components/Dropdown' import Dropdown from '~/components/Dropdown'
import find from 'lodash/find' import find from 'lodash/find'
import orderBy from 'lodash/orderBy' import orderBy from 'lodash/orderBy'
import locales from '~/locales' import locales from '~/locales'
import { mapGetters, mapMutations } from 'vuex'
export default { export default {
components: { components: {
@ -64,15 +66,52 @@ export default {
}) })
return routes return routes
}, },
...mapGetters({
currentUser: 'auth/user',
}),
}, },
methods: { methods: {
changeLanguage(locale, toggleMenu) { changeLanguage(locale, toggleMenu) {
this.$i18n.set(locale) this.$i18n.set(locale)
this.updateUserLocale()
toggleMenu() toggleMenu()
}, },
matcher(locale) { matcher(locale) {
return locale === this.$i18n.locale() return locale === this.$i18n.locale()
}, },
...mapMutations({
setCurrentUser: 'auth/SET_USER',
}),
async updateUserLocale() {
if (!this.currentUser || !this.currentUser.id) return null
try {
await this.$apollo.mutate({
mutation: gql`
mutation($id: ID!, $locale: String) {
UpdateUser(id: $id, locale: $locale) {
id
locale
}
}
`,
variables: {
id: this.currentUser.id,
locale: this.$i18n.locale(),
},
update: (store, { data: { UpdateUser } }) => {
const { locale } = UpdateUser
this.setCurrentUser({
...this.currentUser,
locale,
})
},
})
this.$toast.success(this.$t('contribution.success'))
} catch (err) {
this.$toast.error(err.message)
}
},
}, },
} }
</script> </script>

View File

@ -0,0 +1,75 @@
import { storiesOf } from '@storybook/vue'
import { withA11y } from '@storybook/addon-a11y'
import { action } from '@storybook/addon-actions'
import Vuex from 'vuex'
import helpers from '~/storybook/helpers'
import LoginForm from './LoginForm.vue'
helpers.init()
const createStore = ({ loginSuccess }) => {
return new Vuex.Store({
modules: {
auth: {
namespaced: true,
state: () => ({
pending: false,
}),
mutations: {
SET_PENDING(state, pending) {
state.pending = pending
},
},
getters: {
pending(state) {
return !!state.pending
},
},
actions: {
async login({ commit, dispatch }, args) {
action('Vuex action `auth/login`')(args)
return new Promise((resolve, reject) => {
commit('SET_PENDING', true)
setTimeout(() => {
commit('SET_PENDING', false)
if (loginSuccess) {
resolve(loginSuccess)
} else {
reject(new Error('Login unsuccessful'))
}
}, 1000)
})
},
},
},
},
})
}
storiesOf('LoginForm', module)
.addDecorator(withA11y)
.addDecorator(helpers.layout)
.add('successful login', () => {
return {
components: { LoginForm },
store: createStore({ loginSuccess: true }),
methods: {
handleSuccess() {
action('Login successful!')()
},
},
template: `<login-form @success="handleSuccess"/>`,
}
})
.add('unsuccessful login', () => {
return {
components: { LoginForm },
store: createStore({ loginSuccess: false }),
methods: {
handleSuccess() {
action('Login successful!')()
},
},
template: `<login-form @success="handleSuccess"/>`,
}
})

View File

@ -0,0 +1,121 @@
<template>
<ds-container width="medium">
<ds-space margin="small">
<blockquote>
<p>{{ $t('quotes.african.quote') }}</p>
<b>- {{ $t('quotes.african.author') }}</b>
</blockquote>
</ds-space>
<ds-card class="login-card">
<ds-flex gutter="small">
<ds-flex-item :width="{ base: '100%', sm: '50%' }" centered>
<client-only>
<locale-switch class="login-locale-switch" offset="5" />
</client-only>
<ds-space margin-top="small" margin-bottom="xxx-small" centered>
<img
class="login-image"
alt="Human Connection"
src="/img/sign-up/humanconnection.svg"
/>
</ds-space>
</ds-flex-item>
<ds-flex-item :width="{ base: '100%', sm: '50%' }" centered>
<ds-space margin="small">
<a :href="$t('login.moreInfoURL')" :title="$t('login.moreInfoHint')" target="_blank">
{{ $t('login.moreInfo') }}
</a>
</ds-space>
<ds-space margin="small">
<ds-text size="small">{{ $t('login.copy') }}</ds-text>
</ds-space>
<form :disabled="pending" @submit.prevent="onSubmit">
<ds-input
v-model="form.email"
:disabled="pending"
:placeholder="$t('login.email')"
type="email"
name="email"
icon="envelope"
/>
<ds-input
v-model="form.password"
:disabled="pending"
:placeholder="$t('login.password')"
icon="lock"
icon-right="question-circle"
name="password"
type="password"
/>
<ds-space margin-bottom="large">
<nuxt-link to="/password-reset/request">{{ $t('login.forgotPassword') }}</nuxt-link>
</ds-space>
<ds-button
:loading="pending"
primary
fullwidth
name="submit"
type="submit"
icon="sign-in"
>
{{ $t('login.login') }}
</ds-button>
<ds-space margin-top="large" margin-bottom="x-small">
{{ $t('login.no-account') }}
<nuxt-link to="/registration/signup">{{ $t('login.register') }}</nuxt-link>
</ds-space>
</form>
</ds-flex-item>
</ds-flex>
</ds-card>
</ds-container>
</template>
<script>
import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch.vue'
export default {
components: {
LocaleSwitch,
},
data() {
return {
form: {
email: '',
password: '',
},
}
},
computed: {
pending() {
return this.$store.getters['auth/pending']
},
},
methods: {
async onSubmit() {
try {
await this.$store.dispatch('auth/login', { ...this.form })
this.$toast.success(this.$t('login.success'))
this.$emit('success')
} catch (err) {
this.$toast.error(this.$t('login.failure'))
}
},
},
}
</script>
<style lang="scss">
.login-image {
width: 90%;
max-width: 200px;
}
.login-card {
position: relative;
}
.login-locale-switch {
position: absolute;
top: 1em;
left: 1em;
}
</style>

View File

@ -14,6 +14,10 @@
{{ $t('site.data-privacy') }} {{ $t('site.data-privacy') }}
</a> </a>
<span>-</span> <span>-</span>
<a href="https://faq.human-connection.org/" target="_blank">
{{ $t('site.faq') }}
</a>
<span>-</span>
<a href="https://github.com/Human-Connection/Human-Connection/releases" target="_blank"> <a href="https://github.com/Human-Connection/Human-Connection/releases" target="_blank">
{{ $t('site.changelog') }} {{ $t('site.changelog') }}
</a> </a>

View File

@ -2,7 +2,7 @@ import { config, shallowMount, mount, createLocalVue, RouterLinkStub } from '@vu
import Styleguide from '@human-connection/styleguide' import Styleguide from '@human-connection/styleguide'
import Vuex from 'vuex' import Vuex from 'vuex'
import Filters from '~/plugins/vue-filters' import Filters from '~/plugins/vue-filters'
import PostCard from '.' import PostCard from './PostCard.vue'
const localVue = createLocalVue() const localVue = createLocalVue()

View File

@ -1,6 +1,6 @@
import { storiesOf } from '@storybook/vue' import { storiesOf } from '@storybook/vue'
import { withA11y } from '@storybook/addon-a11y' import { withA11y } from '@storybook/addon-a11y'
import HcPostCard from '~/components/PostCard' import HcPostCard from './PostCard.vue'
import helpers from '~/storybook/helpers' import helpers from '~/storybook/helpers'
helpers.init() helpers.init()
@ -76,3 +76,23 @@ storiesOf('Post Card', module)
/> />
`, `,
})) }))
.add('pinned by admin', () => ({
components: { HcPostCard },
store: helpers.store,
data: () => ({
post: {
...post,
pinnedBy: {
id: '4711',
name: 'Ad Min',
role: 'admin',
},
},
}),
template: `
<hc-post-card
:post="post"
:width="{ base: '100%', xs: '100%', md: '50%', xl: '33%' }"
/>
`,
}))

View File

@ -1,7 +1,7 @@
<template> <template>
<ds-card <ds-card
:image="post.image | proxyApiUrl" :image="post.image | proxyApiUrl"
:class="{ 'post-card': true, 'disabled-content': post.disabled }" :class="{ 'post-card': true, 'disabled-content': post.disabled, 'post--pinned': isPinned }"
> >
<!-- Post Link Target --> <!-- Post Link Target -->
<nuxt-link <nuxt-link
@ -16,7 +16,8 @@
<client-only> <client-only>
<hc-user :user="post.author" :trunc="35" :date-time="post.createdAt" /> <hc-user :user="post.author" :trunc="35" :date-time="post.createdAt" />
</client-only> </client-only>
<hc-ribbon :text="$t('post.name')" /> <hc-ribbon v-if="isPinned" class="ribbon--pinned" :text="$t('post.pinned')" />
<hc-ribbon v-else :text="$t('post.name')" />
</div> </div>
<ds-space margin-bottom="small" /> <ds-space margin-bottom="small" />
<!-- Post Title --> <!-- Post Title -->
@ -61,6 +62,8 @@
:resource="post" :resource="post"
:modalsData="menuModalsData" :modalsData="menuModalsData"
:is-owner="isAuthor" :is-owner="isAuthor"
@pinPost="pinPost"
@unpinPost="unpinPost"
/> />
</div> </div>
</client-only> </client-only>
@ -114,6 +117,9 @@ export default {
this.deletePostCallback, this.deletePostCallback,
) )
}, },
isPinned() {
return this.post && this.post.pinnedBy
},
}, },
methods: { methods: {
async deletePostCallback() { async deletePostCallback() {
@ -127,6 +133,12 @@ export default {
this.$toast.error(err.message) this.$toast.error(err.message)
} }
}, },
pinPost(post) {
this.$emit('pinPost', post)
},
unpinPost(post) {
this.$emit('unpinPost', post)
},
}, },
} }
</script> </script>
@ -167,4 +179,8 @@ export default {
text-indent: -999999px; text-indent: -999999px;
} }
} }
.post--pinned {
border: 1px solid $color-warning;
}
</style> </style>

View File

@ -1,4 +1,5 @@
import { config, mount, createLocalVue } from '@vue/test-utils' import { config, mount, createLocalVue } from '@vue/test-utils'
import { VERSION } from '~/constants/terms-and-conditions-version.js'
import CreateUserAccount from './CreateUserAccount' import CreateUserAccount from './CreateUserAccount'
import { SignupVerificationMutation } from '~/graphql/Registration.js' import { SignupVerificationMutation } from '~/graphql/Registration.js'
import Styleguide from '@human-connection/styleguide' import Styleguide from '@human-connection/styleguide'
@ -24,6 +25,9 @@ describe('CreateUserAccount', () => {
loading: false, loading: false,
mutate: jest.fn(), mutate: jest.fn(),
}, },
$i18n: {
locale: () => 'en',
},
} }
propsData = {} propsData = {}
stubs = { stubs = {
@ -69,12 +73,6 @@ describe('CreateUserAccount', () => {
} }
}) })
it('calls CreateUserAccount graphql mutation', async () => {
await action()
const expected = expect.objectContaining({ mutation: SignupVerificationMutation })
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
})
it('delivers data to backend', async () => { it('delivers data to backend', async () => {
await action() await action()
const expected = expect.objectContaining({ const expected = expect.objectContaining({
@ -84,12 +82,19 @@ describe('CreateUserAccount', () => {
email: 'sixseven@example.org', email: 'sixseven@example.org',
nonce: '666777', nonce: '666777',
password: 'hellopassword', password: 'hellopassword',
termsAndConditionsAgreedVersion: '0.0.2', termsAndConditionsAgreedVersion: VERSION,
locale: 'en',
}, },
}) })
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected) expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
}) })
it('calls CreateUserAccount graphql mutation', async () => {
await action()
const expected = expect.objectContaining({ mutation: SignupVerificationMutation })
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
})
describe('in case mutation resolves', () => { describe('in case mutation resolves', () => {
beforeEach(() => { beforeEach(() => {
mocks.$apollo.mutate = jest.fn().mockResolvedValue({ mocks.$apollo.mutate = jest.fn().mockResolvedValue({

View File

@ -70,22 +70,24 @@
:checked="termsAndConditionsConfirmed" :checked="termsAndConditionsConfirmed"
/> />
<label <label
for="checkbox" for="checkbox0"
v-html="$t('termsAndConditions.termsAndConditionsConfirmed')" v-html="$t('termsAndConditions.termsAndConditionsConfirmed')"
></label> ></label>
</ds-text> </ds-text>
<p> <ds-text>
<label> <input id="checkbox1" type="checkbox" v-model="dataPrivacy" :checked="dataPrivacy" />
<input id="checkbox1" type="checkbox" v-model="dataPrivacy" :checked="dataPrivacy" /> <label
<span v-html="$t('components.registration.signup.form.data-privacy')"></span> for="checkbox1"
</label> v-html="$t('components.registration.signup.form.data-privacy')"
</p> ></label>
<p> </ds-text>
<label> <ds-text>
<input id="checkbox2" type="checkbox" v-model="minimumAge" :checked="minimumAge" /> <input id="checkbox2" type="checkbox" v-model="minimumAge" :checked="minimumAge" />
<span v-html="$t('components.registration.signup.form.minimum-age')"></span> <label
</label> for="checkbox2"
</p> v-html="$t('components.registration.signup.form.minimum-age')"
></label>
</ds-text>
<ds-button <ds-button
style="float: right;" style="float: right;"
icon="check" icon="check"
@ -154,10 +156,19 @@ export default {
const { name, password, about } = this.formData const { name, password, about } = this.formData
const { email, nonce } = this const { email, nonce } = this
const termsAndConditionsAgreedVersion = VERSION const termsAndConditionsAgreedVersion = VERSION
const locale = this.$i18n.locale()
try { try {
await this.$apollo.mutate({ await this.$apollo.mutate({
mutation: SignupVerificationMutation, mutation: SignupVerificationMutation,
variables: { name, password, about, email, nonce, termsAndConditionsAgreedVersion }, variables: {
name,
password,
about,
email,
nonce,
termsAndConditionsAgreedVersion,
locale,
},
}) })
this.response = 'success' this.response = 'success'
setTimeout(() => { setTimeout(() => {

View File

@ -46,4 +46,12 @@ export default {
border-color: $background-color-secondary transparent transparent $background-color-secondary; border-color: $background-color-secondary transparent transparent $background-color-secondary;
} }
} }
.ribbon--pinned {
background-color: $color-warning-active;
&::before {
border-color: $color-warning transparent transparent $color-warning;
}
}
</style> </style>

View File

@ -67,7 +67,7 @@ export default {
<div data-dz-thumbnail-bg></div> <div data-dz-thumbnail-bg></div>
</div> </div>
</div> </div>
` `
}, },
verror(file, message) { verror(file, message) {
this.error = true this.error = true
@ -113,7 +113,7 @@ export default {
image.src = URL.createObjectURL(file) image.src = URL.createObjectURL(file)
editor.appendChild(image) editor.appendChild(image)
// Create Cropper.js and pass image // Create Cropper.js and pass image
let cropper = new Cropper(image, { zoomable: false }) let cropper = new Cropper(image, { zoomable: false, autoCropArea: 1.0 })
}, },
dropzoneDrop() { dropzoneDrop() {
this.showCropper = true this.showCropper = true

View File

@ -78,9 +78,7 @@ export default {
query() { query() {
return notificationQuery(this.$i18n) return notificationQuery(this.$i18n)
}, },
pollInterval() { pollInterval: NOTIFICATIONS_POLL_INTERVAL,
return NOTIFICATIONS_POLL_INTERVAL
},
update(data) { update(data) {
const newNotifications = data.notifications.filter(newN => { const newNotifications = data.notifications.filter(newN => {
return !this.displayedNotifications.find(oldN => this.equalNotification(newN, oldN)) return !this.displayedNotifications.find(oldN => this.equalNotification(newN, oldN))
@ -93,7 +91,7 @@ export default {
return data.notifications return data.notifications
}, },
error(error) { error(error) {
this.$toast.error(error) this.$toast.error(error.message)
}, },
}, },
}, },

View File

@ -1 +1,2 @@
export const VERSION = '0.0.2' // please change also version in file "cypress/constants/terms-and-conditions-version.js"
export const VERSION = '0.0.3'

View File

@ -57,6 +57,12 @@ export const postFragment = lang => gql`
name name
icon icon
} }
pinnedBy {
id
name
role
}
pinnedAt
} }
` `
export const commentFragment = lang => gql` export const commentFragment = lang => gql`

View File

@ -50,6 +50,11 @@ export default () => {
content content
contentExcerpt contentExcerpt
language language
pinnedBy {
id
name
role
}
} }
} }
`, `,
@ -86,5 +91,39 @@ export default () => {
} }
} }
`, `,
pinPost: gql`
mutation($id: ID!) {
pinPost(id: $id) {
id
title
slug
content
contentExcerpt
language
pinnedBy {
id
name
role
}
}
}
`,
unpinPost: gql`
mutation($id: ID!) {
unpinPost(id: $id) {
id
title
slug
content
contentExcerpt
language
pinnedBy {
id
name
role
}
}
}
`,
} }
} }

View File

@ -35,6 +35,26 @@ export const filterPosts = i18n => {
` `
} }
export const profilePagePosts = i18n => {
const lang = i18n.locale().toUpperCase()
return gql`
${postFragment(lang)}
${postCountsFragment}
query profilePagePosts(
$filter: _PostFilter
$first: Int
$offset: Int
$orderBy: [_PostOrdering]
) {
profilePagePosts(filter: $filter, first: $first, offset: $offset, orderBy: $orderBy) {
...post
...postCounts
}
}
`
}
export const PostsEmotionsByCurrentUser = () => { export const PostsEmotionsByCurrentUser = () => {
return gql` return gql`
query PostsEmotionsByCurrentUser($postId: ID!) { query PostsEmotionsByCurrentUser($postId: ID!) {

View File

@ -7,6 +7,7 @@ export const SignupVerificationMutation = gql`
$password: String! $password: String!
$about: String $about: String
$termsAndConditionsAgreedVersion: String! $termsAndConditionsAgreedVersion: String!
$locale: String
) { ) {
SignupVerification( SignupVerification(
nonce: $nonce nonce: $nonce
@ -15,6 +16,7 @@ export const SignupVerificationMutation = gql`
password: $password password: $password
about: $about about: $about
termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
locale: $locale
) { ) {
id id
name name

View File

@ -153,3 +153,14 @@ export const checkSlugAvailableQuery = gql`
} }
} }
` `
export const localeMutation = () => {
return gql`
mutation($id: ID!, $locale: String) {
UpdateUser(id: $id, locale: $locale) {
id
locale
}
}
`
}

View File

@ -50,6 +50,18 @@
} }
} }
}, },
"store": {
"posts": {
"orderBy": {
"newest": {
"label": "Neueste"
},
"oldest": {
"label": "Älteste"
}
}
}
},
"maintenance": { "maintenance": {
"title": "Human Connection befindet sich in der Wartung", "title": "Human Connection befindet sich in der Wartung",
"explanation": "Zurzeit führen wir einige geplante Wartungsarbeiten durch, bitte versuch es später erneut.", "explanation": "Zurzeit führen wir einige geplante Wartungsarbeiten durch, bitte versuch es später erneut.",
@ -83,7 +95,7 @@
"imprint": "Impressum", "imprint": "Impressum",
"data-privacy": "Datenschutz", "data-privacy": "Datenschutz",
"termsAndConditions": "Nutzungsbedingungen", "termsAndConditions": "Nutzungsbedingungen",
"changelog": "Änderungen & Verlauf", "changelog": "Änderungen",
"contact": "Kontakt", "contact": "Kontakt",
"tribunal": "Registergericht", "tribunal": "Registergericht",
"register": "Registernummer", "register": "Registernummer",
@ -93,11 +105,8 @@
"bank": "Bankverbindung", "bank": "Bankverbindung",
"germany": "Deutschland", "germany": "Deutschland",
"code-of-conduct": "Verhaltenscodex", "code-of-conduct": "Verhaltenscodex",
"back-to-login": "Zurück zur Anmeldung" "back-to-login": "Zurück zur Anmeldung",
}, "faq": "FAQ"
"sorting": {
"newest": "Neueste",
"oldest": "Älteste"
}, },
"login": { "login": {
"copy": "Wenn Du bereits ein Konto bei Human Connection hast, melde Dich bitte hier an.", "copy": "Wenn Du bereits ein Konto bei Human Connection hast, melde Dich bitte hier an.",
@ -112,7 +121,8 @@
"moreInfoURL": "https://human-connection.org", "moreInfoURL": "https://human-connection.org",
"moreInfoHint": "zur Präsentationsseite", "moreInfoHint": "zur Präsentationsseite",
"hello": "Hallo", "hello": "Hallo",
"success": "Du bist eingeloggt!" "success": "Du bist eingeloggt!",
"failure": "Fehlerhafte E-Mail-Adresse oder Passwort."
}, },
"editor": { "editor": {
"placeholder": "Schreib etwas Inspirierendes …", "placeholder": "Schreib etwas Inspirierendes …",
@ -354,6 +364,7 @@
}, },
"post": { "post": {
"name": "Beitrag", "name": "Beitrag",
"pinned": "Meldung",
"moreInfo": { "moreInfo": {
"name": "Mehr Info", "name": "Mehr Info",
"title": "Mehr Informationen", "title": "Mehr Informationen",
@ -367,7 +378,11 @@
}, },
"menu": { "menu": {
"edit": "Beitrag bearbeiten", "edit": "Beitrag bearbeiten",
"delete": "Beitrag löschen" "delete": "Beitrag löschen",
"pin": "Post festpinnen",
"pinnedSuccessfully": "Post erfolgreich festgepinnt!",
"unpin": "Post nicht mehr festpinnen",
"unpinnedSuccessfully": "Post erfolgreich nicht mehr festgepinnt!"
}, },
"comment": { "comment": {
"submit": "Kommentiere", "submit": "Kommentiere",
@ -652,39 +667,37 @@
"termsAndConditionsNewConfirmText": "Bitte lies dir die neue Nutzungsbedingungen jetzt durch!", "termsAndConditionsNewConfirmText": "Bitte lies dir die neue Nutzungsbedingungen jetzt durch!",
"termsAndConditionsNewConfirm": "Ich habe die neuen Nutzungsbedingungen durchgelesen und stimme zu.", "termsAndConditionsNewConfirm": "Ich habe die neuen Nutzungsbedingungen durchgelesen und stimme zu.",
"agree": "Ich stimme zu!", "agree": "Ich stimme zu!",
"risk": { "terms-of-service": {
"title": "Unfallgefahr", "title": "Nutzungsbedingungen",
"description": "Das ist eine Testversion! Alle Daten, Dein Profil und die Server können jederzeit komplett vernichtet, verloren, verbrannt und vielleicht auch in der Nähe von Alpha Centauri synchronisiert werden. Die Benutzung läuft auf eigene Gefahr. Mit kommerziellen Nebenwirkungen ist jedoch nicht zu rechnen." "description": "Die folgenden Nutzungsbedingungen sind Basis für die Nutzung unseres Netzwerkes. Beim Registrieren musst Du sie anerkennen und wir werden Dich auch später über ggf. stattfindende Änderungen informieren. Das Human Connection Netzwerk wird in Deutschland betrieben und unterliegt daher deutschem Recht. Gerichtsstand ist Kirchheim / Teck. Zu Details schau in unser Impressum: <a href=\"https://human-connection.org/impressum\" target=\"_blank\" >https://human-connection.org/impressum</a> "
},
"use-and-license" : {
"title": "Nutzung und Lizenz",
"description": "Sind Inhalte, die Du bei uns einstellst, durch Rechte am geistigen Eigentum geschützt, erteilst Du uns eine nicht-exklusive, übertragbare, unterlizenzierbare und weltweite Lizenz für die Nutzung dieser Inhalte für die Bereitstellung in unserem Netzwerk. Diese Lizenz endet, sobald Du Deine Inhalte oder Deinen ganzen Account löscht. Bedenke, dass andere Deine Inhalte weiter teilen können und wir diese nicht löschen können."
}, },
"data-privacy": { "privacy-statement" : {
"title": "Du und deine Daten", "title": "Datenschutz",
"description": "Bitte beachte, dass wir die Inhalte der Alphaversion zu Werbezwecken, Webpräsentationen usw. verwenden, aber wir glauben, dass das auch in Deinem Interesse ist. Am besten keinen Nachnamen eingeben und bei noch mehr Datensparsamkeit ein Profilfoto ohne Identität verwenden. Mehr in unserer <a href=\"https://human-connection.org/datenschutz/\" target=\"_blank\" >Datenschutzerklärung</a>" "description": " Unser Netzwerk ist ein soziales Wissens- und Aktionsnetzwerk. Daher ist es uns besonders wichtig, dass möglichst viele Inhalte öffentlich zugänglich sind. Im Laufe der Entwicklung unseres Netzwerkes wird es mehr und mehr die Möglichkeit geben, über die Sichtbarkeit der selbst angegebenen bzw. persönlichen Daten zu entscheiden. Über diese neuen Features werden wir informieren. Ansonsten gilt, dass Du immer darüber nachdenken solltest, welche persönlichen Daten Du über Dich (oder andere) preisgibst. Dies gilt insbesondere für Inhalte von Posts und Kommentaren, da diese einen weitestgehend öffentlichen Charakter haben. Bei den Profilangaben wird es später Möglichkeiten geben, die Sichtbarkeit selbst einzuschränken. Teil der Nutzungsbedingungen ist unsere Datenschutzerklärung, die Dich über die einzelnen Datenverarbeitungen in unserem Netzwerk informiert: <a href=\"https://human-connection.org/datenschutz/#netzwerk\" target=\"_blank\"> https://human-connection.org/datenschutz/#netzwerk</a> bzw. <a href=\"https://human-connection.org/datenschutz/\" target=\"_blank\">https://human-connection.org/datenschutz/</a> Unsere Datenschutzerklärung wird je nach Gesetzeslage und Eigenschaften unseres Netzwerkes angepasst und ist jeweils in der aktuellen Version gültig."
}, },
"work-in-progress": { "code-of-conduct" : {
"title": "Baustellen",
"description": "Das ist immer noch eine Testversion. Wenn etwas nicht funktioniert, blockiert, irritiert, falsch angezeigt, verbogen oder nicht anklickbar ist, bitten wir dies zu entschuldigen. Fehler, Käfer und Bugs bitte melden! <a href=\"mailto:support@human-connection.org\" target=\"_blank\">support@human-connection.org</a>"
},
"code-of-conduct": {
"title": "Verhaltenscodex", "title": "Verhaltenscodex",
"description": "<a href=\"/code-of-conduct\">Die Verhaltensregeln </a> dienen als Leitsätze für den persönlichen Auftritt und den Umgang untereinander. Wer als Nutzer im Human Connection Netzwerk aktiv ist, Beiträge verfasst, kommentiert oder mit anderen Nutzern, auch außerhalb des Netzwerkes, Kontakt aufnimmt, erkennt diese Verhaltensregeln als verbindlich an" "description": "Unser Verhaltenscodex dient als Handreiche für den persönlichen Auftritt und den Umgang untereinander. Wer als Nutzer im Human Connection Netzwerk aktiv ist, Beiträge verfasst, kommentiert oder mit anderen Nutzern, auch außerhalb des Netzwerkes, Kontakt aufnimmt, erkennt diese Verhaltensregeln als verbindlich an. <a href=\"https://alpha.human-connection.org/code-of-conduct\" target=\"_blank\"> https://alpha.human-connection.org/code-of-conduct</a>"
}, },
"moderation": { "moderation" : {
"title": "Moderation", "title": "Moderation",
"description": "Solange kein Community-Moderationssystem lauffähig ist, entscheidet ein Regenbogen-Einhorn darüber, ob Du körperlich und psychisch stabil genug bist, unsere Testversion zu bedienen. Das Einhorn kann Dich jederzeit von der Alpha entfernen. Also sei nett und lass Regenbogenfutter da!" "description": "Bis unsere finanziellen Möglichkeiten uns erlauben, das Community-Moderationssystem zu implementieren, moderieren wir mit einem vereinfachten System und eigenen bzw. ggf. ehrenamtlichen Mitarbeitern. Wir schulen diese Moderatoren und aus diesem Grund treffen auch nur diese entsprechende Entscheidungen. Diese Moderatoren führen Ihre Tätigkeit anonym aus. Du kannst uns Beiträge, Kommentare und auch Nutzer melden (wenn diese zum Beispiel in ihrem Profil Angaben machen oder Bilder haben, die diese Nutzungsbedingungen verletzen). Wenn Du uns etwas meldest, kannst Du einen Meldegrund angeben und noch einen kurze Erläuterung mitgeben. Wir schauen uns dann das Gemeldete an und sanktionieren ggf., z.B. indem wir Beiträge, Kommentare oder Nutzer sperren. Du und auch der Betroffene erhält zum jetzigen Zeitpunkt von uns leider noch keine Rückmeldung, das ist aber in Planung. Unabhängig davon behalten wir uns prinzipiell Sanktionen vor aus Gründen, die unter Umständen nicht oder noch nicht in unserem Verhaltenscodex oder diesen Nutzungsbedingungen aufgeführt sind."
}, },
"fairness": { "errors-and-feedback" : {
"title": "Fairness", "title": " Fehler und Feedback",
"description": "Sollte Dir die Alphaversion unseres Netzwerks wider Erwarten, egal aus welchen Gründen, nicht gefallen, überweisen wir Dir Deine gespendeten Monatsbeiträge innerhalb der ersten 2 Monate gerne zurück. Einfach Mail an: <a href=\"mailto:info@human-connection.org\" target=\"_blank\" s> info@human-connection.org </a> Achtung: Viele Funktionen werden erst nach und nach eingebaut." "description": "Wir geben uns größte Mühe, unser Netzwerk und die Daten sicher und verfügbar zu halten. Jedes neue Release der Software durchläuft sowohl automatisierte und händische Tests. Trotzdem kann es passieren, dass unvorhergesehene Fehler auftreten. Daher sind wir dankbar für jeden gemeldeten Fehler. Du kannst von Dir entdeckte Fehler gerne per E-Mail an den Support mitteilen: support@human-connection.org"
}, },
"questions": { "help-and-questions" : {
"title": "Fragen", "title": "Hilfe und Fragen",
"description": "Die Termine und Links zu den Zoom-Räumen findest Du hier: <a href=\"https://human-connection.org/events-und-news/\" target=\"_blank\" >https://human-connection.org/veranstaltungen/ </a>" "description": "Für Hilfe und Fragen haben wir für Dich eine umfassende Sammlung an immer wieder gestellten Fragen bzw. Antworten (FAQ) zusammengestellt. Du findest diese hier: <a href=\"https://support.human-connection.org/kb/\" target=\"_blank\" > https://support.human-connection.org/kb/ </a>"
}, },
"human-connection": { "addition" : {
"title": "Von Menschen für Menschen:", "title": "Zusätzliche machen wir regelmäßig Veranstaltungen, wo Du auch Eindrücke wiedergeben und Fragen stellen kannst. Du findest eine aktuelle Übersicht hier:",
"description": "Bitte hilf uns weitere monatlichen Spender für Human Connection zu bekommen, damit das Netzwerk so schnell wie möglich offiziell an den Start gehen kann. <a href=\"https://human-connection.org/\" target=\"_blank\"> https://human-connection.org </a>" "description": "<a href=\"https://human-connection.org/veranstaltungen/\" target=\"_blank\" > https://human-connection.org/veranstaltungen/ </a>"
}, }
"have-fun": "Jetzt aber viel Spaß mit der Alpha von Human Connection! Für den ersten Weltfrieden. ♥︎",
"closing": "Herzlichst <br><br> Euer Human Connection Team"
} }
} }

View File

@ -4,9 +4,9 @@
"request": { "request": {
"title": "Reset your password", "title": "Reset your password",
"form": { "form": {
"description": "A password reset email will be sent to the given email address.", "description": "A password reset e-mail will be sent to the given e-mail address.",
"submit": "Request email", "submit": "Request e-mail",
"submitted": "An email with further instructions has been sent to <b>{email}</b>" "submitted": "An e-mail with further instructions has been sent to <b>{email}</b>"
} }
}, },
"change-password": { "change-password": {
@ -30,13 +30,13 @@
"unavailable": "Unfortunately, public registration of user accounts is not available right now on this server.", "unavailable": "Unfortunately, public registration of user accounts is not available right now on this server.",
"title": "Join Human Connection!", "title": "Join Human Connection!",
"form": { "form": {
"description": "To get started, enter your email address:", "description": "To get started, enter your e-mail address:",
"terms-and-condition": "I confirm to the <a href=\"/terms-and-conditions\"><ds-text bold color=\"primary\" > Terms and conditions</ds-text></a>.", "terms-and-condition": "I confirm to the <a href=\"/terms-and-conditions\"><ds-text bold color=\"primary\" > Terms and conditions</ds-text></a>.",
"data-privacy": " I have read and understood the <a href=\"https://human-connection.org/datenschutz/\" target=\"_blank\"><ds-text bold color=\"primary\" >Privacy Statement</ds-text></a> ", "data-privacy": " I have read and understood the <a href=\"https://human-connection.org/datenschutz/\" target=\"_blank\"><ds-text bold color=\"primary\" >Privacy Statement</ds-text></a> ",
"minimum-age": "I'm 18 years or older.", "minimum-age": "I'm 18 years or older.",
"invitation-code": "Your invitation code is: <b>{code}</b>", "invitation-code": "Your invitation code is: <b>{code}</b>",
"errors": { "errors": {
"email-exists": "There is already a user account with this email address!", "email-exists": "There is already a user account with this e-mail address!",
"invalid-invitation-token": "It looks like as if the invitation has been used already. Invitation links can only be used once." "invalid-invitation-token": "It looks like as if the invitation has been used already. Invitation links can only be used once."
}, },
"submit": "Create an account", "submit": "Create an account",
@ -51,10 +51,22 @@
} }
} }
}, },
"store": {
"posts": {
"orderBy": {
"newest": {
"label": "Newest"
},
"oldest": {
"label": "Oldest"
}
}
}
},
"maintenance": { "maintenance": {
"title": "Human Connection is under maintenance", "title": "Human Connection is under maintenance",
"explanation": "At the moment we are doing some scheduled maintenance, please try again later.", "explanation": "At the moment we are doing some scheduled maintenance, please try again later.",
"questions": "Any Questions or concerns, send an email to" "questions": "Any Questions or concerns, send an e-mail to"
}, },
"index": { "index": {
"no-results": "No contributions found.", "no-results": "No contributions found.",
@ -84,7 +96,7 @@
"imprint": "Imprint", "imprint": "Imprint",
"termsAndConditions": "Terms and conditions", "termsAndConditions": "Terms and conditions",
"data-privacy": "Data privacy", "data-privacy": "Data privacy",
"changelog": "Changes & History", "changelog": "Changes",
"contact": "Contact", "contact": "Contact",
"tribunal": "Registry court", "tribunal": "Registry court",
"register": "Registry number", "register": "Registry number",
@ -94,17 +106,14 @@
"bank": "bank account", "bank": "bank account",
"germany": "Germany", "germany": "Germany",
"code-of-conduct": "Code of Conduct", "code-of-conduct": "Code of Conduct",
"back-to-login": "Back to login page" "back-to-login": "Back to login page",
}, "faq": "FAQ"
"sorting": {
"newest": "Newest",
"oldest": "Oldest"
}, },
"login": { "login": {
"copy": "If you already have a human-connection account, please login.", "copy": "If you already have a human-connection account, please login.",
"login": "Login", "login": "Login",
"logout": "Logout", "logout": "Logout",
"email": "Your Email", "email": "Your E-mail",
"password": "Your Password", "password": "Your Password",
"forgotPassword": "Forgot Password?", "forgotPassword": "Forgot Password?",
"no-account": "Don't have an account?", "no-account": "Don't have an account?",
@ -113,7 +122,8 @@
"moreInfoURL": "https://human-connection.org/en/", "moreInfoURL": "https://human-connection.org/en/",
"moreInfoHint": "to the presentation page", "moreInfoHint": "to the presentation page",
"hello": "Hello", "hello": "Hello",
"success": "You are logged in!" "success": "You are logged in!",
"failure": "Incorrect email address or password."
}, },
"editor": { "editor": {
"placeholder": "Leave your inspirational thoughts …", "placeholder": "Leave your inspirational thoughts …",
@ -152,8 +162,8 @@
}, },
"invites": { "invites": {
"title": "Invite somebody to Human Connection!", "title": "Invite somebody to Human Connection!",
"description": "Enter thier email address for invitation.", "description": "Enter thier e-mail address for invitation.",
"emailPlaceholder": "Email to invite" "emailPlaceholder": "E-mail to invite"
} }
}, },
"notifications": { "notifications": {
@ -182,23 +192,23 @@
}, },
"email": { "email": {
"validation": { "validation": {
"same-email": "This is your current email address" "same-email": "This is your current e-mail address"
}, },
"name": "Your email", "name": "Your e-mail",
"labelEmail": "Change your email address", "labelEmail": "Change your e-mail address",
"labelNewEmail": "New email Address", "labelNewEmail": "New e-mail Address",
"labelNonce": "Enter your code", "labelNonce": "Enter your code",
"success": "A new email address has been registered.", "success": "A new e-mail address has been registered.",
"submitted": "An email to verify your address has been sent to <b>{email}</b>.", "submitted": "An e-mail to verify your address has been sent to <b>{email}</b>.",
"change-successful": "Your email address has been changed successfully.", "change-successful": "Your e-mail address has been changed successfully.",
"verification-error": { "verification-error": {
"message": "Your email could not be changed.", "message": "Your e-mail could not be changed.",
"explanation": "This can have different causes:", "explanation": "This can have different causes:",
"reason": { "reason": {
"invalid-nonce": "Is the confirmation code invalid?", "invalid-nonce": "Is the confirmation code invalid?",
"no-email-request": "Are you certain that you requested a change of your email address?" "no-email-request": "Are you certain that you requested a change of your e-mail address?"
}, },
"support": "If the problem persists, please contact us by email at" "support": "If the problem persists, please contact us by e-mail at"
} }
}, },
"validation": { "validation": {
@ -312,7 +322,7 @@
"users": { "users": {
"name": "Users", "name": "Users",
"form": { "form": {
"placeholder": "email, name or description" "placeholder": "e-mail, name or description"
}, },
"table": { "table": {
"columns": { "columns": {
@ -355,6 +365,7 @@
}, },
"post": { "post": {
"name": "Post", "name": "Post",
"pinned": "Announcement",
"moreInfo": { "moreInfo": {
"name": "More info", "name": "More info",
"title": "More information", "title": "More information",
@ -368,7 +379,11 @@
}, },
"menu": { "menu": {
"edit": "Edit Post", "edit": "Edit Post",
"delete": "Delete Post" "delete": "Delete Post",
"pin": "Pin post",
"pinnedSuccessfully": "Post pinned successfully!",
"unpin": "Unpin post",
"unpinnedSuccessfully": "Post unpinned successfully!"
}, },
"comment": { "comment": {
"submit": "Comment", "submit": "Comment",
@ -415,7 +430,7 @@
"loading": "loading", "loading": "loading",
"reportContent": "Report", "reportContent": "Report",
"validations": { "validations": {
"email": "must be a valid email address", "email": "must be a valid e-mail address",
"url": "must be a valid URL" "url": "must be a valid URL"
} }
}, },
@ -653,39 +668,41 @@
"termsAndConditionsNewConfirmText": "Please read the new terms of use now!", "termsAndConditionsNewConfirmText": "Please read the new terms of use now!",
"termsAndConditionsNewConfirm": "I have read and agree to the new terms of conditions.", "termsAndConditionsNewConfirm": "I have read and agree to the new terms of conditions.",
"agree": "I agree!", "agree": "I agree!",
"risk": { "terms-of-service": {
"title": "Risk of accident", "title": "Terms of Service",
"description": "This is a test version! All data, your profile and the server can be completely destroyed, wiped out, lost, burnt and eventually synchronised near Alpha Centauri at any time. Use on your own risk. Commercial effects are not likely though." "description": "The following terms of use form the basis for the use of our network. When you register, you must accept them and we will inform you later about any changes that may take place. The Human Connection Network is operated in Germany and is therefore subject to German law. Place of jurisdiction is Kirchheim / Teck. For details see our imprint: <a href=\"https://human-connection.org/imprint\" target=\"_blank\" >https://human-connection.org/imprint</a> "
},
"use-and-license" : {
"title": "Use and License",
"description": "If any content you post to us is protected by intellectual property rights, you grant us a non-exclusive, transferable, sublicensable, worldwide license to use such content for posting to our network. This license expires when you delete your content or your entire account. Remember that others may share your content and we cannot delete it."
}, },
"data-privacy": { "privacy-statement" : {
"title": "You and your data", "title": "Privacy Statement",
"description": "Please note that the content of the alpha version will be used for public web presentations etc. but we are sure, this is in your interest. Avoid real names and use anonymous profile pictures without your face. You can find more information in our <a href=\"https://human-connection.org/en/privacy/\" target=\"_blank\">data privacy policy</a>" "description": "Our network is a social knowledge and action network. It is therefore particularly important to us that as much content as possible is publicly accessible. In the course of the development of our network there will be more and more the possibility to decide about the visibility of the personal data. We will inform you about these new features. Otherwise, you should always think about which personal data you disclose about yourself (or others). This applies in particular to the content of posts and comments, as these have a largely public character. Later there will be possibilities to limit the visibility of your profile. Part of the terms of service is our privacy statement, which informs you about the individual data processing operations in our network: <a href=\"https://human-connection.org/datenschutz/#netzwerk\" target=\"_blank\"> https://human-connection.org/datenschutz/#netzwerk</a> bzw. <a href=\"https://human-connection.org/datenschutz/\" target=\"_blank\">https://human-connection.org/datenschutz/</a> Our privacy statement is adapted to the legal situation and characteristics of our network and is always valid in the most current version."
}, },
"work-in-progress": { "code-of-conduct" : {
"title": "Work in progress", "title": "Code of Conduct",
"description": "This is still a test version. Please excuse if some applications are not working, blocking, irritating, displayed falsely or not able to be clicked on. Please report faults and bugs! <a href=\"mailto:support@human-connection.org\" target=\"_blank\">mailto:support@human-connection.org</a>" "description": "Our code of conduct serves as a handbook for personal appearance and interaction with each other. Whoever is active as a user in the Human Connection network, writes articles, comments or makes contact with other users, even outside the network, acknowledges these rules of conduct as binding. <a href=\"https://alpha.human-connection.org/code-of-conduct\" target=\"_blank\"> https://alpha.human-connection.org/code-of-conduct</a>"
}, },
"code-of-conduct": { "moderation" : {
"title": "Code of conduct",
"description": "The <a href=\"/code-of-conduct\">code of conduct</a> serves as guiding principles for our personal appearance and interaction with one another. Anyone who is active as a user in the Human Connection Network, writes articles, comments or contacts other users, including those outside the network, acknowledges these rules of conduct as binding."
},
"moderation": {
"title": "Moderation", "title": "Moderation",
"description": "As long as there is no community moderation-system in operation, a rainbow colored unicorn decides, if you are physically and mentally stable enough to operate our test version. The unicorn can delete you from the alpha version at any time. So be so kind and leave rainbow food!" "description": "Until our financial possibilities allow us to implement the community moderation system, we moderate with a simplified system and with our own or possibly volunteer staff. We train these moderators and for this reason only they make the appropriate decisions. These moderators carry out their work anonymously. You can report posts, comments and users to us (for example, if they provide information in their profile or have images that violate these Terms of Use). If you report something to us, you can give us a reason and a short explanation. We will then take a look at what you have reported and sanction you if necessary, e.g. by blocking contributions, comments or users. Unfortunately, you and the person concerned will not receive any feedback from us at this time, but this is in the planning stage. Irrespective of this, we reserve the right to impose sanctions in principle for reasons that may not or not yet be listed in our Code of Conduct or these terms of service."
}, },
"fairness": { "errors-and-feedback" : {
"title": "Fairness", "title": "Errors and Feedback",
"description": "If, against all expectations, our alpha version is not to your liking, we return your monthly payment within the first two months. Please send a mail to: <a href=\"mailto:info@human-connection.org\" target=\"_blank\"> info@human-connection.org </a> Note that more features are added on a regular basis." "description": "We make every effort to keep our network and data secure and available. Each new release of the software goes through both automated and manual testing. However, unforeseen errors may occur. Therefore, we are grateful for any reported bugs. You are welcome to report any bugs you discover by emailing Support at support@human-connection.org"
}, },
"questions": { "help-and-questions" : {
"title": "Questions", "title": "Help and Questions",
"description": "You can find the dates and links to our zoom-rooms here: <a href=\"https://human-connection.org/en/events/\" target=\"_blank\" >https://human-connection.org/en/events/ </a>" "description": "For help and questions we have compiled a comprehensive collection of frequently asked questions and answers (FAQ) for you. You can find them here: <a href=\"https://support.human-connection.org/kb/\" target=\"_blank\" > https://support.human-connection.org/kb/ </a>"
}, },
"human-connection": { "addition" : {
"title": "By humans for humans", "title": "In addition, we regularly hold events where you can also share your impressions and ask questions. You can find a current overview here:",
"description": "Please help us to get new donators for Human Connection, so the network can take off as soon as possible. <a href=\"https://human-connection.org/\" target=\"_blank\"> https://human-connection.org </a>" "description": "<a href=\"https://human-connection.org/events/\" target=\"_blank\" > https://human-connection.org/events/ </a>"
}, }
"have-fun": "Now have fun with the alpha version of Human Connection! For the first universal peace. ♥︎",
"closing": "Thank you very much <br> <br> your Human Connection Team"
} }
} }

View File

@ -12,7 +12,7 @@
"scripts": { "scripts": {
"dev": "nuxt", "dev": "nuxt",
"dev:styleguide": "cross-env STYLEGUIDE_DEV=true yarn run dev", "dev:styleguide": "cross-env STYLEGUIDE_DEV=true yarn run dev",
"storybook": "start-storybook -p 3002 -c storybook/", "storybook": "start-storybook -p 3002 -s ./static -c storybook/",
"build": "nuxt build", "build": "nuxt build",
"start": "nuxt start", "start": "nuxt start",
"generate:maintenance": "nuxt generate -c nuxt.config.maintenance.js", "generate:maintenance": "nuxt generate -c nuxt.config.maintenance.js",
@ -53,8 +53,8 @@
}, },
"dependencies": { "dependencies": {
"@human-connection/styleguide": "0.5.21", "@human-connection/styleguide": "0.5.21",
"@nuxtjs/apollo": "^4.0.0-rc15", "@nuxtjs/apollo": "^4.0.0-rc17",
"@nuxtjs/axios": "~5.6.0", "@nuxtjs/axios": "~5.8.0",
"@nuxtjs/dotenv": "~1.4.1", "@nuxtjs/dotenv": "~1.4.1",
"@nuxtjs/pwa": "^3.0.0-beta.19", "@nuxtjs/pwa": "^3.0.0-beta.19",
"@nuxtjs/sentry": "^3.0.1", "@nuxtjs/sentry": "^3.0.1",
@ -65,21 +65,21 @@
"cookie-universal-nuxt": "~2.0.18", "cookie-universal-nuxt": "~2.0.18",
"cropperjs": "^1.5.5", "cropperjs": "^1.5.5",
"cross-env": "~6.0.3", "cross-env": "~6.0.3",
"date-fns": "2.5.0", "date-fns": "2.6.0",
"express": "~4.17.1", "express": "~4.17.1",
"graphql": "~14.5.8", "graphql": "~14.5.8",
"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",
"node-fetch": "^2.6.0", "node-fetch": "^2.6.0",
"nuxt": "~2.10.1", "nuxt": "~2.10.2",
"nuxt-dropzone": "^1.0.4", "nuxt-dropzone": "^1.0.4",
"nuxt-env": "~0.1.0", "nuxt-env": "~0.1.0",
"stack-utils": "^1.0.2", "stack-utils": "^1.0.2",
"string-hash": "^1.1.3", "string-hash": "^1.1.3",
"tippy.js": "^4.3.5", "tippy.js": "^4.3.5",
"tiptap": "~1.26.3", "tiptap": "~1.26.3",
"tiptap-extensions": "~1.28.3", "tiptap-extensions": "~1.28.4",
"trunc-html": "^1.1.2", "trunc-html": "^1.1.2",
"v-tooltip": "~2.0.2", "v-tooltip": "~2.0.2",
"vue-count-to": "~1.0.13", "vue-count-to": "~1.0.13",
@ -95,14 +95,14 @@
"@babel/core": "~7.6.4", "@babel/core": "~7.6.4",
"@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/preset-env": "~7.6.3", "@babel/preset-env": "~7.6.3",
"@storybook/addon-a11y": "^5.2.4", "@storybook/addon-a11y": "^5.2.5",
"@storybook/addon-actions": "^5.2.4", "@storybook/addon-actions": "^5.2.5",
"@storybook/vue": "~5.2.4", "@storybook/vue": "~5.2.5",
"@vue/cli-shared-utils": "~3.12.0", "@vue/cli-shared-utils": "~4.0.5",
"@vue/eslint-config-prettier": "~5.0.0", "@vue/eslint-config-prettier": "~5.0.0",
"@vue/server-test-utils": "~1.0.0-beta.29", "@vue/server-test-utils": "~1.0.0-beta.29",
"@vue/test-utils": "~1.0.0-beta.29", "@vue/test-utils": "~1.0.0-beta.29",
"async-validator": "^3.1.0", "async-validator": "^3.2.0",
"babel-core": "~7.0.0-bridge.0", "babel-core": "~7.0.0-bridge.0",
"babel-eslint": "~10.0.3", "babel-eslint": "~10.0.3",
"babel-jest": "~24.9.0", "babel-jest": "~24.9.0",
@ -111,22 +111,23 @@
"core-js": "~2.6.10", "core-js": "~2.6.10",
"css-loader": "~3.2.0", "css-loader": "~3.2.0",
"eslint": "~5.16.0", "eslint": "~5.16.0",
"eslint-config-prettier": "~6.4.0", "eslint-config-prettier": "~6.5.0",
"eslint-config-standard": "~12.0.0", "eslint-config-standard": "~12.0.0",
"eslint-loader": "~3.0.2", "eslint-loader": "~3.0.2",
"eslint-plugin-import": "~2.18.2", "eslint-plugin-import": "~2.18.2",
"eslint-plugin-jest": "~22.19.0", "eslint-plugin-jest": "~23.0.2",
"eslint-plugin-node": "~10.0.0", "eslint-plugin-node": "~10.0.0",
"eslint-plugin-prettier": "~3.1.1", "eslint-plugin-prettier": "~3.1.1",
"eslint-plugin-promise": "~4.2.1", "eslint-plugin-promise": "~4.2.1",
"eslint-plugin-standard": "~4.0.1", "eslint-plugin-standard": "~4.0.1",
"eslint-plugin-vue": "~5.2.3", "eslint-plugin-vue": "~5.2.3",
"faker": "^4.1.0",
"flush-promises": "^1.0.2", "flush-promises": "^1.0.2",
"fuse.js": "^3.4.5", "fuse.js": "^3.4.5",
"identity-obj-proxy": "^3.0.0", "identity-obj-proxy": "^3.0.0",
"jest": "~24.9.0", "jest": "~24.9.0",
"mutation-observer": "^1.0.3", "mutation-observer": "^1.0.3",
"node-sass": "~4.12.0", "node-sass": "~4.13.0",
"prettier": "~1.18.2", "prettier": "~1.18.2",
"sass-loader": "~8.0.0", "sass-loader": "~8.0.0",
"style-loader": "~0.23.1", "style-loader": "~0.23.1",

View File

@ -24,15 +24,37 @@ describe('PostIndex', () => {
let Wrapper let Wrapper
let store let store
let mocks let mocks
let mutations
beforeEach(() => { beforeEach(() => {
mutations = {
'posts/SELECT_ORDER': jest.fn(),
}
store = new Vuex.Store({ store = new Vuex.Store({
getters: { getters: {
'postsFilter/postsFilter': () => ({}), 'posts/filter': () => ({}),
'posts/orderOptions': () => () => [
{
key: 'store.posts.orderBy.oldest.label',
label: 'store.posts.orderBy.oldest.label',
icon: 'sort-amount-asc',
value: 'createdAt_asc',
},
{
key: 'store.posts.orderBy.newest.label',
label: 'store.posts.orderBy.newest.label',
icon: 'sort-amount-desc',
value: 'createdAt_desc',
},
],
'posts/selectedOrder': () => () => 'createdAt_desc',
'posts/orderIcon': () => 'sort-amount-desc',
'posts/orderBy': () => 'createdAt_desc',
'auth/user': () => { 'auth/user': () => {
return { id: 'u23' } return { id: 'u23' }
}, },
}, },
mutations,
}) })
mocks = { mocks = {
$t: key => key, $t: key => key,
@ -103,12 +125,12 @@ describe('PostIndex', () => {
}) })
}) })
it('sets the post in the store when there are posts', () => { it('calls store when using order by menu', () => {
wrapper wrapper
.findAll('li') .findAll('li')
.at(0) .at(0)
.trigger('click') .trigger('click')
expect(wrapper.vm.sorting).toEqual('createdAt_desc') expect(mutations['posts/SELECT_ORDER']).toHaveBeenCalledWith({}, 'createdAt_asc')
}) })
it('updates offset when a user clicks on the load more button', () => { it('updates offset when a user clicks on the load more button', () => {

View File

@ -10,8 +10,7 @@
v-model="selected" v-model="selected"
:options="sortingOptions" :options="sortingOptions"
size="large" size="large"
v-bind:icon-right="sortingIcon" :icon-right="sortingIcon"
@input="toggleOnlySorting"
></ds-select> ></ds-select>
</div> </div>
</ds-grid-item> </ds-grid-item>
@ -21,6 +20,8 @@
:post="post" :post="post"
:width="{ base: '100%', xs: '100%', md: '50%', xl: '33%' }" :width="{ base: '100%', xs: '100%', md: '50%', xl: '33%' }"
@removePostFromList="deletePost" @removePostFromList="deletePost"
@pinPost="pinPost"
@unpinPost="unpinPost"
/> />
</masonry-grid-item> </masonry-grid-item>
</template> </template>
@ -58,12 +59,13 @@
<script> <script>
import FilterMenu from '~/components/FilterMenu/FilterMenu.vue' import FilterMenu from '~/components/FilterMenu/FilterMenu.vue'
import HcEmpty from '~/components/Empty' import HcEmpty from '~/components/Empty'
import HcPostCard from '~/components/PostCard' import HcPostCard from '~/components/PostCard/PostCard.vue'
import HcLoadMore from '~/components/LoadMore.vue' import HcLoadMore from '~/components/LoadMore.vue'
import MasonryGrid from '~/components/MasonryGrid/MasonryGrid.vue' import MasonryGrid from '~/components/MasonryGrid/MasonryGrid.vue'
import MasonryGridItem from '~/components/MasonryGrid/MasonryGridItem.vue' import MasonryGridItem from '~/components/MasonryGrid/MasonryGridItem.vue'
import { mapGetters } from 'vuex' import { mapGetters, mapMutations } from 'vuex'
import { filterPosts } from '~/graphql/PostQuery.js' import { filterPosts } from '~/graphql/PostQuery.js'
import PostMutations from '~/graphql/PostMutations'
export default { export default {
components: { components: {
@ -83,30 +85,29 @@ export default {
offset: 0, offset: 0,
pageSize: 12, pageSize: 12,
hashtag, hashtag,
placeholder: this.$t('sorting.newest'),
selected: this.$t('sorting.newest'),
sortingIcon: 'sort-amount-desc',
sorting: 'createdAt_desc',
sortingOptions: [
{
label: this.$t('sorting.newest'),
value: 'Newest',
icons: 'sort-amount-desc',
order: 'createdAt_desc',
},
{
label: this.$t('sorting.oldest'),
value: 'Oldest',
icons: 'sort-amount-asc',
order: 'createdAt_asc',
},
],
} }
}, },
computed: { computed: {
...mapGetters({ ...mapGetters({
postsFilter: 'postsFilter/postsFilter', postsFilter: 'posts/filter',
orderOptions: 'posts/orderOptions',
orderBy: 'posts/orderBy',
selectedOrder: 'posts/selectedOrder',
sortingIcon: 'posts/orderIcon',
}), }),
selected: {
get() {
return this.selectedOrder(this)
},
set({ value }) {
this.offset = 0
this.posts = []
this.selectOrder(value)
},
},
sortingOptions() {
return this.orderOptions(this)
},
finalFilters() { finalFilters() {
let filter = this.postsFilter let filter = this.postsFilter
if (this.hashtag) { if (this.hashtag) {
@ -122,12 +123,9 @@ export default {
}, },
}, },
methods: { methods: {
toggleOnlySorting(x) { ...mapMutations({
this.offset = 0 selectOrder: 'posts/SELECT_ORDER',
this.posts = [] }),
this.sortingIcon = x.icons
this.sorting = x.order
},
clearSearch() { clearSearch() {
this.$router.push({ path: '/' }) this.$router.push({ path: '/' })
this.hashtag = null this.hashtag = null
@ -148,7 +146,7 @@ export default {
offset: this.offset, offset: this.offset,
filter: this.finalFilters, filter: this.finalFilters,
first: this.pageSize, first: this.pageSize,
orderBy: this.sorting, orderBy: ['pinned_asc', this.orderBy],
}, },
updateQuery: (previousResult, { fetchMoreResult }) => { updateQuery: (previousResult, { fetchMoreResult }) => {
if (!fetchMoreResult || fetchMoreResult.Post.length < this.pageSize) { if (!fetchMoreResult || fetchMoreResult.Post.length < this.pageSize) {
@ -166,6 +164,37 @@ export default {
return post.id !== deletedPost.id return post.id !== deletedPost.id
}) })
}, },
resetPostList() {
this.offset = 0
this.posts = []
this.hasMore = true
},
pinPost(post) {
this.$apollo
.mutate({
mutation: PostMutations().pinPost,
variables: { id: post.id },
})
.then(() => {
this.$toast.success(this.$t('post.menu.pinnedSuccessfully'))
this.resetPostList()
this.$apollo.queries.Post.refetch()
})
.catch(error => this.$toast.error(error.message))
},
unpinPost(post) {
this.$apollo
.mutate({
mutation: PostMutations().unpinPost,
variables: { id: post.id },
})
.then(() => {
this.$toast.success(this.$t('post.menu.unpinnedSuccessfully'))
this.resetPostList()
this.$apollo.queries.Post.refetch()
})
.catch(error => this.$toast.error(error.message))
},
}, },
apollo: { apollo: {
Post: { Post: {
@ -176,7 +205,7 @@ export default {
return { return {
filter: this.finalFilters, filter: this.finalFilters,
first: this.pageSize, first: this.pageSize,
orderBy: this.sorting, orderBy: ['pinned_asc', this.orderBy],
offset: 0, offset: 0,
} }
}, },

View File

@ -1,138 +1,27 @@
<template> <template>
<transition name="fade" appear> <transition name="fade" appear>
<ds-container v-if="ready" width="medium"> <login-form @success="handleSuccess" />
<ds-space margin="small">
<blockquote>
<p>{{ $t('quotes.african.quote') }}</p>
<b>- {{ $t('quotes.african.author') }}</b>
</blockquote>
</ds-space>
<ds-card class="login-card">
<ds-flex gutter="small">
<ds-flex-item :width="{ base: '100%', sm: '50%' }" centered>
<client-only>
<locale-switch class="login-locale-switch" offset="5" />
</client-only>
<ds-space margin-top="small" margin-bottom="xxx-small" centered>
<img
class="login-image"
alt="Human Connection"
src="/img/sign-up/humanconnection.svg"
/>
</ds-space>
</ds-flex-item>
<ds-flex-item :width="{ base: '100%', sm: '50%' }" centered>
<ds-space margin="small">
<a :href="$t('login.moreInfoURL')" :title="$t('login.moreInfoHint')" target="_blank">
{{ $t('login.moreInfo') }}
</a>
</ds-space>
<ds-space margin="small">
<ds-text size="small">{{ $t('login.copy') }}</ds-text>
</ds-space>
<form :disabled="pending" @submit.prevent="onSubmit">
<ds-input
v-model="form.email"
:disabled="pending"
:placeholder="$t('login.email')"
type="email"
name="email"
icon="envelope"
/>
<ds-input
v-model="form.password"
:disabled="pending"
:placeholder="$t('login.password')"
icon="lock"
icon-right="question-circle"
name="password"
type="password"
/>
<ds-space margin-bottom="large">
<nuxt-link to="/password-reset/request">{{ $t('login.forgotPassword') }}</nuxt-link>
</ds-space>
<ds-button
:loading="pending"
primary
fullwidth
name="submit"
type="submit"
icon="sign-in"
>
{{ $t('login.login') }}
</ds-button>
<ds-space margin-top="large" margin-bottom="x-small">
{{ $t('login.no-account') }}
<nuxt-link to="/registration/signup">{{ $t('login.register') }}</nuxt-link>
</ds-space>
</form>
</ds-flex-item>
</ds-flex>
</ds-card>
</ds-container>
</transition> </transition>
</template> </template>
<script> <script>
import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch' import LoginForm from '~/components/LoginForm/LoginForm.vue'
import { VERSION } from '~/constants/terms-and-conditions-version.js' import { VERSION } from '~/constants/terms-and-conditions-version.js'
export default { export default {
layout: 'no-header', layout: 'no-header',
components: { components: {
LocaleSwitch, LoginForm,
},
data() {
return {
ready: false,
form: {
email: '',
password: '',
},
}
},
computed: {
pending() {
return this.$store.getters['auth/pending']
},
}, },
asyncData({ store, redirect }) { asyncData({ store, redirect }) {
if (store.getters['auth/user'].termsAndConditionsAgreedVersion === VERSION) { if (store.getters['auth/user'].termsAndConditionsAgreedVersion === VERSION) {
redirect('/') redirect('/')
} }
}, },
mounted() {
setTimeout(() => {
// NOTE: quick fix for jumping flexbox implementation
// will be fixed in a future update of the styleguide
this.ready = true
}, 50)
},
methods: { methods: {
async onSubmit() { handleSuccess() {
try { this.$router.replace(this.$route.query.path || '/')
await this.$store.dispatch('auth/login', { ...this.form })
this.$toast.success(this.$t('login.success'))
this.$router.replace(this.$route.query.path || '/')
} catch (err) {
this.$toast.error(err.message)
}
}, },
}, },
} }
</script> </script>
<style lang="scss">
.login-image {
width: 90%;
max-width: 200px;
}
.login-card {
position: relative;
}
.login-locale-switch {
position: absolute;
top: 1em;
left: 1em;
}
</style>

View File

@ -31,6 +31,9 @@ describe('PostSlug', () => {
$filters: { $filters: {
truncate: a => a, truncate: a => a,
}, },
$route: {
hash: '',
},
// If you are mocking the router, then don't use VueRouter with localVue: https://vue-test-utils.vuejs.org/guides/using-with-vue-router.html // If you are mocking the router, then don't use VueRouter with localVue: https://vue-test-utils.vuejs.org/guides/using-with-vue-router.html
$router: { $router: {
history: { history: {

View File

@ -18,6 +18,8 @@
:resource="post" :resource="post"
:modalsData="menuModalsData" :modalsData="menuModalsData"
:is-owner="isAuthor(post.author ? post.author.id : null)" :is-owner="isAuthor(post.author ? post.author.id : null)"
@pinPost="pinPost"
@unpinPost="unpinPost"
/> />
</client-only> </client-only>
<ds-space margin-bottom="small" /> <ds-space margin-bottom="small" />
@ -68,9 +70,13 @@
</ds-space> </ds-space>
<!-- Comments --> <!-- Comments -->
<ds-section slot="footer"> <ds-section slot="footer">
<hc-comment-list :post="post" :routeHash="$route.hash" /> <hc-comment-list
:post="post"
:routeHash="$route.hash"
@toggleNewCommentForm="toggleNewCommentForm"
/>
<ds-space margin-bottom="large" /> <ds-space margin-bottom="large" />
<hc-comment-form :post="post" @createComment="createComment" /> <hc-comment-form v-if="showNewCommentForm" :post="post" @createComment="createComment" />
</ds-section> </ds-section>
</ds-card> </ds-card>
</transition> </transition>
@ -88,6 +94,7 @@ import HcCommentList from '~/components/CommentList/CommentList'
import { postMenuModalsData, deletePostMutation } from '~/components/utils/PostHelpers' import { postMenuModalsData, deletePostMutation } from '~/components/utils/PostHelpers'
import PostQuery from '~/graphql/PostQuery' import PostQuery from '~/graphql/PostQuery'
import HcEmotions from '~/components/Emotions/Emotions' import HcEmotions from '~/components/Emotions/Emotions'
import PostMutations from '~/graphql/PostMutations'
export default { export default {
name: 'PostSlug', name: 'PostSlug',
@ -116,6 +123,7 @@ export default {
post: null, post: null,
ready: false, ready: false,
title: 'loading', title: 'loading',
showNewCommentForm: true,
} }
}, },
watch: { watch: {
@ -156,6 +164,31 @@ export default {
async createComment(comment) { async createComment(comment) {
this.post.comments.push(comment) this.post.comments.push(comment)
}, },
pinPost(post) {
this.$apollo
.mutate({
mutation: PostMutations().pinPost,
variables: { id: post.id },
})
.then(() => {
this.$toast.success(this.$t('post.menu.pinnedSuccessfully'))
})
.catch(error => this.$toast.error(error.message))
},
unpinPost(post) {
this.$apollo
.mutate({
mutation: PostMutations().unpinPost,
variables: { id: post.id },
})
.then(() => {
this.$toast.success(this.$t('post.menu.unpinnedSuccessfully'))
})
.catch(error => this.$toast.error(error.message))
},
toggleNewCommentForm(showNewCommentForm) {
this.showNewCommentForm = showNewCommentForm
},
}, },
apollo: { apollo: {
Post: { Post: {

View File

@ -37,7 +37,7 @@
<script> <script>
import HcEmpty from '~/components/Empty.vue' import HcEmpty from '~/components/Empty.vue'
import HcPostCard from '~/components/PostCard' import HcPostCard from '~/components/PostCard/PostCard.vue'
import HcCategory from '~/components/Category' import HcCategory from '~/components/Category'
import HcHashtag from '~/components/Hashtag/Hashtag' import HcHashtag from '~/components/Hashtag/Hashtag'
import { relatedContributions } from '~/graphql/PostQuery' import { relatedContributions } from '~/graphql/PostQuery'

View File

@ -172,7 +172,7 @@
<ds-grid-item class="profile-top-navigation" :row-span="3" column-span="fullWidth"> <ds-grid-item class="profile-top-navigation" :row-span="3" column-span="fullWidth">
<ds-card class="ds-tab-nav"> <ds-card class="ds-tab-nav">
<ul class="Tabs"> <ul class="Tabs">
<li class="Tabs__tab Tab pointer" :class="{ active: tabActive === 'post' }"> <li class="Tabs__tab pointer" :class="{ active: tabActive === 'post' }">
<a @click="handleTab('post')"> <a @click="handleTab('post')">
<ds-space margin="small"> <ds-space margin="small">
<client-only placeholder="Loading..."> <client-only placeholder="Loading...">
@ -183,7 +183,7 @@
</ds-space> </ds-space>
</a> </a>
</li> </li>
<li class="Tabs__tab Tab pointer" :class="{ active: tabActive === 'comment' }"> <li class="Tabs__tab pointer" :class="{ active: tabActive === 'comment' }">
<a @click="handleTab('comment')"> <a @click="handleTab('comment')">
<ds-space margin="small"> <ds-space margin="small">
<client-only placeholder="Loading..."> <client-only placeholder="Loading...">
@ -194,7 +194,11 @@
</ds-space> </ds-space>
</a> </a>
</li> </li>
<li class="Tabs__tab Tab pointer" :class="{ active: tabActive === 'shout' }"> <li
class="Tabs__tab pointer"
:class="{ active: tabActive === 'shout' }"
v-if="myProfile"
>
<a @click="handleTab('shout')"> <a @click="handleTab('shout')">
<ds-space margin="small"> <ds-space margin="small">
<client-only placeholder="Loading..."> <client-only placeholder="Loading...">
@ -205,7 +209,6 @@
</ds-space> </ds-space>
</a> </a>
</li> </li>
<li class="Tabs__presentation-slider" role="presentation"></li>
</ul> </ul>
</ds-card> </ds-card>
</ds-grid-item> </ds-grid-item>
@ -234,6 +237,8 @@
:post="post" :post="post"
:width="{ base: '100%', md: '100%', xl: '50%' }" :width="{ base: '100%', md: '100%', xl: '50%' }"
@removePostFromList="removePostFromList" @removePostFromList="removePostFromList"
@pinPost="pinPost"
@unpinPost="unpinPost"
/> />
</masonry-grid-item> </masonry-grid-item>
</template> </template>
@ -268,7 +273,7 @@
<script> <script>
import uniqBy from 'lodash/uniqBy' import uniqBy from 'lodash/uniqBy'
import User from '~/components/User/User' import User from '~/components/User/User'
import HcPostCard from '~/components/PostCard' import HcPostCard from '~/components/PostCard/PostCard.vue'
import HcFollowButton from '~/components/FollowButton.vue' import HcFollowButton from '~/components/FollowButton.vue'
import HcCountTo from '~/components/CountTo.vue' import HcCountTo from '~/components/CountTo.vue'
import HcBadges from '~/components/Badges.vue' import HcBadges from '~/components/Badges.vue'
@ -279,9 +284,10 @@ import HcUpload from '~/components/Upload'
import HcAvatar from '~/components/Avatar/Avatar.vue' import HcAvatar from '~/components/Avatar/Avatar.vue'
import MasonryGrid from '~/components/MasonryGrid/MasonryGrid.vue' import MasonryGrid from '~/components/MasonryGrid/MasonryGrid.vue'
import MasonryGridItem from '~/components/MasonryGrid/MasonryGridItem.vue' import MasonryGridItem from '~/components/MasonryGrid/MasonryGridItem.vue'
import { filterPosts } from '~/graphql/PostQuery' import { profilePagePosts } from '~/graphql/PostQuery'
import UserQuery from '~/graphql/User' import UserQuery from '~/graphql/User'
import { Block, Unblock } from '~/graphql/settings/BlockedUsers' import { Block, Unblock } from '~/graphql/settings/BlockedUsers'
import PostMutations from '~/graphql/PostMutations'
const tabToFilterMapping = ({ tab, id }) => { const tabToFilterMapping = ({ tab, id }) => {
return { return {
@ -373,7 +379,7 @@ export default {
return uniqBy(items, field) return uniqBy(items, field)
}, },
showMoreContributions() { showMoreContributions() {
const { Post: PostQuery } = this.$apollo.queries const { profilePagePosts: PostQuery } = this.$apollo.queries
if (!PostQuery) return // seems this can be undefined on subpages if (!PostQuery) return // seems this can be undefined on subpages
this.offset += this.pageSize this.offset += this.pageSize
@ -385,11 +391,14 @@ export default {
orderBy: 'createdAt_desc', orderBy: 'createdAt_desc',
}, },
updateQuery: (previousResult, { fetchMoreResult }) => { updateQuery: (previousResult, { fetchMoreResult }) => {
if (!fetchMoreResult || fetchMoreResult.Post.length < this.pageSize) { if (!fetchMoreResult || fetchMoreResult.profilePagePosts.length < this.pageSize) {
this.hasMore = false this.hasMore = false
} }
const result = Object.assign({}, previousResult, { const result = Object.assign({}, previousResult, {
Post: [...previousResult.Post, ...fetchMoreResult.Post], profilePagePosts: [
...previousResult.profilePagePosts,
...fetchMoreResult.profilePagePosts,
],
}) })
return result return result
}, },
@ -404,13 +413,39 @@ export default {
await this.$apollo.mutate({ mutation: Block(), variables: { id: user.id } }) await this.$apollo.mutate({ mutation: Block(), variables: { id: user.id } })
this.$apollo.queries.User.refetch() this.$apollo.queries.User.refetch()
this.resetPostList() this.resetPostList()
this.$apollo.queries.Post.refetch() this.$apollo.queries.profilePagePosts.refetch()
}, },
async unblock(user) { async unblock(user) {
await this.$apollo.mutate({ mutation: Unblock(), variables: { id: user.id } }) await this.$apollo.mutate({ mutation: Unblock(), variables: { id: user.id } })
this.$apollo.queries.User.refetch() this.$apollo.queries.User.refetch()
this.resetPostList() this.resetPostList()
this.$apollo.queries.Post.refetch() this.$apollo.queries.profilePagePosts.refetch()
},
pinPost(post) {
this.$apollo
.mutate({
mutation: PostMutations().pinPost,
variables: { id: post.id },
})
.then(() => {
this.$toast.success(this.$t('post.menu.pinnedSuccessfully'))
this.resetPostList()
this.$apollo.queries.profilePagePosts.refetch()
})
.catch(error => this.$toast.error(error.message))
},
unpinPost(post) {
this.$apollo
.mutate({
mutation: PostMutations().unpinPost,
variables: { id: post.id },
})
.then(() => {
this.$toast.success(this.$t('post.menu.unpinnedSuccessfully'))
this.resetPostList()
this.$apollo.queries.profilePagePosts.refetch()
})
.catch(error => this.$toast.error(error.message))
}, },
optimisticFollow({ followedByCurrentUser }) { optimisticFollow({ followedByCurrentUser }) {
/* /*
@ -435,9 +470,9 @@ export default {
}, },
}, },
apollo: { apollo: {
Post: { profilePagePosts: {
query() { query() {
return filterPosts(this.$i18n) return profilePagePosts(this.$i18n)
}, },
variables() { variables() {
return { return {
@ -447,8 +482,8 @@ export default {
orderBy: 'createdAt_desc', orderBy: 'createdAt_desc',
} }
}, },
update({ Post }) { update({ profilePagePosts }) {
this.posts = Post this.posts = profilePagePosts
}, },
fetchPolicy: 'cache-and-network', fetchPolicy: 'cache-and-network',
}, },
@ -469,51 +504,28 @@ export default {
.pointer { .pointer {
cursor: pointer; cursor: pointer;
} }
.Tab {
border-collapse: collapse;
padding-bottom: 5px;
}
.Tab:hover {
border-bottom: 2px solid #c9c6ce;
}
.Tabs { .Tabs {
position: relative; position: relative;
background-color: #fff; background-color: #fff;
height: 100%; height: 100%;
display: flex;
&:after {
content: ' ';
display: table;
clear: both;
}
margin: 0; margin: 0;
padding: 0; padding: 0;
list-style: none; list-style: none;
&__tab { &__tab {
float: left;
width: 33.333%;
text-align: center; text-align: center;
height: 100%; height: 100%;
flex-grow: 1;
&:first-child.active ~ .Tabs__presentation-slider { &:hover {
left: 0; border-bottom: 2px solid #c9c6ce;
} }
&:nth-child(2).active ~ .Tabs__presentation-slider {
left: 33.333%; &.active {
border-bottom: 2px solid #17b53f;
} }
&:nth-child(3).active ~ .Tabs__presentation-slider {
left: calc(33.333% * 2);
}
}
&__presentation-slider {
position: absolute;
bottom: 0;
left: 0;
width: 33.333%;
height: 2px;
background-color: #17b53f;
transition: left 0.25s;
} }
} }
.profile-avatar.ds-avatar { .profile-avatar.ds-avatar {

View File

@ -11,11 +11,6 @@
<p v-html="$t(`termsAndConditions.${section}.description`)" /> <p v-html="$t(`termsAndConditions.${section}.description`)" />
</li> </li>
</ol> </ol>
<p>{{ $t('termsAndConditions.have-fun') }}</p>
<br />
<p>
<strong v-html="$t('termsAndConditions.closing')" />
</p>
</div> </div>
</ds-container> </ds-container>
</div> </div>
@ -31,15 +26,16 @@ export default {
}, },
data() { data() {
return { return {
// if you change terms and conditions please change also version in file "webapp/constants/terms-and-conditions-version.js"
sections: [ sections: [
'risk', 'terms-of-service',
'data-privacy', 'use-and-license',
'work-in-progress', 'privacy-statement',
'code-of-conduct', 'code-of-conduct',
'moderation', 'moderation',
'fairness', 'errors-and-feedback',
'questions', 'help-and-questions',
'human-connection', 'addition',
], ],
} }
}, },

View File

@ -31,11 +31,7 @@ export default ({ app = {} }) => {
if (length <= 0) { if (length <= 0) {
return value return value
} }
let output = trunc(value, length).html return trunc(value, length).html
if (output.length < value.length) {
output += ' …'
}
return output
}, },
list: (value, glue = ', ', truncate = 0) => { list: (value, glue = ', ', truncate = 0) => {
if (!Array.isArray(value) || !value.length) { if (!Array.isArray(value) || !value.length) {

View File

@ -7,11 +7,25 @@ import clone from 'lodash/clone'
const defaultFilter = {} const defaultFilter = {}
const orderOptions = {
createdAt_asc: {
value: 'createdAt_asc',
key: 'store.posts.orderBy.oldest.label',
icon: 'sort-amount-asc',
},
createdAt_desc: {
value: 'createdAt_desc',
key: 'store.posts.orderBy.newest.label',
icon: 'sort-amount-desc',
},
}
export const state = () => { export const state = () => {
return { return {
filter: { filter: {
...defaultFilter, ...defaultFilter,
}, },
order: orderOptions['createdAt_desc'],
} }
} }
@ -46,13 +60,16 @@ export const mutations = {
if (isEmpty(get(filter, 'emotions_some.emotion_in'))) delete filter.emotions_some if (isEmpty(get(filter, 'emotions_some.emotion_in'))) delete filter.emotions_some
state.filter = filter state.filter = filter
}, },
SELECT_ORDER(state, value) {
state.order = orderOptions[value]
},
} }
export const getters = { export const getters = {
isActive(state) { isActive(state) {
return !isEqual(state.filter, defaultFilter) return !isEqual(state.filter, defaultFilter)
}, },
postsFilter(state) { filter(state) {
return state.filter return state.filter
}, },
filteredCategoryIds(state) { filteredCategoryIds(state) {
@ -64,4 +81,23 @@ export const getters = {
filteredByEmotions(state) { filteredByEmotions(state) {
return get(state.filter, 'emotions_some.emotion_in') || [] return get(state.filter, 'emotions_some.emotion_in') || []
}, },
orderOptions: state => ({ $t }) =>
Object.values(orderOptions).map(option => {
return {
...option,
label: $t(option.key),
}
}),
selectedOrder: state => ({ $t }) => {
return {
...state.order,
label: $t(state.order.key),
}
},
orderBy(state) {
return state.order.value
},
orderIcon(state) {
return state.order.icon
},
} }

View File

@ -1,7 +1,7 @@
import { getters, mutations } from './postsFilter.js' import { getters, mutations } from './posts.js'
let state let state
let testAction let testMutation
describe('getters', () => { describe('getters', () => {
describe('isActive', () => { describe('isActive', () => {
@ -25,10 +25,10 @@ describe('getters', () => {
}) })
}) })
describe('postsFilter', () => { describe('filter', () => {
it('returns filter', () => { it('returns filter', () => {
state = { filter: { author: { followedBy_some: { id: 7 } } } } state = { filter: { author: { followedBy_some: { id: 7 } } } }
expect(getters.postsFilter(state)).toEqual({ author: { followedBy_some: { id: 7 } } }) expect(getters.filter(state)).toEqual({ author: { followedBy_some: { id: 7 } } })
}) })
}) })
@ -67,14 +67,48 @@ describe('getters', () => {
expect(getters.filteredByEmotions(state)).toEqual([]) expect(getters.filteredByEmotions(state)).toEqual([])
}) })
}) })
describe('orderByOptions', () => {
it('returns all options regardless of current state', () => {
const $t = jest.fn(t => t)
expect(getters.orderOptions()({ $t })).toEqual([
{
key: 'store.posts.orderBy.oldest.label',
label: 'store.posts.orderBy.oldest.label',
icon: 'sort-amount-asc',
value: 'createdAt_asc',
},
{
key: 'store.posts.orderBy.newest.label',
label: 'store.posts.orderBy.newest.label',
icon: 'sort-amount-desc',
value: 'createdAt_desc',
},
])
})
})
describe('orderBy', () => {
it('returns value for graphql query', () => {
state = {
order: {
key: 'store.posts.orderBy.newest.label',
label: 'store.posts.orderBy.newest.label',
icon: 'sort-amount-desc',
value: 'createdAt_desc',
},
}
expect(getters.orderBy(state)).toEqual('createdAt_desc')
})
})
}) })
describe('mutations', () => { describe('mutations', () => {
describe('RESET_CATEGORIES', () => { describe('RESET_CATEGORIES', () => {
beforeEach(() => { beforeEach(() => {
testAction = categoryId => { testMutation = categoryId => {
mutations.RESET_CATEGORIES(state, categoryId) mutations.RESET_CATEGORIES(state, categoryId)
return getters.postsFilter(state) return getters.filter(state)
} }
}) })
it('resets the categories filter', () => { it('resets the categories filter', () => {
@ -84,37 +118,37 @@ describe('mutations', () => {
categories_some: { id_in: [23] }, categories_some: { id_in: [23] },
}, },
} }
expect(testAction(23)).toEqual({ author: { followedBy_some: { id: 7 } } }) expect(testMutation(23)).toEqual({ author: { followedBy_some: { id: 7 } } })
}) })
}) })
describe('TOGGLE_CATEGORY', () => { describe('TOGGLE_CATEGORY', () => {
beforeEach(() => { beforeEach(() => {
testAction = categoryId => { testMutation = categoryId => {
mutations.TOGGLE_CATEGORY(state, categoryId) mutations.TOGGLE_CATEGORY(state, categoryId)
return getters.postsFilter(state) return getters.filter(state)
} }
}) })
it('creates category filter if empty', () => { it('creates category filter if empty', () => {
state = { filter: {} } state = { filter: {} }
expect(testAction(23)).toEqual({ categories_some: { id_in: [23] } }) expect(testMutation(23)).toEqual({ categories_some: { id_in: [23] } })
}) })
it('adds category id not present', () => { it('adds category id not present', () => {
state = { filter: { categories_some: { id_in: [24] } } } state = { filter: { categories_some: { id_in: [24] } } }
expect(testAction(23)).toEqual({ categories_some: { id_in: [24, 23] } }) expect(testMutation(23)).toEqual({ categories_some: { id_in: [24, 23] } })
}) })
it('removes category id if present', () => { it('removes category id if present', () => {
state = { filter: { categories_some: { id_in: [23, 24] } } } state = { filter: { categories_some: { id_in: [23, 24] } } }
const result = testAction(23) const result = testMutation(23)
expect(result).toEqual({ categories_some: { id_in: [24] } }) expect(result).toEqual({ categories_some: { id_in: [24] } })
}) })
it('removes category filter if empty', () => { it('removes category filter if empty', () => {
state = { filter: { categories_some: { id_in: [23] } } } state = { filter: { categories_some: { id_in: [23] } } }
expect(testAction(23)).toEqual({}) expect(testMutation(23)).toEqual({})
}) })
it('does not get in the way of other filters', () => { it('does not get in the way of other filters', () => {
@ -124,15 +158,15 @@ describe('mutations', () => {
categories_some: { id_in: [23] }, categories_some: { id_in: [23] },
}, },
} }
expect(testAction(23)).toEqual({ author: { followedBy_some: { id: 7 } } }) expect(testMutation(23)).toEqual({ author: { followedBy_some: { id: 7 } } })
}) })
}) })
describe('TOGGLE_FILTER_BY_FOLLOWED', () => { describe('TOGGLE_FILTER_BY_FOLLOWED', () => {
beforeEach(() => { beforeEach(() => {
testAction = userId => { testMutation = userId => {
mutations.TOGGLE_FILTER_BY_FOLLOWED(state, userId) mutations.TOGGLE_FILTER_BY_FOLLOWED(state, userId)
return getters.postsFilter(state) return getters.filter(state)
} }
}) })
@ -142,7 +176,7 @@ describe('mutations', () => {
}) })
it('attaches the id of the current user to the filter object', () => { it('attaches the id of the current user to the filter object', () => {
expect(testAction(4711)).toEqual({ author: { followedBy_some: { id: 4711 } } }) expect(testMutation(4711)).toEqual({ author: { followedBy_some: { id: 4711 } } })
}) })
}) })
@ -152,8 +186,23 @@ describe('mutations', () => {
}) })
it('remove the id of the current user from the filter object', () => { it('remove the id of the current user from the filter object', () => {
expect(testAction(4711)).toEqual({}) expect(testMutation(4711)).toEqual({})
}) })
}) })
}) })
describe('SELECT_ORDER', () => {
beforeEach(() => {
testMutation = key => {
mutations.SELECT_ORDER(state, key)
return getters.orderBy(state)
}
})
it('switches the currently selected order', () => {
state = {
// does not matter
}
expect(testMutation('createdAt_asc')).toEqual('createdAt_asc')
})
})
}) })

View File

@ -3,7 +3,9 @@ import Vuex from 'vuex'
import vuexI18n from 'vuex-i18n/dist/vuex-i18n.umd.js' import vuexI18n from 'vuex-i18n/dist/vuex-i18n.umd.js'
import Styleguide from '@human-connection/styleguide' import Styleguide from '@human-connection/styleguide'
import Filters from '~/plugins/vue-filters' import Filters from '~/plugins/vue-filters'
import IziToast from '~/plugins/izi-toast'
import layout from './layout.vue' import layout from './layout.vue'
import locales from '~/locales/index.js'
import '~/plugins/v-tooltip' import '~/plugins/v-tooltip'
@ -12,10 +14,13 @@ const helpers = {
Vue.use(Vuex) Vue.use(Vuex)
Vue.use(Styleguide) Vue.use(Styleguide)
Vue.use(Filters) Vue.use(Filters)
Vue.use(IziToast)
Vue.use(vuexI18n.plugin, helpers.store) Vue.use(vuexI18n.plugin, helpers.store)
Vue.i18n.add('en', require('~/locales/en.json')) locales.forEach(({ code }) => {
Vue.i18n.add('de', require('~/locales/de.json')) Vue.i18n.add(code, require(`~/locales/${code}.json`))
})
Vue.i18n.set('en') Vue.i18n.set('en')
Vue.i18n.fallback('en') Vue.i18n.fallback('en')
@ -35,14 +40,6 @@ const helpers = {
}, },
}, },
}, },
editor: {
namespaced: true,
getters: {
placeholder(state) {
return 'Leave your inspirational thoughts ...'
},
},
},
}, },
}), }),
layout(storyFn) { layout(storyFn) {

File diff suppressed because it is too large Load Diff

166
yarn.lock
View File

@ -891,6 +891,11 @@
dependencies: dependencies:
"@hapi/hoek" "8.x.x" "@hapi/hoek" "8.x.x"
"@types/sizzle@2.3.2":
version "2.3.2"
resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47"
integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==
JSONStream@^1.0.3: JSONStream@^1.0.3:
version "1.3.5" version "1.3.5"
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0"
@ -1120,6 +1125,20 @@ atob@^2.1.1:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
auto-changelog@^1.16.2:
version "1.16.2"
resolved "https://registry.yarnpkg.com/auto-changelog/-/auto-changelog-1.16.2.tgz#4b08b7cbd07fdbd9139c6e06ea0b704db3f5485c"
integrity sha512-QL7zKH5FBBHz6tECO8CjZ8LpdevVSJoDskDzzPeoB9Bfe6LyXmRzXUoTIFKJXXdVaX8ydMpDO9Oa8ihZ4Au+CA==
dependencies:
commander "^3.0.1"
core-js "^3.2.1"
handlebars "^4.1.2"
lodash.uniqby "^4.7.0"
node-fetch "^2.6.0"
parse-github-url "^1.0.2"
regenerator-runtime "^0.13.3"
semver "^6.3.0"
aws-sign2@~0.7.0: aws-sign2@~0.7.0:
version "0.7.0" version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
@ -1649,10 +1668,15 @@ commander@2.15.1:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==
commander@^2.9.0: commander@^2.9.0, commander@~2.20.3:
version "2.19.0" version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commander@^3.0.1:
version "3.0.2"
resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e"
integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==
common-tags@1.8.0: common-tags@1.8.0:
version "1.8.0" version "1.8.0"
@ -1726,6 +1750,11 @@ core-js@^2.4.0:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895"
integrity sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A== integrity sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==
core-js@^3.2.1:
version "3.3.3"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.3.3.tgz#b7048d3c6c1a52b5fe55a729c1d4ccdffe0891bb"
integrity sha512-0xmD4vUJRY8nfLyV9zcpC17FtSie5STXzw+HyYw2t8IIvmDnbq7RJUULECCo+NstpJtwK9kx8S+898iyqgeUow==
core-util-is@1.0.2, core-util-is@~1.0.0: core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@ -1895,23 +1924,24 @@ cypress-cucumber-preprocessor@^1.16.2:
js-string-escape "^1.0.1" js-string-escape "^1.0.1"
through "^2.3.8" through "^2.3.8"
cypress-file-upload@^3.3.4: cypress-file-upload@^3.4.0:
version "3.3.4" version "3.4.0"
resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-3.3.4.tgz#cbeb8a7a07150a1c60f2873666979e48b6335070" resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-3.4.0.tgz#f066853357994ed7b64e0ea35920d3d85273914e"
integrity sha512-kfdrQ6cWBw82G7EbHSqZJiOQWRh9cGz9K1mjePNZax00gBL0qOdRTjfkAnR2vEmmJyCfnN3efryjfhFeLrGWVw== integrity sha512-BY7jrpOPFEGcGBzkTReEjwQ59+O3u2SH2OleXdnDCuWIPHjbDx7haXukyAFd906JsI4Z2zXPiKrUVFHZc96eFA==
cypress-plugin-retries@^1.3.0: cypress-plugin-retries@^1.4.0:
version "1.3.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/cypress-plugin-retries/-/cypress-plugin-retries-1.3.0.tgz#a2c1f49dce69b521cbb5ce3ab1a3a25acf41f08f" resolved "https://registry.yarnpkg.com/cypress-plugin-retries/-/cypress-plugin-retries-1.4.0.tgz#30477294a22e368c874d50dc282e657906080955"
integrity sha512-s2STd3vVeoIeKmdOvDhmWicARxK3cu7xF02MhH120wycUhdtR0SbAbo+zmcNnHquyshccE6cv17DfNvPOV7Rog== integrity sha512-Pudna9+dn0wp3flUVWt1ttn6hKTnD1MIBUSznYkw+uRv3JPNJhxHIv9cfxrZmig49/R1fIyGBVNORchtnFedEw==
cypress@^3.4.1: cypress@^3.6.0:
version "3.4.1" version "3.6.0"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-3.4.1.tgz#ca2e4e9864679da686c6a6189603efd409664c30" resolved "https://registry.yarnpkg.com/cypress/-/cypress-3.6.0.tgz#b7c88c169970aeb74a00182a1e8dc43a355d9eea"
integrity sha512-1HBS7t9XXzkt6QHbwfirWYty8vzxNMawGj1yI+Fu6C3/VZJ8UtUngMW6layqwYZzLTZV8tiDpdCNBypn78V4Dg== integrity sha512-ODhbOrH1XZx0DUoYmJSvOSbEQjycNOpFYe7jOnHkT1+sdsn2+uqwAjZ1x982q3H4R/5iZjpSd50gd/iw2bofzg==
dependencies: dependencies:
"@cypress/listr-verbose-renderer" "0.4.1" "@cypress/listr-verbose-renderer" "0.4.1"
"@cypress/xvfb" "1.2.4" "@cypress/xvfb" "1.2.4"
"@types/sizzle" "2.3.2"
arch "2.1.1" arch "2.1.1"
bluebird "3.5.0" bluebird "3.5.0"
cachedir "1.3.0" cachedir "1.3.0"
@ -1938,6 +1968,7 @@ cypress@^3.4.1:
request-progress "3.0.0" request-progress "3.0.0"
supports-color "5.5.0" supports-color "5.5.0"
tmp "0.1.0" tmp "0.1.0"
untildify "3.0.3"
url "0.11.0" url "0.11.0"
yauzl "2.10.0" yauzl "2.10.0"
@ -1965,10 +1996,10 @@ date-fns@^1.27.2:
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
date-fns@^2.5.0: date-fns@^2.6.0:
version "2.5.0" version "2.6.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.5.0.tgz#b939f17c2902ce81cffe449702ba22c0781b38ec" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.6.0.tgz#a5bc82e6a4c3995ae124b0ba1a71aec7b8cbd666"
integrity sha512-I6Tkis01//nRcmvMQw/MRE1HAtcuA5Ie6jGPb8bJZJub7494LGOObqkV3ParnsSVviAjk5C8mNKDqYVBzCopWg== integrity sha512-F55YxqRdEfP/eYQmQjLN798v0AwLjmZ8nMBjdQvNwEE3N/zWVrlkkqT+9seBlPlsbkybG4JmWg3Ee3dIV9BcGQ==
date-now@^0.1.4: date-now@^0.1.4:
version "0.1.4" version "0.1.4"
@ -2113,10 +2144,10 @@ dotenv@^4.0.0:
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d"
integrity sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0= integrity sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0=
dotenv@^8.1.0: dotenv@^8.2.0:
version "8.1.0" version "8.2.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.1.0.tgz#d811e178652bfb8a1e593c6dd704ec7e90d85ea2" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
integrity sha512-GUE3gqcDCaMltj2++g6bRQ5rBJWtkWTmqmD0fo1RnnMuUqHNCt2oTPeDnS9n6fKYvlhn7AeBkb38lymBtWBQdA== integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2: duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2:
version "0.1.4" version "0.1.4"
@ -2359,7 +2390,7 @@ extsprintf@^1.2.0:
faker@Marak/faker.js#master: faker@Marak/faker.js#master:
version "4.1.0" version "4.1.0"
resolved "https://codeload.github.com/Marak/faker.js/tar.gz/10bfb9f467b0ac2b8912ffc15690b50ef3244f09" resolved "https://codeload.github.com/Marak/faker.js/tar.gz/9fd8d7d37b398842d0784a116a340f7aa6afb89b"
fast-deep-equal@^2.0.1: fast-deep-equal@^2.0.1:
version "2.0.1" version "2.0.1"
@ -2576,6 +2607,17 @@ graphql-request@^1.8.2:
dependencies: dependencies:
cross-fetch "2.2.2" cross-fetch "2.2.2"
handlebars@^4.1.2:
version "4.4.5"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.4.5.tgz#1b1f94f9bfe7379adda86a8b73fb570265a0dddd"
integrity sha512-0Ce31oWVB7YidkaTq33ZxEbN+UDxMMgThvCe8ptgQViymL5DPis9uLdTA13MiRPhgvqyxIegugrP97iK3JeBHg==
dependencies:
neo-async "^2.6.0"
optimist "^0.6.1"
source-map "^0.6.1"
optionalDependencies:
uglify-js "^3.1.4"
har-schema@^2.0.0: har-schema@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
@ -3245,6 +3287,11 @@ lodash.once@^4.1.1:
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
lodash.uniqby@^4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302"
integrity sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=
lodash@4.17.15, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.4: lodash@4.17.15, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.4:
version "4.17.15" version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
@ -3376,6 +3423,11 @@ minimist@1.2.0, minimist@^1.1.0, minimist@^1.1.1, minimist@^1.2.0:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
minimist@~0.0.1:
version "0.0.10"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=
minipass@^2.2.1, minipass@^2.3.4: minipass@^2.2.1, minipass@^2.3.4:
version "2.3.5" version "2.3.5"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848"
@ -3482,6 +3534,11 @@ needle@^2.2.1:
iconv-lite "^0.4.4" iconv-lite "^0.4.4"
sax "^1.2.4" sax "^1.2.4"
neo-async@^2.6.0:
version "2.6.1"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c"
integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==
neo4j-driver@^1.7.6: neo4j-driver@^1.7.6:
version "1.7.6" version "1.7.6"
resolved "https://registry.yarnpkg.com/neo4j-driver/-/neo4j-driver-1.7.6.tgz#eccb135a71eba9048c68717444593a6424cffc49" resolved "https://registry.yarnpkg.com/neo4j-driver/-/neo4j-driver-1.7.6.tgz#eccb135a71eba9048c68717444593a6424cffc49"
@ -3523,10 +3580,10 @@ node-fetch@2.1.2:
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5"
integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U= integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=
node-fetch@^2.2.0: node-fetch@^2.2.0, node-fetch@^2.6.0:
version "2.3.0" version "2.6.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA== integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
node-pre-gyp@^0.10.0: node-pre-gyp@^0.10.0:
version "0.10.3" version "0.10.3"
@ -3696,6 +3753,14 @@ onetime@^1.0.0:
resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=
optimist@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY=
dependencies:
minimist "~0.0.1"
wordwrap "~0.0.2"
ora@^0.2.3: ora@^0.2.3:
version "0.2.3" version "0.2.3"
resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4" resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4"
@ -3777,6 +3842,11 @@ parse-asn1@^5.0.0:
pbkdf2 "^3.0.3" pbkdf2 "^3.0.3"
safe-buffer "^5.1.1" safe-buffer "^5.1.1"
parse-github-url@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/parse-github-url/-/parse-github-url-1.0.2.tgz#242d3b65cbcdda14bb50439e3242acf6971db395"
integrity sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==
parse-json@^4.0.0: parse-json@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
@ -4040,10 +4110,10 @@ regenerator-runtime@^0.12.0:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg== integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
regenerator-runtime@^0.13.2: regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.3:
version "0.13.2" version "0.13.3"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5"
integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA== integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==
regenerator-transform@^0.14.0: regenerator-transform@^0.14.0:
version "0.14.1" version "0.14.1"
@ -4228,16 +4298,11 @@ seed-random@~2.2.0:
resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54" resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54"
integrity sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ= integrity sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ=
"semver@2 || 3 || 4 || 5": "semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1:
version "5.7.0" version "5.7.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==
semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1:
version "5.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
semver@^6.3.0: semver@^6.3.0:
version "6.3.0" version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
@ -4379,6 +4444,11 @@ source-map@^0.5.0, source-map@^0.5.6, source-map@~0.5.3:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
source-map@^0.6.1, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
spdx-correct@^3.0.0: spdx-correct@^3.0.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4"
@ -4775,6 +4845,14 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
uglify-js@^3.1.4:
version "3.6.4"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.4.tgz#88cc880c6ed5cf9868fdfa0760654e7bed463f1d"
integrity sha512-9Yc2i881pF4BPGhjteCXQNaXx1DCwm3dtOyBaG2hitHjLWOczw/ki8vD1bqyT3u6K0Ms/FpCShkmfg+FtlOfYA==
dependencies:
commander "~2.20.3"
source-map "~0.6.1"
umd@^3.0.0: umd@^3.0.0:
version "3.0.3" version "3.0.3"
resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.3.tgz#aa9fe653c42b9097678489c01000acb69f0b26cf" resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.3.tgz#aa9fe653c42b9097678489c01000acb69f0b26cf"
@ -4842,6 +4920,11 @@ unset-value@^1.0.0:
has-value "^0.3.1" has-value "^0.3.1"
isobject "^3.0.0" isobject "^3.0.0"
untildify@3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/untildify/-/untildify-3.0.3.tgz#1e7b42b140bcfd922b22e70ca1265bfe3634c7c9"
integrity sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA==
upath@^1.1.1: upath@^1.1.1:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068"
@ -4965,6 +5048,11 @@ wide-align@^1.1.0:
dependencies: dependencies:
string-width "^1.0.2 || 2" string-width "^1.0.2 || 2"
wordwrap@~0.0.2:
version "0.0.3"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc=
wrappy@1: wrappy@1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"