mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2026-04-06 01:25:31 +00:00
Merge branch 'master' of github.com:Human-Connection/Human-Connection into update-legacy-migration-config
This commit is contained in:
commit
60659626c8
@ -83,11 +83,11 @@
|
|||||||
"metascraper-lang-detector": "^4.8.5",
|
"metascraper-lang-detector": "^4.8.5",
|
||||||
"metascraper-logo": "^5.6.5",
|
"metascraper-logo": "^5.6.5",
|
||||||
"metascraper-publisher": "^5.6.5",
|
"metascraper-publisher": "^5.6.5",
|
||||||
"metascraper-soundcloud": "^5.6.5",
|
"metascraper-soundcloud": "^5.6.7",
|
||||||
"metascraper-title": "^5.6.5",
|
"metascraper-title": "^5.6.5",
|
||||||
"metascraper-url": "^5.6.5",
|
"metascraper-url": "^5.6.5",
|
||||||
"metascraper-video": "^5.6.5",
|
"metascraper-video": "^5.6.5",
|
||||||
"metascraper-youtube": "^5.6.3",
|
"metascraper-youtube": "^5.6.7",
|
||||||
"minimatch": "^3.0.4",
|
"minimatch": "^3.0.4",
|
||||||
"neo4j-driver": "~1.7.6",
|
"neo4j-driver": "~1.7.6",
|
||||||
"neo4j-graphql-js": "^2.7.2",
|
"neo4j-graphql-js": "^2.7.2",
|
||||||
@ -100,6 +100,7 @@
|
|||||||
"slug": "~1.1.0",
|
"slug": "~1.1.0",
|
||||||
"trunc-html": "~1.1.2",
|
"trunc-html": "~1.1.2",
|
||||||
"uuid": "~3.3.3",
|
"uuid": "~3.3.3",
|
||||||
|
"xregexp": "^4.2.4",
|
||||||
"wait-on": "~3.3.0"
|
"wait-on": "~3.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -130,4 +131,4 @@
|
|||||||
"prettier": "~1.18.2",
|
"prettier": "~1.18.2",
|
||||||
"supertest": "~4.0.2"
|
"supertest": "~4.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -11,10 +11,8 @@ export default {
|
|||||||
Mutation: {
|
Mutation: {
|
||||||
CreatePost: setCreatedAt,
|
CreatePost: setCreatedAt,
|
||||||
CreateComment: setCreatedAt,
|
CreateComment: setCreatedAt,
|
||||||
CreateOrganization: setCreatedAt,
|
|
||||||
UpdateUser: setUpdatedAt,
|
UpdateUser: setUpdatedAt,
|
||||||
UpdatePost: setUpdatedAt,
|
UpdatePost: setUpdatedAt,
|
||||||
UpdateComment: setUpdatedAt,
|
UpdateComment: setUpdatedAt,
|
||||||
UpdateOrganization: setUpdatedAt,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,15 +22,5 @@ export default {
|
|||||||
const result = await resolve(root, args, context, info)
|
const result = await resolve(root, args, context, info)
|
||||||
return result
|
return result
|
||||||
},
|
},
|
||||||
CreateOrganization: async (resolve, root, args, context, info) => {
|
|
||||||
args.descriptionExcerpt = trunc(args.description, 120).html
|
|
||||||
const result = await resolve(root, args, context, info)
|
|
||||||
return result
|
|
||||||
},
|
|
||||||
UpdateOrganization: async (resolve, root, args, context, info) => {
|
|
||||||
args.descriptionExcerpt = trunc(args.description, 120).html
|
|
||||||
const result = await resolve(root, args, context, info)
|
|
||||||
return result
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,18 @@
|
|||||||
import cheerio from 'cheerio'
|
import cheerio from 'cheerio'
|
||||||
|
import { exec, build } from 'xregexp/xregexp-all.js'
|
||||||
// formats of a Hashtag:
|
// formats of a Hashtag:
|
||||||
// https://en.wikipedia.org/w/index.php?title=Hashtag&oldid=905141980#Style
|
// https://en.wikipedia.org/w/index.php?title=Hashtag&oldid=905141980#Style
|
||||||
// here:
|
// here:
|
||||||
// 0. Search for whole string.
|
// 0. Search for whole string.
|
||||||
// 1. Hashtag has only 'a-z', 'A-Z', and '0-9'.
|
// 1. Hashtag has only all unicode characters and '0-9'.
|
||||||
// 2. If it starts with a digit '0-9' than 'a-z', or 'A-Z' has to follow.
|
// 2. If it starts with a digit '0-9' than a unicode character has to follow.
|
||||||
const ID_REGEX = /^\/search\/hashtag\/(([a-zA-Z]+[a-zA-Z0-9]*)|([0-9]+[a-zA-Z]+[a-zA-Z0-9]*))$/g
|
const regX = build('^/search/hashtag/((\\pL+[\\pL0-9]*)|([0-9]+\\pL+[\\pL0-9]*))$')
|
||||||
|
|
||||||
export default function(content) {
|
export default function(content) {
|
||||||
if (!content) return []
|
if (!content) return []
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
// We can not search for class '.hashtag', because the classes are removed at the 'xss' middleware.
|
// We can not search for class '.hashtag', because the classes are removed at the 'xss' middleware.
|
||||||
// But we have to know, which Hashtags are removed from the content es well, so we search for the 'a' html-tag.
|
// But we have to know, which Hashtags are removed from the content as well, so we search for the 'a' html-tag.
|
||||||
const urls = $('a')
|
const urls = $('a')
|
||||||
.map((_, el) => {
|
.map((_, el) => {
|
||||||
return $(el).attr('href')
|
return $(el).attr('href')
|
||||||
@ -19,8 +20,8 @@ export default function(content) {
|
|||||||
.get()
|
.get()
|
||||||
const hashtags = []
|
const hashtags = []
|
||||||
urls.forEach(url => {
|
urls.forEach(url => {
|
||||||
let match
|
const match = exec(url, regX)
|
||||||
while ((match = ID_REGEX.exec(url)) != null) {
|
if (match != null) {
|
||||||
hashtags.push(match[1])
|
hashtags.push(match[1])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -28,9 +28,14 @@ describe('extractHashtags', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('ignores Hashtag links with not allowed character combinations', () => {
|
it('ignores Hashtag links with not allowed character combinations', () => {
|
||||||
|
// Allowed are all unicode letters '\pL' and all digits '0-9'. There haveto be at least one letter in it.
|
||||||
const content =
|
const content =
|
||||||
'<p>Something inspirational about <a href="/search/hashtag/AbcDefXyz0123456789!*(),2" class="hashtag" target="_blank">#AbcDefXyz0123456789!*(),2</a>, <a href="/search/hashtag/0123456789" class="hashtag" target="_blank">#0123456789</a>, <a href="/search/hashtag/0123456789a" class="hashtag" target="_blank">#0123456789a</a> and <a href="/search/hashtag/AbcDefXyz0123456789" target="_blank">#AbcDefXyz0123456789</a>.</p>'
|
'<p>Something inspirational about <a href="/search/hashtag/AbcDefXyz0123456789!*(),2" class="hashtag" target="_blank">#AbcDefXyz0123456789!*(),2</a>, <a href="/search/hashtag/0123456789" class="hashtag" target="_blank">#0123456789</a>, <a href="/search/hashtag/0123456789a" class="hashtag" target="_blank">#0123456789a</a>, <a href="/search/hashtag/AbcDefXyz0123456789" target="_blank">#AbcDefXyz0123456789</a>, and <a href="/search/hashtag/λαπ" target="_blank">#λαπ</a>.</p>'
|
||||||
expect(extractHashtags(content)).toEqual(['0123456789a', 'AbcDefXyz0123456789'])
|
expect(extractHashtags(content).sort()).toEqual([
|
||||||
|
'0123456789a',
|
||||||
|
'AbcDefXyz0123456789',
|
||||||
|
'λαπ',
|
||||||
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -25,10 +25,6 @@ export default {
|
|||||||
args.slug = args.slug || (await uniqueSlug(args.title, isUniqueFor(context, 'Post')))
|
args.slug = args.slug || (await uniqueSlug(args.title, isUniqueFor(context, 'Post')))
|
||||||
return resolve(root, args, context, info)
|
return resolve(root, args, context, info)
|
||||||
},
|
},
|
||||||
CreateOrganization: async (resolve, root, args, context, info) => {
|
|
||||||
args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'Organization')))
|
|
||||||
return resolve(root, args, context, info)
|
|
||||||
},
|
|
||||||
CreateCategory: async (resolve, root, args, context, info) => {
|
CreateCategory: async (resolve, root, args, context, info) => {
|
||||||
args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'Category')))
|
args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'Category')))
|
||||||
return resolve(root, args, context, info)
|
return resolve(root, args, context, info)
|
||||||
|
|||||||
@ -89,8 +89,10 @@ describe('slugify', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('SignupVerification', () => {
|
describe('SignupVerification', () => {
|
||||||
const mutation = `mutation($password: String!, $email: String!, $name: String!, $slug: String, $nonce: String!) {
|
const mutation = `mutation($password: String!, $email: String!, $name: String!, $slug: String, $nonce: String!, $termsAndConditionsAgreedVersion: String!) {
|
||||||
SignupVerification(email: $email, password: $password, name: $name, slug: $slug, nonce: $nonce) { slug }
|
SignupVerification(email: $email, password: $password, name: $name, slug: $slug, nonce: $nonce, termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion) {
|
||||||
|
slug
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -98,7 +100,12 @@ describe('slugify', () => {
|
|||||||
// required for SignupVerification
|
// required for SignupVerification
|
||||||
await instance.create('EmailAddress', { email: '123@example.org', nonce: '123456' })
|
await instance.create('EmailAddress', { email: '123@example.org', nonce: '123456' })
|
||||||
|
|
||||||
const defaultVariables = { nonce: '123456', password: 'yo', email: '123@example.org' }
|
const defaultVariables = {
|
||||||
|
nonce: '123456',
|
||||||
|
password: 'yo',
|
||||||
|
email: '123@example.org',
|
||||||
|
termsAndConditionsAgreedVersion: '0.0.1',
|
||||||
|
}
|
||||||
return authenticatedClient.request(mutation, { ...defaultVariables, ...variables })
|
return authenticatedClient.request(mutation, { ...defaultVariables, ...variables })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -80,9 +80,19 @@ module.exports = {
|
|||||||
notifications: {
|
notifications: {
|
||||||
type: 'relationship',
|
type: 'relationship',
|
||||||
relationship: 'NOTIFIED',
|
relationship: 'NOTIFIED',
|
||||||
target: 'Notification',
|
target: 'User',
|
||||||
direction: 'in',
|
direction: 'in',
|
||||||
},
|
},
|
||||||
|
termsAndConditionsAgreedVersion: {
|
||||||
|
type: 'string',
|
||||||
|
allow: [null],
|
||||||
|
},
|
||||||
|
/* termsAndConditionsAgreedAt: {
|
||||||
|
type: 'string',
|
||||||
|
isoDate: true,
|
||||||
|
allow: [null],
|
||||||
|
// required: true, TODO
|
||||||
|
}, */
|
||||||
shouted: {
|
shouted: {
|
||||||
type: 'relationship',
|
type: 'relationship',
|
||||||
relationship: 'SHOUTED',
|
relationship: 'SHOUTED',
|
||||||
|
|||||||
@ -14,11 +14,13 @@ export default applyScalars(
|
|||||||
query: {
|
query: {
|
||||||
exclude: [
|
exclude: [
|
||||||
'Badge',
|
'Badge',
|
||||||
|
'Embed',
|
||||||
'InvitationCode',
|
'InvitationCode',
|
||||||
'EmailAddress',
|
'EmailAddress',
|
||||||
'Notfication',
|
'Notfication',
|
||||||
'Statistics',
|
'Statistics',
|
||||||
'LoggedInUser',
|
'LoggedInUser',
|
||||||
|
'Location',
|
||||||
'SocialMedia',
|
'SocialMedia',
|
||||||
'NOTIFIED',
|
'NOTIFIED',
|
||||||
],
|
],
|
||||||
@ -27,12 +29,19 @@ export default applyScalars(
|
|||||||
mutation: {
|
mutation: {
|
||||||
exclude: [
|
exclude: [
|
||||||
'Badge',
|
'Badge',
|
||||||
|
'Embed',
|
||||||
'InvitationCode',
|
'InvitationCode',
|
||||||
'EmailAddress',
|
'EmailAddress',
|
||||||
'Notfication',
|
'Notfication',
|
||||||
|
'Post',
|
||||||
|
'Comment',
|
||||||
|
'Report',
|
||||||
'Statistics',
|
'Statistics',
|
||||||
'LoggedInUser',
|
'LoggedInUser',
|
||||||
|
'Location',
|
||||||
'SocialMedia',
|
'SocialMedia',
|
||||||
|
'User',
|
||||||
|
'EMOTED',
|
||||||
'NOTIFIED',
|
'NOTIFIED',
|
||||||
],
|
],
|
||||||
// add 'User' here as soon as possible
|
// add 'User' here as soon as possible
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { UserInputError } from 'apollo-server'
|
import { ForbiddenError, UserInputError } from 'apollo-server'
|
||||||
import uuid from 'uuid/v4'
|
import uuid from 'uuid/v4'
|
||||||
import { neode } from '../../bootstrap/neo4j'
|
import { neode } from '../../bootstrap/neo4j'
|
||||||
import fileUpload from './fileUpload'
|
import fileUpload from './fileUpload'
|
||||||
@ -77,6 +77,12 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
SignupVerification: async (object, args, context, resolveInfo) => {
|
SignupVerification: async (object, args, context, resolveInfo) => {
|
||||||
|
const { termsAndConditionsAgreedVersion } = args
|
||||||
|
const regEx = new RegExp(/^[0-9]+\.[0-9]+\.[0-9]+$/g)
|
||||||
|
if (!regEx.test(termsAndConditionsAgreedVersion)) {
|
||||||
|
throw new ForbiddenError('Invalid version format!')
|
||||||
|
}
|
||||||
|
|
||||||
let { nonce, email } = args
|
let { nonce, email } = args
|
||||||
email = email.toLowerCase()
|
email = email.toLowerCase()
|
||||||
const result = await instance.cypher(
|
const result = await instance.cypher(
|
||||||
|
|||||||
@ -34,6 +34,7 @@ describe('CreateInvitationCode', () => {
|
|||||||
name: 'Inviter',
|
name: 'Inviter',
|
||||||
email: 'inviter@example.org',
|
email: 'inviter@example.org',
|
||||||
password: '1234',
|
password: '1234',
|
||||||
|
termsAndConditionsAgreedVersion: '0.0.1',
|
||||||
}
|
}
|
||||||
action = async () => {
|
action = async () => {
|
||||||
const factory = Factory()
|
const factory = Factory()
|
||||||
@ -293,19 +294,25 @@ describe('Signup', () => {
|
|||||||
|
|
||||||
describe('SignupVerification', () => {
|
describe('SignupVerification', () => {
|
||||||
const mutation = `
|
const mutation = `
|
||||||
mutation($name: String!, $password: String!, $email: String!, $nonce: String!) {
|
mutation($name: String!, $password: String!, $email: String!, $nonce: String!, $termsAndConditionsAgreedVersion: String!) {
|
||||||
SignupVerification(name: $name, password: $password, email: $email, nonce: $nonce) {
|
SignupVerification(name: $name, password: $password, email: $email, nonce: $nonce, termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion) {
|
||||||
id
|
id
|
||||||
|
termsAndConditionsAgreedVersion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
describe('given valid password and email', () => {
|
describe('given valid password and email', () => {
|
||||||
const variables = {
|
let variables
|
||||||
nonce: '123456',
|
|
||||||
name: 'John Doe',
|
beforeEach(async () => {
|
||||||
password: '123',
|
variables = {
|
||||||
email: 'john@example.org',
|
nonce: '123456',
|
||||||
}
|
name: 'John Doe',
|
||||||
|
password: '123',
|
||||||
|
email: 'john@example.org',
|
||||||
|
termsAndConditionsAgreedVersion: '0.0.1',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
describe('unauthenticated', () => {
|
describe('unauthenticated', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@ -349,9 +356,9 @@ describe('SignupVerification', () => {
|
|||||||
describe('sending a valid nonce', () => {
|
describe('sending a valid nonce', () => {
|
||||||
it('creates a user account', async () => {
|
it('creates a user account', async () => {
|
||||||
const expected = {
|
const expected = {
|
||||||
SignupVerification: {
|
SignupVerification: expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
},
|
}),
|
||||||
}
|
}
|
||||||
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||||
})
|
})
|
||||||
@ -385,6 +392,24 @@ describe('SignupVerification', () => {
|
|||||||
const { records: emails } = await instance.cypher(cypher, { name: 'John Doe' })
|
const { records: emails } = await instance.cypher(cypher, { name: 'John Doe' })
|
||||||
expect(emails).toHaveLength(1)
|
expect(emails).toHaveLength(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('is version of terms and conditions saved correctly', async () => {
|
||||||
|
const expected = {
|
||||||
|
SignupVerification: expect.objectContaining({
|
||||||
|
termsAndConditionsAgreedVersion: '0.0.1',
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('rejects if version of terms and conditions has wrong format', async () => {
|
||||||
|
await expect(
|
||||||
|
client.request(mutation, {
|
||||||
|
...variables,
|
||||||
|
termsAndConditionsAgreedVersion: 'invalid version format',
|
||||||
|
}),
|
||||||
|
).rejects.toThrow('Invalid version format!')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('sending invalid nonce', () => {
|
describe('sending invalid nonce', () => {
|
||||||
|
|||||||
@ -43,14 +43,8 @@ export default {
|
|||||||
'MATCH (r:Post) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countPosts',
|
'MATCH (r:Post) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countPosts',
|
||||||
countComments:
|
countComments:
|
||||||
'MATCH (r:Comment) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countComments',
|
'MATCH (r:Comment) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countComments',
|
||||||
countNotifications:
|
countNotifications: 'MATCH ()-[r:NOTIFIED]->() RETURN COUNT(r) AS countNotifications',
|
||||||
'MATCH (r:Notification) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countNotifications',
|
countInvites: 'MATCH (r:InvitationCode) RETURN COUNT(r) AS countInvites',
|
||||||
countOrganizations:
|
|
||||||
'MATCH (r:Organization) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countOrganizations',
|
|
||||||
countProjects:
|
|
||||||
'MATCH (r:Project) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countProjects',
|
|
||||||
countInvites:
|
|
||||||
'MATCH (r:Invite) WHERE r.wasUsed <> true OR NOT exists(r.wasUsed) RETURN COUNT(r) AS countInvites',
|
|
||||||
countFollows: 'MATCH (:User)-[r:FOLLOWS]->(:User) RETURN COUNT(r) AS countFollows',
|
countFollows: 'MATCH (:User)-[r:FOLLOWS]->(:User) RETURN COUNT(r) AS countFollows',
|
||||||
countShouts: 'MATCH (:User)-[r:SHOUTED]->(:Post) RETURN COUNT(r) AS countShouts',
|
countShouts: 'MATCH (:User)-[r:SHOUTED]->(:Post) RETURN COUNT(r) AS countShouts',
|
||||||
}
|
}
|
||||||
@ -63,12 +57,6 @@ export default {
|
|||||||
countNotifications: queryOne(queries.countNotifications, session).then(
|
countNotifications: queryOne(queries.countNotifications, session).then(
|
||||||
res => res.countNotifications.low,
|
res => res.countNotifications.low,
|
||||||
),
|
),
|
||||||
countOrganizations: queryOne(queries.countOrganizations, session).then(
|
|
||||||
res => res.countOrganizations.low,
|
|
||||||
),
|
|
||||||
countProjects: queryOne(queries.countProjects, session).then(
|
|
||||||
res => res.countProjects.low,
|
|
||||||
),
|
|
||||||
countInvites: queryOne(queries.countInvites, session).then(res => res.countInvites.low),
|
countInvites: queryOne(queries.countInvites, session).then(res => res.countInvites.low),
|
||||||
countFollows: queryOne(queries.countFollows, session).then(res => res.countFollows.low),
|
countFollows: queryOne(queries.countFollows, session).then(res => res.countFollows.low),
|
||||||
countShouts: queryOne(queries.countShouts, session).then(res => res.countShouts.low),
|
countShouts: queryOne(queries.countShouts, session).then(res => res.countShouts.low),
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import encode from '../../jwt/encode'
|
import encode from '../../jwt/encode'
|
||||||
import bcrypt from 'bcryptjs'
|
import bcrypt from 'bcryptjs'
|
||||||
import { AuthenticationError } from 'apollo-server'
|
import { AuthenticationError } from 'apollo-server'
|
||||||
import { neo4jgraphql } from 'neo4j-graphql-js'
|
|
||||||
import { neode } from '../../bootstrap/neo4j'
|
import { neode } from '../../bootstrap/neo4j'
|
||||||
|
|
||||||
const instance = neode()
|
const instance = neode()
|
||||||
@ -12,9 +11,9 @@ export default {
|
|||||||
return Boolean(user && user.id)
|
return Boolean(user && user.id)
|
||||||
},
|
},
|
||||||
currentUser: async (object, params, ctx, resolveInfo) => {
|
currentUser: async (object, params, ctx, resolveInfo) => {
|
||||||
const { user } = ctx
|
if (!ctx.user) return null
|
||||||
if (!user) return null
|
const user = await instance.find('User', ctx.user.id)
|
||||||
return neo4jgraphql(object, { id: user.id }, ctx, resolveInfo, false)
|
return user.toJson()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { neo4jgraphql } from 'neo4j-graphql-js'
|
import { neo4jgraphql } from 'neo4j-graphql-js'
|
||||||
import fileUpload from './fileUpload'
|
import fileUpload from './fileUpload'
|
||||||
import { neode } from '../../bootstrap/neo4j'
|
import { neode } from '../../bootstrap/neo4j'
|
||||||
import { UserInputError } from 'apollo-server'
|
import { UserInputError, ForbiddenError } from 'apollo-server'
|
||||||
import Resolver from './helpers/Resolver'
|
import Resolver from './helpers/Resolver'
|
||||||
|
|
||||||
const instance = neode()
|
const instance = neode()
|
||||||
@ -88,6 +88,13 @@ export default {
|
|||||||
return blockedUser.toJson()
|
return blockedUser.toJson()
|
||||||
},
|
},
|
||||||
UpdateUser: async (object, args, context, resolveInfo) => {
|
UpdateUser: async (object, args, context, resolveInfo) => {
|
||||||
|
const { termsAndConditionsAgreedVersion } = args
|
||||||
|
if (termsAndConditionsAgreedVersion) {
|
||||||
|
const regEx = new RegExp(/^[0-9]+\.[0-9]+\.[0-9]+$/g)
|
||||||
|
if (!regEx.test(termsAndConditionsAgreedVersion)) {
|
||||||
|
throw new ForbiddenError('Invalid version format!')
|
||||||
|
}
|
||||||
|
}
|
||||||
args = await fileUpload(args, { file: 'avatarUpload', url: 'avatar' })
|
args = await fileUpload(args, { file: 'avatarUpload', url: 'avatar' })
|
||||||
try {
|
try {
|
||||||
const user = await instance.find('User', args.id)
|
const user = await instance.find('User', args.id)
|
||||||
@ -158,6 +165,7 @@ export default {
|
|||||||
},
|
},
|
||||||
...Resolver('User', {
|
...Resolver('User', {
|
||||||
undefinedToNull: [
|
undefinedToNull: [
|
||||||
|
'termsAndConditionsAgreedVersion',
|
||||||
'actorId',
|
'actorId',
|
||||||
'avatar',
|
'avatar',
|
||||||
'coverImg',
|
'coverImg',
|
||||||
@ -165,6 +173,8 @@ export default {
|
|||||||
'disabled',
|
'disabled',
|
||||||
'locationName',
|
'locationName',
|
||||||
'about',
|
'about',
|
||||||
|
'termsAndConditionsAgreedVersion',
|
||||||
|
// TODO: 'termsAndConditionsAgreedAt',
|
||||||
],
|
],
|
||||||
boolean: {
|
boolean: {
|
||||||
followedByCurrentUser:
|
followedByCurrentUser:
|
||||||
@ -197,8 +207,6 @@ export default {
|
|||||||
contributions: '-[:WROTE]->(related:Post)',
|
contributions: '-[:WROTE]->(related:Post)',
|
||||||
comments: '-[:WROTE]->(related:Comment)',
|
comments: '-[:WROTE]->(related:Comment)',
|
||||||
shouted: '-[:SHOUTED]->(related:Post)',
|
shouted: '-[:SHOUTED]->(related:Post)',
|
||||||
organizationsCreated: '-[:CREATED_ORGA]->(related:Organization)',
|
|
||||||
organizationsOwned: '-[:OWNING_ORGA]->(related:Organization)',
|
|
||||||
categories: '-[:CATEGORIZED]->(related:Category)',
|
categories: '-[:CATEGORIZED]->(related:Category)',
|
||||||
badges: '<-[:REWARDED]-(related:Badge)',
|
badges: '<-[:REWARDED]-(related:Badge)',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -75,22 +75,33 @@ describe('User', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('UpdateUser', () => {
|
describe('UpdateUser', () => {
|
||||||
const userParams = {
|
let userParams
|
||||||
email: 'user@example.org',
|
let variables
|
||||||
password: '1234',
|
|
||||||
id: 'u47',
|
beforeEach(async () => {
|
||||||
name: 'John Doe',
|
userParams = {
|
||||||
}
|
email: 'user@example.org',
|
||||||
const variables = {
|
password: '1234',
|
||||||
id: 'u47',
|
id: 'u47',
|
||||||
name: 'John Doughnut',
|
name: 'John Doe',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variables = {
|
||||||
|
id: 'u47',
|
||||||
|
name: 'John Doughnut',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const updateUserMutation = gql`
|
const updateUserMutation = gql`
|
||||||
mutation($id: ID!, $name: String) {
|
mutation($id: ID!, $name: String, $termsAndConditionsAgreedVersion: String) {
|
||||||
UpdateUser(id: $id, name: $name) {
|
UpdateUser(
|
||||||
|
id: $id
|
||||||
|
name: $name
|
||||||
|
termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
|
||||||
|
) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
termsAndConditionsAgreedVersion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
@ -159,6 +170,30 @@ describe('UpdateUser', () => {
|
|||||||
'child "name" fails because ["name" length must be at least 3 characters long]',
|
'child "name" fails because ["name" length must be at least 3 characters long]',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('given a new agreed version of terms and conditions', async () => {
|
||||||
|
variables = { ...variables, termsAndConditionsAgreedVersion: '0.0.2' }
|
||||||
|
const expected = {
|
||||||
|
data: {
|
||||||
|
UpdateUser: expect.objectContaining({
|
||||||
|
termsAndConditionsAgreedVersion: '0.0.2',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject(
|
||||||
|
expected,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('rejects if version of terms and conditions has wrong format', async () => {
|
||||||
|
variables = {
|
||||||
|
...variables,
|
||||||
|
termsAndConditionsAgreedVersion: 'invalid version format',
|
||||||
|
}
|
||||||
|
const { errors } = await mutate({ mutation: updateUserMutation, variables })
|
||||||
|
expect(errors[0]).toHaveProperty('message', 'Invalid version format!')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -44,8 +44,6 @@ type Statistics {
|
|||||||
countPosts: Int!
|
countPosts: Int!
|
||||||
countComments: Int!
|
countComments: Int!
|
||||||
countNotifications: Int!
|
countNotifications: Int!
|
||||||
countOrganizations: Int!
|
|
||||||
countProjects: Int!
|
|
||||||
countInvites: Int!
|
countInvites: Int!
|
||||||
countFollows: Int!
|
countFollows: Int!
|
||||||
countShouts: Int!
|
countShouts: Int!
|
||||||
@ -70,13 +68,9 @@ enum Deletable {
|
|||||||
|
|
||||||
enum ShoutTypeEnum {
|
enum ShoutTypeEnum {
|
||||||
Post
|
Post
|
||||||
Organization
|
|
||||||
Project
|
|
||||||
}
|
}
|
||||||
enum FollowTypeEnum {
|
enum FollowTypeEnum {
|
||||||
User
|
User
|
||||||
Organization
|
|
||||||
Project
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Reward {
|
type Reward {
|
||||||
@ -87,21 +81,6 @@ type Reward {
|
|||||||
badge: Badge @relation(name: "REWARDED", direction: "OUT")
|
badge: Badge @relation(name: "REWARDED", direction: "OUT")
|
||||||
}
|
}
|
||||||
|
|
||||||
type Organization {
|
|
||||||
id: ID!
|
|
||||||
createdBy: User @relation(name: "CREATED_ORGA", direction: "IN")
|
|
||||||
ownedBy: [User] @relation(name: "OWNING_ORGA", direction: "IN")
|
|
||||||
name: String!
|
|
||||||
slug: String
|
|
||||||
description: String!
|
|
||||||
descriptionExcerpt: String
|
|
||||||
deleted: Boolean
|
|
||||||
disabled: Boolean
|
|
||||||
|
|
||||||
tags: [Tag]! @relation(name: "TAGGED", direction: "OUT")
|
|
||||||
categories: [Category]! @relation(name: "CATEGORIZED", direction: "OUT")
|
|
||||||
}
|
|
||||||
|
|
||||||
type SharedInboxEndpoint {
|
type SharedInboxEndpoint {
|
||||||
id: ID!
|
id: ID!
|
||||||
uri: String
|
uri: String
|
||||||
|
|||||||
@ -8,3 +8,14 @@ type EMOTED @relation(name: "EMOTED") {
|
|||||||
createdAt: String
|
createdAt: String
|
||||||
updatedAt: String
|
updatedAt: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input _EMOTEDInput {
|
||||||
|
emotion: Emotion
|
||||||
|
createdAt: String
|
||||||
|
updatedAt: String
|
||||||
|
}
|
||||||
|
|
||||||
|
type Mutation {
|
||||||
|
AddPostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED
|
||||||
|
RemovePostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
type EmailAddress {
|
type EmailAddress {
|
||||||
id: ID!
|
email: ID!
|
||||||
email: String!
|
|
||||||
verifiedAt: String
|
verifiedAt: String
|
||||||
createdAt: String
|
createdAt: String
|
||||||
}
|
}
|
||||||
@ -19,5 +18,6 @@ type Mutation {
|
|||||||
avatarUpload: Upload
|
avatarUpload: Upload
|
||||||
locationName: String
|
locationName: String
|
||||||
about: String
|
about: String
|
||||||
|
termsAndConditionsAgreedVersion: String
|
||||||
): User
|
): User
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,6 +52,10 @@ type Post {
|
|||||||
@cypher(statement: "MATCH (this)<-[emoted:EMOTED]-(:User) RETURN COUNT(DISTINCT emoted)")
|
@cypher(statement: "MATCH (this)<-[emoted:EMOTED]-(:User) RETURN COUNT(DISTINCT emoted)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input _PostInput {
|
||||||
|
id: ID!
|
||||||
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
CreatePost(
|
CreatePost(
|
||||||
id: ID
|
id: ID
|
||||||
@ -77,6 +81,7 @@ type Mutation {
|
|||||||
language: String
|
language: String
|
||||||
categoryIds: [ID]
|
categoryIds: [ID]
|
||||||
): Post
|
): 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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
type Tag {
|
type Tag {
|
||||||
id: ID!
|
id: ID!
|
||||||
taggedPosts: [Post]! @relation(name: "TAGGED", direction: "IN")
|
taggedPosts: [Post]! @relation(name: "TAGGED", direction: "IN")
|
||||||
taggedOrganizations: [Organization]! @relation(name: "TAGGED", direction: "IN")
|
|
||||||
taggedCount: Int! @cypher(statement: "MATCH (this)<-[:TAGGED]-(p) RETURN COUNT(DISTINCT p)")
|
taggedCount: Int! @cypher(statement: "MATCH (this)<-[:TAGGED]-(p) RETURN COUNT(DISTINCT p)")
|
||||||
taggedCountUnique: Int! @cypher(statement: "MATCH (this)<-[:TAGGED]-(p)<-[:WROTE]-(u:User) RETURN COUNT(DISTINCT u)")
|
taggedCountUnique: Int! @cypher(statement: "MATCH (this)<-[:TAGGED]-(p)<-[:WROTE]-(u:User) RETURN COUNT(DISTINCT u)")
|
||||||
deleted: Boolean
|
deleted: Boolean
|
||||||
|
|||||||
@ -1,173 +1,174 @@
|
|||||||
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
|
||||||
|
|
||||||
friends: [User]! @relation(name: "FRIENDS", direction: "BOTH")
|
termsAndConditionsAgreedVersion: String
|
||||||
friendsCount: Int! @cypher(statement: "MATCH (this)<-[:FRIENDS]->(r:User) RETURN COUNT(DISTINCT r)")
|
|
||||||
|
|
||||||
following: [User]! @relation(name: "FOLLOWS", direction: "OUT")
|
friends: [User]! @relation(name: "FRIENDS", direction: "BOTH")
|
||||||
followingCount: Int! @cypher(statement: "MATCH (this)-[:FOLLOWS]->(r:User) RETURN COUNT(DISTINCT r)")
|
friendsCount: Int! @cypher(statement: "MATCH (this)<-[: FRIENDS]->(r: User) RETURN COUNT(DISTINCT r)")
|
||||||
|
|
||||||
followedBy: [User]! @relation(name: "FOLLOWS", direction: "IN")
|
following: [User]! @relation(name: "FOLLOWS", direction: "OUT")
|
||||||
followedByCount: Int! @cypher(statement: "MATCH (this)<-[:FOLLOWS]-(r:User) RETURN COUNT(DISTINCT r)")
|
followingCount: Int! @cypher(statement: "MATCH (this)-[: FOLLOWS]->(r: User) RETURN COUNT(DISTINCT r)")
|
||||||
|
|
||||||
# Is the currently logged in user following that user?
|
followedBy: [User]! @relation(name: "FOLLOWS", direction: "IN")
|
||||||
followedByCurrentUser: Boolean! @cypher(
|
followedByCount: Int! @cypher(statement: "MATCH (this)<-[: FOLLOWS]-(r: User) RETURN COUNT(DISTINCT r)")
|
||||||
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
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
#contributions: [WrittenPost]!
|
# Is the currently logged in user following that user?
|
||||||
#contributions2(first: Int = 10, offset: Int = 0): [WrittenPost2]!
|
followedByCurrentUser: Boolean! @cypher(
|
||||||
# @cypher(
|
statement: """
|
||||||
# statement: "MATCH (this)-[w:WROTE]->(p:Post) RETURN p as Post, w.timestamp as timestamp"
|
MATCH (this)<-[: FOLLOWS]-(u: User { id: $cypherParams.currentUserId})
|
||||||
# )
|
RETURN COUNT(u) >= 1
|
||||||
contributions: [Post]! @relation(name: "WROTE", direction: "OUT")
|
"""
|
||||||
contributionsCount: Int! @cypher(
|
)
|
||||||
statement: """
|
isBlocked: Boolean! @cypher(
|
||||||
MATCH (this)-[:WROTE]->(r:Post)
|
statement: """
|
||||||
WHERE NOT r.deleted = true AND NOT r.disabled = true
|
MATCH (this)<-[: BLOCKED]-(u: User { id: $cypherParams.currentUserId})
|
||||||
RETURN COUNT(r)
|
RETURN COUNT(u) >= 1
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
comments: [Comment]! @relation(name: "WROTE", direction: "OUT")
|
# contributions: [WrittenPost]!
|
||||||
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))")
|
# contributions2(first: Int = 10, offset: Int = 0): [WrittenPost2]!
|
||||||
|
# @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)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
shouted: [Post]! @relation(name: "SHOUTED", direction: "OUT")
|
comments: [Comment]! @relation(name: "WROTE", 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)")
|
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))")
|
||||||
|
|
||||||
organizationsCreated: [Organization] @relation(name: "CREATED_ORGA", direction: "OUT")
|
shouted: [Post]! @relation(name: "SHOUTED", direction: "OUT")
|
||||||
organizationsOwned: [Organization] @relation(name: "OWNING_ORGA", 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")
|
categories: [Category]! @relation(name: "CATEGORIZED", direction: "OUT")
|
||||||
|
|
||||||
badges: [Badge]! @relation(name: "REWARDED", direction: "IN")
|
badges: [Badge]! @relation(name: "REWARDED", direction: "IN")
|
||||||
badgesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)")
|
badgesCount: Int! @cypher(statement: "MATCH (this)<-[: REWARDED]-(r: Badge) RETURN COUNT(r)")
|
||||||
|
|
||||||
emotions: [EMOTED]
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
): User
|
termsAndConditionsAgreedVersion: 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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,8 @@ export default function create() {
|
|||||||
role: 'user',
|
role: 'user',
|
||||||
avatar: faker.internet.avatar(),
|
avatar: faker.internet.avatar(),
|
||||||
about: faker.lorem.paragraph(),
|
about: faker.lorem.paragraph(),
|
||||||
|
// termsAndConditionsAgreedAt: new Date().toISOString(),
|
||||||
|
termsAndConditionsAgreedVersion: '0.0.1',
|
||||||
}
|
}
|
||||||
defaults.slug = slugify(defaults.name, { lower: true })
|
defaults.slug = slugify(defaults.name, { lower: true })
|
||||||
args = {
|
args = {
|
||||||
|
|||||||
@ -681,7 +681,7 @@
|
|||||||
pirates "^4.0.0"
|
pirates "^4.0.0"
|
||||||
source-map-support "^0.5.9"
|
source-map-support "^0.5.9"
|
||||||
|
|
||||||
"@babel/runtime-corejs2@^7.5.5":
|
"@babel/runtime-corejs2@^7.2.0", "@babel/runtime-corejs2@^7.5.5":
|
||||||
version "7.5.5"
|
version "7.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.5.5.tgz#c3214c08ef20341af4187f1c9fbdc357fbec96b2"
|
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.5.5.tgz#c3214c08ef20341af4187f1c9fbdc357fbec96b2"
|
||||||
integrity sha512-FYATQVR00NSNi7mUfpPDp7E8RYMXDuO8gaix7u/w3GekfUinKgX1AcTxs7SoiEmoEW9mbpjrwqWSW6zCmw5h8A==
|
integrity sha512-FYATQVR00NSNi7mUfpPDp7E8RYMXDuO8gaix7u/w3GekfUinKgX1AcTxs7SoiEmoEW9mbpjrwqWSW6zCmw5h8A==
|
||||||
@ -5811,10 +5811,10 @@ mem@~5.1.1:
|
|||||||
mimic-fn "^2.1.0"
|
mimic-fn "^2.1.0"
|
||||||
p-is-promise "^2.1.0"
|
p-is-promise "^2.1.0"
|
||||||
|
|
||||||
memoize-one@~5.1.0:
|
memoize-one@~5.1.1:
|
||||||
version "5.1.0"
|
version "5.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.0.tgz#ce7af291c0e2fe041b709cac5e8c7b198c994286"
|
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
|
||||||
integrity sha512-p3tPVJNrjOkJ0vk0FRn6yv898qlQZct1rsQAXuwK9X5brNVajPv/y13ytrUByzSS8olyzeqCLX8BKEWmTmXa1A==
|
integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==
|
||||||
|
|
||||||
memorystream@^0.3.1:
|
memorystream@^0.3.1:
|
||||||
version "0.3.1"
|
version "0.3.1"
|
||||||
@ -5912,14 +5912,14 @@ metascraper-publisher@^5.6.5:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@metascraper/helpers" "^5.6.6"
|
"@metascraper/helpers" "^5.6.6"
|
||||||
|
|
||||||
metascraper-soundcloud@^5.6.5:
|
metascraper-soundcloud@^5.6.7:
|
||||||
version "5.6.6"
|
version "5.6.7"
|
||||||
resolved "https://registry.yarnpkg.com/metascraper-soundcloud/-/metascraper-soundcloud-5.6.6.tgz#7f609cd1f5ad4d466d8ac5e232bb3e2c9608ec24"
|
resolved "https://registry.yarnpkg.com/metascraper-soundcloud/-/metascraper-soundcloud-5.6.7.tgz#c06a5398c85ec2d8e139c7f2016451ebf54de039"
|
||||||
integrity sha512-8p3tI9BbD3MKv37t5gsh8E5ZDMZvZLKbLqK6hKEG2v0I0fzmpDQdUwhgo6NI4eAmOrPsyiPeQmu6sZLTWeK3OA==
|
integrity sha512-pHfhrAej8adCwK5Vo0IIOvoCTSWM1gSXeUQAwSyPGySME7NRMfgMq/TJSFF24Qih0UrE7u/4ogtGR3k3/Sno6g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@metascraper/helpers" "^5.6.6"
|
"@metascraper/helpers" "^5.6.6"
|
||||||
memoize-one "~5.1.0"
|
memoize-one "~5.1.1"
|
||||||
tldts "~5.3.2"
|
tldts "~5.4.0"
|
||||||
|
|
||||||
metascraper-title@^5.6.5:
|
metascraper-title@^5.6.5:
|
||||||
version "5.6.6"
|
version "5.6.6"
|
||||||
@ -5944,15 +5944,15 @@ metascraper-video@^5.6.5:
|
|||||||
"@metascraper/helpers" "^5.6.6"
|
"@metascraper/helpers" "^5.6.6"
|
||||||
lodash "~4.17.15"
|
lodash "~4.17.15"
|
||||||
|
|
||||||
metascraper-youtube@^5.6.3:
|
metascraper-youtube@^5.6.7:
|
||||||
version "5.6.6"
|
version "5.6.7"
|
||||||
resolved "https://registry.yarnpkg.com/metascraper-youtube/-/metascraper-youtube-5.6.6.tgz#ba098f1c92306e39645a11ace263c24d77223a18"
|
resolved "https://registry.yarnpkg.com/metascraper-youtube/-/metascraper-youtube-5.6.7.tgz#48c68aefb8b67aaae1df532dad8b4c0d6e8b7240"
|
||||||
integrity sha512-xV9uyMNUdXhsNMqnFk7bde2ygt2rKHJ8r+3Ft+GbgP+Zdw6PXOCy6/+yA/kF5nZouF7DOBBTw2bWEXqu08eCmQ==
|
integrity sha512-8/v+PwkNyVgRrD5SVVXH7aY1ld0NVlHlz/RhX6sre9OoYY70axa+zNttEp15zGcbOpS+g3RtEb7So/fbyb/Xcg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@metascraper/helpers" "^5.6.6"
|
"@metascraper/helpers" "^5.6.6"
|
||||||
get-video-id "~3.1.4"
|
get-video-id "~3.1.4"
|
||||||
is-reachable "~3.1.0"
|
is-reachable "~3.1.0"
|
||||||
memoize-one "~5.1.0"
|
memoize-one "~5.1.1"
|
||||||
p-locate "~4.1.0"
|
p-locate "~4.1.0"
|
||||||
|
|
||||||
metascraper@^4.10.3:
|
metascraper@^4.10.3:
|
||||||
@ -8154,17 +8154,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.3.2:
|
tldts-core@^5.4.0:
|
||||||
version "5.3.2"
|
version "5.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-5.3.2.tgz#3823653310c2dc6e51d00998098a72a3cc203ea9"
|
resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-5.4.0.tgz#bd69ca3ad96a78ab675c74684c6d0717929777ec"
|
||||||
integrity sha512-bGI7MvrFXnbNhSkKEKAjiRo+eoIuIsuzF/hOtpI7HLMLWamIGtrLuNv2nlBOdN/h6iB6B32B4MxmwPWTZ70TaQ==
|
integrity sha512-kfDnB7fcNgNUcn5k21TPM/MbXqJNt2bBGQRfGyE39H334Qk+qNcSqw9It3YPxvrA7msl7DQ8wvcIsa0y55auug==
|
||||||
|
|
||||||
tldts@~5.3.2:
|
tldts@~5.4.0:
|
||||||
version "5.3.2"
|
version "5.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/tldts/-/tldts-5.3.2.tgz#d7e9b31b70b7d7687a16fe31a18ec2ca8a33f852"
|
resolved "https://registry.yarnpkg.com/tldts/-/tldts-5.4.0.tgz#ded769383615341660243acf547f197eb48f6795"
|
||||||
integrity sha512-R96Q0sJoD1vMYZ5YI0om32AZW2SYDxWfcg0111gnJYIvfyVgHOwyYj2dLFhpkPCSH7AwwUe0MmewmUrQhJXRjQ==
|
integrity sha512-S1CmstJxRb6KK+uLHhMUXXkI/zjA20RGM9QYkLgDEj42C9Zmr+OLjzEqf4Id/EWppuLi1z9FdNsz8/qi/pLCGA==
|
||||||
dependencies:
|
dependencies:
|
||||||
tldts-core "^5.3.2"
|
tldts-core "^5.4.0"
|
||||||
|
|
||||||
tmp@^0.0.33:
|
tmp@^0.0.33:
|
||||||
version "0.0.33"
|
version "0.0.33"
|
||||||
@ -8862,6 +8862,13 @@ xmldom@0.1.19:
|
|||||||
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.19.tgz#631fc07776efd84118bf25171b37ed4d075a0abc"
|
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.19.tgz#631fc07776efd84118bf25171b37ed4d075a0abc"
|
||||||
integrity sha1-Yx/Ad3bv2EEYvyUXGzftTQdaCrw=
|
integrity sha1-Yx/Ad3bv2EEYvyUXGzftTQdaCrw=
|
||||||
|
|
||||||
|
xregexp@^4.2.4:
|
||||||
|
version "4.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.2.4.tgz#02a4aea056d65a42632c02f0233eab8e4d7e57ed"
|
||||||
|
integrity sha512-sO0bYdYeJAJBcJA8g7MJJX7UrOZIfJPd8U2SC7B2Dd/J24U0aQNoGp33shCaBSWeb0rD5rh6VBUIXOkGal1TZA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime-corejs2" "^7.2.0"
|
||||||
|
|
||||||
xtend@^4.0.1:
|
xtend@^4.0.1:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||||
|
|||||||
@ -3,4 +3,4 @@
|
|||||||
"NEO4J_URI": "bolt://localhost:7687",
|
"NEO4J_URI": "bolt://localhost:7687",
|
||||||
"NEO4J_USERNAME": "neo4j",
|
"NEO4J_USERNAME": "neo4j",
|
||||||
"NEO4J_PASSWORD": "letmein"
|
"NEO4J_PASSWORD": "letmein"
|
||||||
}
|
}
|
||||||
@ -31,6 +31,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",
|
||||||
role
|
role
|
||||||
})
|
})
|
||||||
cy.login({
|
cy.login({
|
||||||
|
|||||||
@ -1,4 +1,8 @@
|
|||||||
import { Given, When, Then } from "cypress-cucumber-preprocessor/steps";
|
import {
|
||||||
|
Given,
|
||||||
|
When,
|
||||||
|
Then
|
||||||
|
} from "cypress-cucumber-preprocessor/steps";
|
||||||
import helpers from "../../support/helpers";
|
import helpers from "../../support/helpers";
|
||||||
|
|
||||||
/* global cy */
|
/* global cy */
|
||||||
@ -9,12 +13,16 @@ let loginCredentials = {
|
|||||||
email: "peterpan@example.org",
|
email: "peterpan@example.org",
|
||||||
password: "1234"
|
password: "1234"
|
||||||
};
|
};
|
||||||
|
const termsAndConditionsAgreedVersion = {
|
||||||
|
termsAndConditionsAgreedVersion: "0.0.2"
|
||||||
|
};
|
||||||
const narratorParams = {
|
const narratorParams = {
|
||||||
id: 'id-of-peter-pan',
|
id: 'id-of-peter-pan',
|
||||||
name: "Peter Pan",
|
name: "Peter Pan",
|
||||||
slug: "peter-pan",
|
slug: "peter-pan",
|
||||||
avatar: "https://s3.amazonaws.com/uifaces/faces/twitter/nerrsoft/128.jpg",
|
avatar: "https://s3.amazonaws.com/uifaces/faces/twitter/nerrsoft/128.jpg",
|
||||||
...loginCredentials
|
...loginCredentials,
|
||||||
|
...termsAndConditionsAgreedVersion,
|
||||||
};
|
};
|
||||||
|
|
||||||
Given("I am logged in", () => {
|
Given("I am logged in", () => {
|
||||||
@ -28,25 +36,54 @@ Given("we have a selection of categories", () => {
|
|||||||
Given("we have a selection of tags and categories as well as posts", () => {
|
Given("we have a selection of tags and categories as well as posts", () => {
|
||||||
cy.createCategories("cat12")
|
cy.createCategories("cat12")
|
||||||
.factory()
|
.factory()
|
||||||
.create("Tag", { id: "Ecology" })
|
.create("Tag", {
|
||||||
.create("Tag", { id: "Nature" })
|
id: "Ecology"
|
||||||
.create("Tag", { id: "Democracy" });
|
})
|
||||||
|
.create("Tag", {
|
||||||
|
id: "Nature"
|
||||||
|
})
|
||||||
|
.create("Tag", {
|
||||||
|
id: "Democracy"
|
||||||
|
});
|
||||||
|
|
||||||
cy.factory()
|
cy.factory()
|
||||||
.create("User", { id: 'a1' })
|
.create("User", {
|
||||||
.create("Post", {authorId: 'a1', tagIds: [ "Ecology", "Nature", "Democracy" ], categoryIds: ["cat12"] })
|
id: 'a1'
|
||||||
.create("Post", {authorId: 'a1', tagIds: [ "Nature", "Democracy" ], categoryIds: ["cat121"] });
|
})
|
||||||
|
.create("Post", {
|
||||||
|
authorId: 'a1',
|
||||||
|
tagIds: ["Ecology", "Nature", "Democracy"],
|
||||||
|
categoryIds: ["cat12"]
|
||||||
|
})
|
||||||
|
.create("Post", {
|
||||||
|
authorId: 'a1',
|
||||||
|
tagIds: ["Nature", "Democracy"],
|
||||||
|
categoryIds: ["cat121"]
|
||||||
|
});
|
||||||
|
|
||||||
cy.factory()
|
cy.factory()
|
||||||
.create("User", { id: 'a2'})
|
.create("User", {
|
||||||
.create("Post", { authorId: 'a2', tagIds: ['Nature', 'Democracy'], categoryIds: ["cat12"] });
|
id: 'a2'
|
||||||
|
})
|
||||||
|
.create("Post", {
|
||||||
|
authorId: 'a2',
|
||||||
|
tagIds: ['Nature', 'Democracy'],
|
||||||
|
categoryIds: ["cat12"]
|
||||||
|
});
|
||||||
cy.factory()
|
cy.factory()
|
||||||
.create("Post", { authorId: narratorParams.id, tagIds: ['Democracy'], categoryIds: ["cat122"] })
|
.create("Post", {
|
||||||
|
authorId: narratorParams.id,
|
||||||
|
tagIds: ['Democracy'],
|
||||||
|
categoryIds: ["cat122"]
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
Given("we have the following user accounts:", table => {
|
Given("we have the following user accounts:", table => {
|
||||||
table.hashes().forEach(params => {
|
table.hashes().forEach(params => {
|
||||||
cy.factory().create("User", params);
|
cy.factory().create("User", {
|
||||||
|
...params,
|
||||||
|
...termsAndConditionsAgreedVersion
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -57,7 +94,8 @@ Given("I have a user account", () => {
|
|||||||
Given("my user account has the role {string}", role => {
|
Given("my user account has the role {string}", role => {
|
||||||
cy.factory().create("User", {
|
cy.factory().create("User", {
|
||||||
role,
|
role,
|
||||||
...loginCredentials
|
...loginCredentials,
|
||||||
|
...termsAndConditionsAgreedVersion,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -117,7 +155,9 @@ Given("I previously switched the language to {string}", name => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Then("the whole user interface appears in {string}", name => {
|
Then("the whole user interface appears in {string}", name => {
|
||||||
const { code } = helpers.getLangByName(name);
|
const {
|
||||||
|
code
|
||||||
|
} = helpers.getLangByName(name);
|
||||||
cy.get(`html[lang=${code}]`);
|
cy.get(`html[lang=${code}]`);
|
||||||
cy.getCookie("locale").should("have.property", "value", code);
|
cy.getCookie("locale").should("have.property", "value", code);
|
||||||
});
|
});
|
||||||
@ -151,7 +191,9 @@ Given("we have the following posts in our database:", table => {
|
|||||||
icon: "smile"
|
icon: "smile"
|
||||||
})
|
})
|
||||||
|
|
||||||
table.hashes().forEach(({ ...postAttributes }, i) => {
|
table.hashes().forEach(({
|
||||||
|
...postAttributes
|
||||||
|
}, i) => {
|
||||||
postAttributes = {
|
postAttributes = {
|
||||||
...postAttributes,
|
...postAttributes,
|
||||||
deleted: Boolean(postAttributes.deleted),
|
deleted: Boolean(postAttributes.deleted),
|
||||||
@ -229,10 +271,15 @@ Then("the first post on the landing page has the title:", title => {
|
|||||||
Then(
|
Then(
|
||||||
"the page {string} returns a 404 error with a message:",
|
"the page {string} returns a 404 error with a message:",
|
||||||
(route, message) => {
|
(route, message) => {
|
||||||
cy.request({ url: route, failOnStatusCode: false })
|
cy.request({
|
||||||
|
url: route,
|
||||||
|
failOnStatusCode: false
|
||||||
|
})
|
||||||
.its("status")
|
.its("status")
|
||||||
.should("eq", 404);
|
.should("eq", 404);
|
||||||
cy.visit(route, { failOnStatusCode: false });
|
cy.visit(route, {
|
||||||
|
failOnStatusCode: false
|
||||||
|
});
|
||||||
cy.get(".error").should("contain", message);
|
cy.get(".error").should("contain", message);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -240,7 +287,10 @@ Then(
|
|||||||
Given("my user account has the following login credentials:", table => {
|
Given("my user account has the following login credentials:", table => {
|
||||||
loginCredentials = table.hashes()[0];
|
loginCredentials = table.hashes()[0];
|
||||||
cy.debug();
|
cy.debug();
|
||||||
cy.factory().create("User", loginCredentials);
|
cy.factory().create("User", {
|
||||||
|
...termsAndConditionsAgreedVersion,
|
||||||
|
...loginCredentials
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
When("I fill the password form with:", table => {
|
When("I fill the password form with:", table => {
|
||||||
@ -259,7 +309,9 @@ When("submit the form", () => {
|
|||||||
|
|
||||||
Then("I cannot login anymore with password {string}", password => {
|
Then("I cannot login anymore with password {string}", password => {
|
||||||
cy.reload();
|
cy.reload();
|
||||||
const { email } = loginCredentials;
|
const {
|
||||||
|
email
|
||||||
|
} = loginCredentials;
|
||||||
cy.visit(`/login`);
|
cy.visit(`/login`);
|
||||||
cy.get("input[name=email]")
|
cy.get("input[name=email]")
|
||||||
.trigger("focus")
|
.trigger("focus")
|
||||||
@ -280,14 +332,22 @@ Then("I can login successfully with password {string}", password => {
|
|||||||
cy.reload();
|
cy.reload();
|
||||||
cy.login({
|
cy.login({
|
||||||
...loginCredentials,
|
...loginCredentials,
|
||||||
...{ password }
|
...{
|
||||||
|
password
|
||||||
|
}
|
||||||
});
|
});
|
||||||
cy.get(".iziToast-wrapper").should("contain", "You are logged in!");
|
cy.get(".iziToast-wrapper").should("contain", "You are logged in!");
|
||||||
});
|
});
|
||||||
|
|
||||||
When("I log in with the following credentials:", table => {
|
When("I log in with the following credentials:", table => {
|
||||||
const { email, password } = table.hashes()[0];
|
const {
|
||||||
cy.login({ email, password });
|
email,
|
||||||
|
password
|
||||||
|
} = table.hashes()[0];
|
||||||
|
cy.login({
|
||||||
|
email,
|
||||||
|
password
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
When("open the notification menu and click on the first item", () => {
|
When("open the notification menu and click on the first item", () => {
|
||||||
@ -337,12 +397,14 @@ Then("there are no notifications in the top menu", () => {
|
|||||||
Given("there is an annoying user called {string}", name => {
|
Given("there is an annoying user called {string}", name => {
|
||||||
const annoyingParams = {
|
const annoyingParams = {
|
||||||
email: "spammy-spammer@example.org",
|
email: "spammy-spammer@example.org",
|
||||||
password: "1234"
|
password: "1234",
|
||||||
|
...termsAndConditionsAgreedVersion
|
||||||
};
|
};
|
||||||
cy.factory().create("User", {
|
cy.factory().create("User", {
|
||||||
...annoyingParams,
|
...annoyingParams,
|
||||||
id: "annoying-user",
|
id: "annoying-user",
|
||||||
name
|
name,
|
||||||
|
...termsAndConditionsAgreedVersion,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -364,7 +426,9 @@ When(
|
|||||||
cy.get(".user-content-menu .content-menu-trigger").click();
|
cy.get(".user-content-menu .content-menu-trigger").click();
|
||||||
cy.get(".popover .ds-menu-item-link")
|
cy.get(".popover .ds-menu-item-link")
|
||||||
.contains(button)
|
.contains(button)
|
||||||
.click({ force: true });
|
.click({
|
||||||
|
force: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -379,10 +443,14 @@ When("I navigate to my {string} settings page", settingsPage => {
|
|||||||
|
|
||||||
Given("I follow the user {string}", name => {
|
Given("I follow the user {string}", name => {
|
||||||
cy.neode()
|
cy.neode()
|
||||||
.first("User", { name })
|
.first("User", {
|
||||||
|
name
|
||||||
|
})
|
||||||
.then(followed => {
|
.then(followed => {
|
||||||
cy.neode()
|
cy.neode()
|
||||||
.first("User", { name: narratorParams.name })
|
.first("User", {
|
||||||
|
name: narratorParams.name
|
||||||
|
})
|
||||||
.relateTo(followed, "following");
|
.relateTo(followed, "following");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -390,7 +458,11 @@ Given("I follow the user {string}", name => {
|
|||||||
Given('"Spammy Spammer" wrote a post {string}', title => {
|
Given('"Spammy Spammer" wrote a post {string}', title => {
|
||||||
cy.createCategories("cat21")
|
cy.createCategories("cat21")
|
||||||
.factory()
|
.factory()
|
||||||
.create("Post", { authorId: 'annoying-user', title, categoryIds: ["cat21"] });
|
.create("Post", {
|
||||||
|
authorId: 'annoying-user',
|
||||||
|
title,
|
||||||
|
categoryIds: ["cat21"]
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Then("the list of posts of this user is empty", () => {
|
Then("the list of posts of this user is empty", () => {
|
||||||
@ -409,23 +481,37 @@ Then("nobody is following the user profile anymore", () => {
|
|||||||
Given("I wrote a post {string}", title => {
|
Given("I wrote a post {string}", title => {
|
||||||
cy.createCategories(`cat213`, title)
|
cy.createCategories(`cat213`, title)
|
||||||
.factory()
|
.factory()
|
||||||
.create("Post", { authorId: narratorParams.id, title, categoryIds: ["cat213"] });
|
.create("Post", {
|
||||||
|
authorId: narratorParams.id,
|
||||||
|
title,
|
||||||
|
categoryIds: ["cat213"]
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
When("I block the user {string}", name => {
|
When("I block the user {string}", name => {
|
||||||
cy.neode()
|
cy.neode()
|
||||||
.first("User", { name })
|
.first("User", {
|
||||||
|
name
|
||||||
|
})
|
||||||
.then(blocked => {
|
.then(blocked => {
|
||||||
cy.neode()
|
cy.neode()
|
||||||
.first("User", { name: narratorParams.name })
|
.first("User", {
|
||||||
|
name: narratorParams.name
|
||||||
|
})
|
||||||
.relateTo(blocked, "blocked");
|
.relateTo(blocked, "blocked");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
When("I log in with:", table => {
|
When("I log in with:", table => {
|
||||||
const [firstRow] = table.hashes();
|
const [firstRow] = table.hashes();
|
||||||
const { Email, Password } = firstRow;
|
const {
|
||||||
cy.login({ email: Email, password: Password });
|
Email,
|
||||||
|
Password
|
||||||
|
} = firstRow;
|
||||||
|
cy.login({
|
||||||
|
email: Email,
|
||||||
|
password: Password
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Then("I see only one post with the title {string}", title => {
|
Then("I see only one post with the title {string}", title => {
|
||||||
@ -433,4 +519,4 @@ Then("I see only one post with the title {string}", title => {
|
|||||||
.find(".post-link")
|
.find(".post-link")
|
||||||
.should("have.length", 1);
|
.should("have.length", 1);
|
||||||
cy.get(".main-container").contains(".post-link", title);
|
cy.get(".main-container").contains(".post-link", title);
|
||||||
});
|
});
|
||||||
@ -33,20 +33,20 @@ export_collection "categories"
|
|||||||
export_collection "comments"
|
export_collection "comments"
|
||||||
export_collection_query "contributions" '{"type": "DELETED"}' "DELETED"
|
export_collection_query "contributions" '{"type": "DELETED"}' "DELETED"
|
||||||
export_collection_query "contributions" '{"type": "post"}' "post"
|
export_collection_query "contributions" '{"type": "post"}' "post"
|
||||||
export_collection_query "contributions" '{"type": "cando"}' "cando"
|
# export_collection_query "contributions" '{"type": "cando"}' "cando"
|
||||||
export_collection "emotions"
|
export_collection "emotions"
|
||||||
export_collection_query "follows" '{"foreignService": "organizations"}' "organizations"
|
# export_collection_query "follows" '{"foreignService": "organizations"}' "organizations"
|
||||||
export_collection_query "follows" '{"foreignService": "users"}' "users"
|
export_collection_query "follows" '{"foreignService": "users"}' "users"
|
||||||
export_collection "invites"
|
# export_collection "invites"
|
||||||
export_collection "organizations"
|
# export_collection "organizations"
|
||||||
export_collection "pages"
|
# export_collection "pages"
|
||||||
export_collection "projects"
|
# export_collection "projects"
|
||||||
export_collection "settings"
|
# export_collection "settings"
|
||||||
export_collection "shouts"
|
export_collection "shouts"
|
||||||
export_collection "status"
|
# export_collection "status"
|
||||||
export_collection "users"
|
export_collection "users"
|
||||||
export_collection "userscandos"
|
# export_collection "userscandos"
|
||||||
export_collection "usersettings"
|
# export_collection "usersettings"
|
||||||
|
|
||||||
# Close SSH Tunnel
|
# Close SSH Tunnel
|
||||||
ssh -S my-ctrl-socket -O check -l ${SSH_USERNAME} ${SSH_HOST}
|
ssh -S my-ctrl-socket -O check -l ${SSH_USERNAME} ${SSH_HOST}
|
||||||
|
|||||||
@ -125,7 +125,6 @@
|
|||||||
[ ] wasSeeded: { type: Boolean }
|
[ ] wasSeeded: { type: Boolean }
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as post
|
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as post
|
||||||
MERGE (p:Post {id: post._id["$oid"]})
|
MERGE (p:Post {id: post._id["$oid"]})
|
||||||
ON CREATE SET
|
ON CREATE SET
|
||||||
@ -148,6 +147,10 @@ MATCH (c:Category {id: categoryId})
|
|||||||
MERGE (p)-[:CATEGORIZED]->(c)
|
MERGE (p)-[:CATEGORIZED]->(c)
|
||||||
WITH p, post.tags AS tags
|
WITH p, post.tags AS tags
|
||||||
UNWIND tags AS tag
|
UNWIND tags AS tag
|
||||||
MERGE (t:Tag {id: apoc.text.clean(tag), disabled: false, deleted: false})
|
WITH apoc.text.replace(tag, '[^\\p{L}0-9]', '') as tagNoSpacesAllowed
|
||||||
|
CALL apoc.when(tagNoSpacesAllowed =~ '^((\\p{L}+[\\p{L}0-9]*)|([0-9]+\\p{L}+[\\p{L}0-9]*))$', 'RETURN tagNoSpacesAllowed', '', {tagNoSpacesAllowed: tagNoSpacesAllowed})
|
||||||
|
YIELD value as validated
|
||||||
|
WHERE validated.tagNoSpacesAllowed IS NOT NULL
|
||||||
|
MERGE (t:Tag { id: validated.tagNoSpacesAllowed, disabled: false, deleted: false })
|
||||||
MERGE (p)-[:TAGGED]->(t)
|
MERGE (p)-[:TAGGED]->(t)
|
||||||
;
|
;
|
||||||
|
|||||||
@ -26,13 +26,11 @@ CREATE CONSTRAINT ON (p:Post) ASSERT p.id IS UNIQUE;
|
|||||||
CREATE CONSTRAINT ON (c:Comment) ASSERT c.id IS UNIQUE;
|
CREATE CONSTRAINT ON (c:Comment) ASSERT c.id IS UNIQUE;
|
||||||
CREATE CONSTRAINT ON (c:Category) ASSERT c.id IS UNIQUE;
|
CREATE CONSTRAINT ON (c:Category) ASSERT c.id IS UNIQUE;
|
||||||
CREATE CONSTRAINT ON (u:User) ASSERT u.id IS UNIQUE;
|
CREATE CONSTRAINT ON (u:User) ASSERT u.id IS UNIQUE;
|
||||||
CREATE CONSTRAINT ON (o:Organization) ASSERT o.id IS UNIQUE;
|
|
||||||
CREATE CONSTRAINT ON (t:Tag) ASSERT t.id IS UNIQUE;
|
CREATE CONSTRAINT ON (t:Tag) ASSERT t.id IS UNIQUE;
|
||||||
|
|
||||||
CREATE CONSTRAINT ON (p:Post) ASSERT p.slug IS UNIQUE;
|
CREATE CONSTRAINT ON (p:Post) ASSERT p.slug IS UNIQUE;
|
||||||
CREATE CONSTRAINT ON (c:Category) ASSERT c.slug IS UNIQUE;
|
CREATE CONSTRAINT ON (c:Category) ASSERT c.slug IS UNIQUE;
|
||||||
CREATE CONSTRAINT ON (u:User) ASSERT u.slug IS UNIQUE;
|
CREATE CONSTRAINT ON (u:User) ASSERT u.slug IS UNIQUE;
|
||||||
CREATE CONSTRAINT ON (o:Organization) ASSERT o.slug IS UNIQUE;
|
|
||||||
|
|
||||||
CREATE CONSTRAINT ON (e:EmailAddress) ASSERT e.email IS UNIQUE;
|
CREATE CONSTRAINT ON (e:EmailAddress) ASSERT e.email IS UNIQUE;
|
||||||
' | cypher-shell
|
' | cypher-shell
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import { Editor, EditorContent } from 'tiptap'
|
|||||||
import { History } from 'tiptap-extensions'
|
import { History } from 'tiptap-extensions'
|
||||||
import linkify from 'linkify-it'
|
import linkify from 'linkify-it'
|
||||||
import stringHash from 'string-hash'
|
import stringHash from 'string-hash'
|
||||||
|
import { replace, build } from 'xregexp/xregexp-all.js'
|
||||||
|
|
||||||
import * as key from '../../constants/keycodes'
|
import * as key from '../../constants/keycodes'
|
||||||
import { HASHTAG, MENTION } from '../../constants/editor'
|
import { HASHTAG, MENTION } from '../../constants/editor'
|
||||||
@ -214,8 +215,9 @@ export default {
|
|||||||
},
|
},
|
||||||
sanitizeQuery(query) {
|
sanitizeQuery(query) {
|
||||||
if (this.suggestionType === HASHTAG) {
|
if (this.suggestionType === HASHTAG) {
|
||||||
// remove all not allowed chars
|
// remove all non unicode letters and non digits
|
||||||
query = query.replace(/[^a-zA-Z0-9]/gm, '')
|
const regexMatchAllNonUnicodeLettersOrDigits = build('[^\\pL0-9]')
|
||||||
|
query = replace(query, regexMatchAllNonUnicodeLettersOrDigits, '', 'all')
|
||||||
// if the query is only made of digits, make it empty
|
// if the query is only made of digits, make it empty
|
||||||
return query.replace(/[0-9]/gm, '') === '' ? '' : query
|
return query.replace(/[0-9]/gm, '') === '' ? '' : query
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,15 +7,15 @@
|
|||||||
:class="{ 'is-selected': navigatedItemIndex === index }"
|
:class="{ 'is-selected': navigatedItemIndex === index }"
|
||||||
@click="selectItem(item)"
|
@click="selectItem(item)"
|
||||||
>
|
>
|
||||||
{{ createItemLabel(item) }}
|
{{ createItemLabel(item) | truncate(50) }}
|
||||||
</li>
|
</li>
|
||||||
<template v-if="isHashtag">
|
<template v-if="isHashtag">
|
||||||
<li v-if="!query" class="suggestion-list__item hint">
|
<li v-if="!query" class="suggestion-list__item hint">{{ $t('editor.hashtag.addLetter') }}</li>
|
||||||
{{ $t('editor.hashtag.addLetter') }}
|
|
||||||
</li>
|
|
||||||
<template v-else-if="!filteredItems.find(el => el.id === query)">
|
<template v-else-if="!filteredItems.find(el => el.id === query)">
|
||||||
<li class="suggestion-list__item hint">{{ $t('editor.hashtag.addHashtag') }}</li>
|
<li class="suggestion-list__item hint">{{ $t('editor.hashtag.addHashtag') }}</li>
|
||||||
<li class="suggestion-list__item" @click="selectItem({ id: query })">#{{ query }}</li>
|
<li class="suggestion-list__item" @click="selectItem({ id: query })">
|
||||||
|
#{{ query | truncate(50) }}
|
||||||
|
</li>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="isMention">
|
<template v-else-if="isMention">
|
||||||
|
|||||||
@ -77,6 +77,7 @@ describe('CreateUserAccount', () => {
|
|||||||
email: 'sixseven@example.org',
|
email: 'sixseven@example.org',
|
||||||
nonce: '666777',
|
nonce: '666777',
|
||||||
password: 'hellopassword',
|
password: 'hellopassword',
|
||||||
|
termsAndConditionsAgreedVersion: '0.0.2',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
|
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
|
||||||
|
|||||||
@ -55,7 +55,10 @@
|
|||||||
v-model="termsAndConditionsConfirmed"
|
v-model="termsAndConditionsConfirmed"
|
||||||
:checked="termsAndConditionsConfirmed"
|
:checked="termsAndConditionsConfirmed"
|
||||||
/>
|
/>
|
||||||
<label for="checkbox" v-html="$t('site.termsAndConditionsConfirmed')"></label>
|
<label
|
||||||
|
for="checkbox"
|
||||||
|
v-html="$t('termsAndConditions.termsAndConditionsConfirmed')"
|
||||||
|
></label>
|
||||||
</ds-text>
|
</ds-text>
|
||||||
|
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
@ -84,9 +87,24 @@ import gql from 'graphql-tag'
|
|||||||
import PasswordStrength from '../Password/Strength'
|
import PasswordStrength from '../Password/Strength'
|
||||||
import { SweetalertIcon } from 'vue-sweetalert-icons'
|
import { SweetalertIcon } from 'vue-sweetalert-icons'
|
||||||
import PasswordForm from '~/components/utils/PasswordFormHelper'
|
import PasswordForm from '~/components/utils/PasswordFormHelper'
|
||||||
|
import { VERSION } from '~/constants/terms-and-conditions-version.js'
|
||||||
|
|
||||||
|
/* TODO: hier muss die version rein */
|
||||||
export const SignupVerificationMutation = gql`
|
export const SignupVerificationMutation = gql`
|
||||||
mutation($nonce: String!, $name: String!, $email: String!, $password: String!) {
|
mutation(
|
||||||
SignupVerification(nonce: $nonce, email: $email, name: $name, password: $password) {
|
$nonce: String!
|
||||||
|
$name: String!
|
||||||
|
$email: String!
|
||||||
|
$password: String!
|
||||||
|
$termsAndConditionsAgreedVersion: String!
|
||||||
|
) {
|
||||||
|
SignupVerification(
|
||||||
|
nonce: $nonce
|
||||||
|
email: $email
|
||||||
|
name: $name
|
||||||
|
password: $password
|
||||||
|
termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
|
||||||
|
) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
slug
|
slug
|
||||||
@ -135,10 +153,11 @@ export default {
|
|||||||
async submit() {
|
async submit() {
|
||||||
const { name, password, about } = this.formData
|
const { name, password, about } = this.formData
|
||||||
const { email, nonce } = this
|
const { email, nonce } = this
|
||||||
|
const termsAndConditionsAgreedVersion = VERSION
|
||||||
try {
|
try {
|
||||||
await this.$apollo.mutate({
|
await this.$apollo.mutate({
|
||||||
mutation: SignupVerificationMutation,
|
mutation: SignupVerificationMutation,
|
||||||
variables: { name, password, about, email, nonce },
|
variables: { name, password, about, email, nonce, termsAndConditionsAgreedVersion },
|
||||||
})
|
})
|
||||||
this.success = true
|
this.success = true
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
1
webapp/constants/terms-and-conditions-version.js
Normal file
1
webapp/constants/terms-and-conditions-version.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const VERSION = '0.0.2'
|
||||||
@ -6,9 +6,9 @@
|
|||||||
<ds-flex class="main-navigation-flex" centered>
|
<ds-flex class="main-navigation-flex" centered>
|
||||||
<ds-flex-item :width="{ lg: '3.5%' }" />
|
<ds-flex-item :width="{ lg: '3.5%' }" />
|
||||||
<ds-flex-item :width="{ base: '80%', sm: '80%', md: '80%', lg: '15%' }">
|
<ds-flex-item :width="{ base: '80%', sm: '80%', md: '80%', lg: '15%' }">
|
||||||
<a @click="redirectToRoot">
|
<nuxt-link :to="{ name: 'index' }">
|
||||||
<ds-logo />
|
<ds-logo />
|
||||||
</a>
|
</nuxt-link>
|
||||||
</ds-flex-item>
|
</ds-flex-item>
|
||||||
<ds-flex-item
|
<ds-flex-item
|
||||||
:width="{ base: '20%', sm: '20%', md: '20%', lg: '0%' }"
|
:width="{ base: '20%', sm: '20%', md: '20%', lg: '0%' }"
|
||||||
@ -147,7 +147,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters, mapActions, mapMutations } from 'vuex'
|
import { mapGetters, mapActions } from 'vuex'
|
||||||
import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch'
|
import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch'
|
||||||
import SearchInput from '~/components/SearchInput.vue'
|
import SearchInput from '~/components/SearchInput.vue'
|
||||||
import Modal from '~/components/Modal'
|
import Modal from '~/components/Modal'
|
||||||
@ -183,8 +183,6 @@ export default {
|
|||||||
isAdmin: 'auth/isAdmin',
|
isAdmin: 'auth/isAdmin',
|
||||||
quickSearchResults: 'search/quickResults',
|
quickSearchResults: 'search/quickResults',
|
||||||
quickSearchPending: 'search/quickPending',
|
quickSearchPending: 'search/quickPending',
|
||||||
usersFollowedFilter: 'posts/usersFollowedFilter',
|
|
||||||
categoriesFilter: 'posts/categoriesFilter',
|
|
||||||
}),
|
}),
|
||||||
userName() {
|
userName() {
|
||||||
const { name } = this.user || {}
|
const { name } = this.user || {}
|
||||||
@ -224,7 +222,7 @@ export default {
|
|||||||
},
|
},
|
||||||
showFilterPostsDropdown() {
|
showFilterPostsDropdown() {
|
||||||
const [firstRoute] = this.$route.matched
|
const [firstRoute] = this.$route.matched
|
||||||
return firstRoute.name === 'index'
|
return firstRoute && firstRoute.name === 'index'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@ -236,10 +234,6 @@ export default {
|
|||||||
...mapActions({
|
...mapActions({
|
||||||
quickSearchClear: 'search/quickClear',
|
quickSearchClear: 'search/quickClear',
|
||||||
quickSearch: 'search/quickSearch',
|
quickSearch: 'search/quickSearch',
|
||||||
fetchPosts: 'posts/fetchPosts',
|
|
||||||
}),
|
|
||||||
...mapMutations({
|
|
||||||
setFilteredByFollowers: 'posts/SET_FILTERED_BY_FOLLOWERS',
|
|
||||||
}),
|
}),
|
||||||
goToPost(item) {
|
goToPost(item) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
@ -259,17 +253,6 @@ export default {
|
|||||||
toggleMobileMenuView() {
|
toggleMobileMenuView() {
|
||||||
this.toggleMobileMenu = !this.toggleMobileMenu
|
this.toggleMobileMenu = !this.toggleMobileMenu
|
||||||
},
|
},
|
||||||
redirectToRoot() {
|
|
||||||
this.$router.replace('/')
|
|
||||||
this.fetchPosts({
|
|
||||||
i18n: this.$i18n,
|
|
||||||
filter: {
|
|
||||||
...this.usersFollowedFilter,
|
|
||||||
...this.categoriesFilter,
|
|
||||||
...this.filter,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
apollo: {
|
apollo: {
|
||||||
Category: {
|
Category: {
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"thanks": "Danke!",
|
||||||
"made": "Mit ❤ gemacht",
|
"made": "Mit ❤ gemacht",
|
||||||
"imprint": "Impressum",
|
"imprint": "Impressum",
|
||||||
"data-privacy": "Datenschutz",
|
"data-privacy": "Datenschutz",
|
||||||
@ -34,14 +35,11 @@
|
|||||||
"responsible": "Verantwortlicher gemäß § 55 Abs. 2 RStV ",
|
"responsible": "Verantwortlicher gemäß § 55 Abs. 2 RStV ",
|
||||||
"bank": "Bankverbindung",
|
"bank": "Bankverbindung",
|
||||||
"germany": "Deutschland",
|
"germany": "Deutschland",
|
||||||
"code-of-conduct": "Verhaltenscodex",
|
"code-of-conduct": "Verhaltenscodex"
|
||||||
"termsAndConditionsConfirmed": "Ich habe die <a href=\"/terms-and-conditions\" target=\"_blank\">Nutzungsbedingungen</a> durchgelesen und stimme ihnen zu."
|
|
||||||
},
|
},
|
||||||
"sorting": {
|
"sorting": {
|
||||||
"newest": "Neuste",
|
"newest": "Neuste",
|
||||||
"oldest": "Älteste",
|
"oldest": "Älteste"
|
||||||
"popular": "Beliebt",
|
|
||||||
"commented": "meist Kommentiert"
|
|
||||||
},
|
},
|
||||||
"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.",
|
||||||
@ -575,6 +573,11 @@
|
|||||||
"get-help": "Wenn du einem inakzeptablen Verhalten ausgesetzt bist, es miterlebst oder andere Bedenken hast, benachrichtige bitte so schnell wie möglich einen Organisator der Gemeinschaft und verlinke oder verweise auf den entsprechenden Inhalt:"
|
"get-help": "Wenn du einem inakzeptablen Verhalten ausgesetzt bist, es miterlebst oder andere Bedenken hast, benachrichtige bitte so schnell wie möglich einen Organisator der Gemeinschaft und verlinke oder verweise auf den entsprechenden Inhalt:"
|
||||||
},
|
},
|
||||||
"termsAndConditions": {
|
"termsAndConditions": {
|
||||||
|
"termsAndConditionsConfirmed": "Ich habe die <a href=\"/terms-and-conditions\" target=\"_blank\">Nutzungsbedingungen</a> durchgelesen und stimme ihnen zu.",
|
||||||
|
"newTermsAndConditions": "Neue Nutzungsbedingungen",
|
||||||
|
"termsAndConditionsNewConfirmText": "Bitte lies dir die neue Nutzungsbedingungen jetzt durch!",
|
||||||
|
"termsAndConditionsNewConfirm": "Ich habe die neuen Nutzungsbedingungen durchgelesen und stimme zu.",
|
||||||
|
"agree": "Ich stimme zu!",
|
||||||
"risk": {
|
"risk": {
|
||||||
"title": "Unfallgefahr",
|
"title": "Unfallgefahr",
|
||||||
"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": "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."
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"thanks": "Thanks!",
|
||||||
"made": "Made with ❤",
|
"made": "Made with ❤",
|
||||||
"imprint": "Imprint",
|
"imprint": "Imprint",
|
||||||
"termsAndConditions": "Terms and conditions",
|
"termsAndConditions": "Terms and conditions",
|
||||||
@ -34,14 +35,11 @@
|
|||||||
"responsible": "responsible for contents of this page (§ 55 Abs. 2 RStV)",
|
"responsible": "responsible for contents of this page (§ 55 Abs. 2 RStV)",
|
||||||
"bank": "bank account",
|
"bank": "bank account",
|
||||||
"germany": "Germany",
|
"germany": "Germany",
|
||||||
"code-of-conduct": "Code of Conduct",
|
"code-of-conduct": "Code of Conduct"
|
||||||
"termsAndConditionsConfirmed": "I have read and confirmed the <a href=\"/terms-and-conditions\" target=\"_blank\">terms and conditions</a>."
|
|
||||||
},
|
},
|
||||||
"sorting": {
|
"sorting": {
|
||||||
"newest": "Newest",
|
"newest": "Newest",
|
||||||
"oldest": "Oldest",
|
"oldest": "Oldest"
|
||||||
"popular": "Popular",
|
|
||||||
"commented": "Most commented"
|
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"copy": "If you already have a human-connection account, login here.",
|
"copy": "If you already have a human-connection account, login here.",
|
||||||
@ -575,6 +573,11 @@
|
|||||||
"get-help": "If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible and link or refer to the corresponding content:"
|
"get-help": "If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible and link or refer to the corresponding content:"
|
||||||
},
|
},
|
||||||
"termsAndConditions": {
|
"termsAndConditions": {
|
||||||
|
"newTermsAndConditions": "New Terms and Conditions",
|
||||||
|
"termsAndConditionsConfirmed": "I have read and confirmed the <a href=\"/terms-and-conditions\" target=\"_blank\">Terms and Conditions</a>.",
|
||||||
|
"termsAndConditionsNewConfirmText": "Please read the new terms of use now!",
|
||||||
|
"termsAndConditionsNewConfirm": "I have read and agree to the new terms of conditions.",
|
||||||
|
"agree": "I agree!",
|
||||||
"risk": {
|
"risk": {
|
||||||
"title": "Risk of accident",
|
"title": "Risk of accident",
|
||||||
"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": "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."
|
||||||
|
|||||||
19
webapp/middleware/termsAndConditions.js
Normal file
19
webapp/middleware/termsAndConditions.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import isEmpty from 'lodash/isEmpty'
|
||||||
|
|
||||||
|
export default async ({ store, env, route, redirect }) => {
|
||||||
|
let publicPages = env.publicPages
|
||||||
|
// only affect non public pages
|
||||||
|
if (publicPages.indexOf(route.name) >= 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (route.name === 'terms-and-conditions-confirm') return true // avoid endless loop
|
||||||
|
|
||||||
|
if (store.getters['auth/termsAndConditionsAgreed']) return true
|
||||||
|
|
||||||
|
let params = {}
|
||||||
|
if (!isEmpty(route.path) && route.path !== '/') {
|
||||||
|
params.path = route.path
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect('/terms-and-conditions-confirm', params)
|
||||||
|
}
|
||||||
@ -122,7 +122,7 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
router: {
|
router: {
|
||||||
middleware: ['authenticated'],
|
middleware: ['authenticated', 'termsAndConditions'],
|
||||||
linkActiveClass: 'router-link-active',
|
linkActiveClass: 'router-link-active',
|
||||||
linkExactActiveClass: 'router-link-exact-active',
|
linkExactActiveClass: 'router-link-exact-active',
|
||||||
scrollBehavior: (to, _from, savedPosition) => {
|
scrollBehavior: (to, _from, savedPosition) => {
|
||||||
|
|||||||
@ -81,6 +81,7 @@
|
|||||||
"vue-izitoast": "^1.2.1",
|
"vue-izitoast": "^1.2.1",
|
||||||
"vue-sweetalert-icons": "~4.2.0",
|
"vue-sweetalert-icons": "~4.2.0",
|
||||||
"vuex-i18n": "~1.13.1",
|
"vuex-i18n": "~1.13.1",
|
||||||
|
"xregexp": "^4.2.4",
|
||||||
"zxcvbn": "^4.4.2"
|
"zxcvbn": "^4.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@ -49,34 +49,6 @@
|
|||||||
</ds-number>
|
</ds-number>
|
||||||
</ds-space>
|
</ds-space>
|
||||||
</ds-flex-item>
|
</ds-flex-item>
|
||||||
<ds-flex-item :width="{ base: '100%', sm: '50%', md: '33%' }">
|
|
||||||
<ds-space margin="small">
|
|
||||||
<ds-number
|
|
||||||
:count="0"
|
|
||||||
:label="$t('admin.dashboard.organizations')"
|
|
||||||
size="x-large"
|
|
||||||
uppercase
|
|
||||||
>
|
|
||||||
<client-only slot="count">
|
|
||||||
<hc-count-to :end-val="statistics.countOrganizations" />
|
|
||||||
</client-only>
|
|
||||||
</ds-number>
|
|
||||||
</ds-space>
|
|
||||||
</ds-flex-item>
|
|
||||||
<ds-flex-item :width="{ base: '100%', sm: '50%', md: '33%' }">
|
|
||||||
<ds-space margin="small">
|
|
||||||
<ds-number
|
|
||||||
:count="0"
|
|
||||||
:label="$t('admin.dashboard.projects')"
|
|
||||||
size="x-large"
|
|
||||||
uppercase
|
|
||||||
>
|
|
||||||
<client-only slot="count">
|
|
||||||
<hc-count-to :end-val="statistics.countProjects" />
|
|
||||||
</client-only>
|
|
||||||
</ds-number>
|
|
||||||
</ds-space>
|
|
||||||
</ds-flex-item>
|
|
||||||
<ds-flex-item :width="{ base: '100%', sm: '50%', md: '33%' }">
|
<ds-flex-item :width="{ base: '100%', sm: '50%', md: '33%' }">
|
||||||
<ds-space margin="small">
|
<ds-space margin="small">
|
||||||
<ds-number :count="0" :label="$t('admin.dashboard.invites')" size="x-large" uppercase>
|
<ds-number :count="0" :label="$t('admin.dashboard.invites')" size="x-large" uppercase>
|
||||||
@ -137,8 +109,6 @@ export default {
|
|||||||
countPosts
|
countPosts
|
||||||
countComments
|
countComments
|
||||||
countNotifications
|
countNotifications
|
||||||
countOrganizations
|
|
||||||
countProjects
|
|
||||||
countInvites
|
countInvites
|
||||||
countFollows
|
countFollows
|
||||||
countShouts
|
countShouts
|
||||||
|
|||||||
@ -105,18 +105,6 @@ export default {
|
|||||||
icons: 'sort-amount-asc',
|
icons: 'sort-amount-asc',
|
||||||
order: 'createdAt_asc',
|
order: 'createdAt_asc',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: this.$t('sorting.popular'),
|
|
||||||
value: 'Popular',
|
|
||||||
icons: 'fire',
|
|
||||||
order: 'shoutedCount_desc',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: this.$t('sorting.commented'),
|
|
||||||
value: 'Commented',
|
|
||||||
icons: 'comment',
|
|
||||||
order: 'commentsCount_desc',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -75,6 +75,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch'
|
import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch'
|
||||||
|
import { VERSION } from '~/constants/terms-and-conditions-version.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -96,7 +97,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
asyncData({ store, redirect }) {
|
asyncData({ store, redirect }) {
|
||||||
if (store.getters['auth/isLoggedIn']) {
|
if (store.getters['auth/user'].termsAndConditionsAgreedVersion === VERSION) {
|
||||||
redirect('/')
|
redirect('/')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -178,6 +178,7 @@ export default {
|
|||||||
|
|
||||||
.post-card {
|
.post-card {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
cursor: auto;
|
||||||
|
|
||||||
.comments {
|
.comments {
|
||||||
margin-top: $space-small;
|
margin-top: $space-small;
|
||||||
|
|||||||
@ -33,19 +33,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<h3>{{ $t('post.moreInfo.titleOfRelatedContributionsSection') }}</h3>
|
<h3>{{ $t('post.moreInfo.titleOfRelatedContributionsSection') }}</h3>
|
||||||
<ds-section style="margin: 0 -1.5rem; padding: 1.5rem;">
|
<ds-section>
|
||||||
<ds-flex v-if="post.relatedContributions && post.relatedContributions.length" gutter="small">
|
<masonry-grid v-if="post.relatedContributions && post.relatedContributions.length">
|
||||||
<hc-post-card
|
<masonry-grid-item v-for="relatedPost in post.relatedContributions" :key="relatedPost.id">
|
||||||
v-for="relatedPost in post.relatedContributions"
|
<hc-post-card
|
||||||
:key="relatedPost.id"
|
:post="relatedPost"
|
||||||
:post="relatedPost"
|
:width="{ base: '100%', lg: 1 }"
|
||||||
:width="{ base: '100%', lg: 1 }"
|
@removePostFromList="removePostFromList"
|
||||||
@removePostFromList="removePostFromList"
|
/>
|
||||||
/>
|
</masonry-grid-item>
|
||||||
</ds-flex>
|
</masonry-grid>
|
||||||
<hc-empty v-else margin="large" icon="file" message="No related Posts" />
|
<hc-empty v-else margin="large" icon="file" message="No related Posts" />
|
||||||
</ds-section>
|
</ds-section>
|
||||||
<ds-space margin-bottom="large" />
|
|
||||||
</ds-card>
|
</ds-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -53,6 +52,8 @@
|
|||||||
import HcPostCard from '~/components/PostCard'
|
import HcPostCard from '~/components/PostCard'
|
||||||
import HcEmpty from '~/components/Empty.vue'
|
import HcEmpty from '~/components/Empty.vue'
|
||||||
import { relatedContributions } from '~/graphql/PostQuery'
|
import { relatedContributions } from '~/graphql/PostQuery'
|
||||||
|
import MasonryGrid from '~/components/MasonryGrid/MasonryGrid.vue'
|
||||||
|
import MasonryGridItem from '~/components/MasonryGrid/MasonryGridItem.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
transition: {
|
transition: {
|
||||||
@ -62,6 +63,8 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
HcPostCard,
|
HcPostCard,
|
||||||
HcEmpty,
|
HcEmpty,
|
||||||
|
MasonryGrid,
|
||||||
|
MasonryGridItem,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
post() {
|
post() {
|
||||||
|
|||||||
98
webapp/pages/terms-and-conditions-confirm.vue
Normal file
98
webapp/pages/terms-and-conditions-confirm.vue
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<template>
|
||||||
|
<ds-container width="medium">
|
||||||
|
<ds-card icon="balance-scale" :header="$t(`termsAndConditions.newTermsAndConditions`)" centered>
|
||||||
|
<p>
|
||||||
|
<ds-button>
|
||||||
|
<nuxt-link class="post-link" :to="{ name: 'terms-and-conditions' }" target="_blank">
|
||||||
|
{{ $t(`termsAndConditions.termsAndConditionsNewConfirmText`) }}
|
||||||
|
</nuxt-link>
|
||||||
|
</ds-button>
|
||||||
|
</p>
|
||||||
|
<ds-text>
|
||||||
|
<input id="checkbox" type="checkbox" v-model="checked" :checked="checked" />
|
||||||
|
<label
|
||||||
|
for="checkbox"
|
||||||
|
v-html="$t('termsAndConditions.termsAndConditionsNewConfirm')"
|
||||||
|
></label>
|
||||||
|
</ds-text>
|
||||||
|
|
||||||
|
<template slot="footer">
|
||||||
|
<ds-button primary @click="submit" :disabled="!checked">{{ $t(`actions.save`) }}</ds-button>
|
||||||
|
</template>
|
||||||
|
</ds-card>
|
||||||
|
</ds-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
import { mapGetters, mapMutations } from 'vuex'
|
||||||
|
import { VERSION } from '~/constants/terms-and-conditions-version.js'
|
||||||
|
const mutation = gql`
|
||||||
|
mutation($id: ID!, $termsAndConditionsAgreedVersion: String) {
|
||||||
|
UpdateUser(id: $id, termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion) {
|
||||||
|
id
|
||||||
|
termsAndConditionsAgreedVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
export default {
|
||||||
|
layout: 'default',
|
||||||
|
head() {
|
||||||
|
return {
|
||||||
|
title: this.$t('termsAndConditions.newTermsAndConditions'),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
currentUser: 'auth/user',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
checked: false,
|
||||||
|
sections: [
|
||||||
|
'risk',
|
||||||
|
'data-privacy',
|
||||||
|
'work-in-progress',
|
||||||
|
'code-of-conduct',
|
||||||
|
'moderation',
|
||||||
|
'fairness',
|
||||||
|
'questions',
|
||||||
|
'human-connection',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
asyncData({ store, redirect }) {
|
||||||
|
if (store.getters['auth/termsAndConditionsAgreed']) {
|
||||||
|
redirect('/')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapMutations({
|
||||||
|
setCurrentUser: 'auth/SET_USER',
|
||||||
|
}),
|
||||||
|
async submit() {
|
||||||
|
try {
|
||||||
|
await this.$apollo.mutate({
|
||||||
|
mutation,
|
||||||
|
variables: {
|
||||||
|
id: this.currentUser.id,
|
||||||
|
termsAndConditionsAgreedVersion: VERSION,
|
||||||
|
},
|
||||||
|
update: (store, { data: { UpdateUser } }) => {
|
||||||
|
const { termsAndConditionsAgreedVersion } = UpdateUser
|
||||||
|
this.setCurrentUser({
|
||||||
|
...this.currentUser,
|
||||||
|
termsAndConditionsAgreedVersion,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
this.$toast.success(this.$t('site.thanks'))
|
||||||
|
this.$router.replace(this.$route.query.path || '/')
|
||||||
|
} catch (err) {
|
||||||
|
this.$toast.error(err.message)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
|
import { VERSION } from '~/constants/terms-and-conditions-version.js'
|
||||||
|
|
||||||
export const state = () => {
|
export const state = () => {
|
||||||
return {
|
return {
|
||||||
@ -42,6 +43,9 @@ export const getters = {
|
|||||||
token(state) {
|
token(state) {
|
||||||
return state.token
|
return state.token
|
||||||
},
|
},
|
||||||
|
termsAndConditionsAgreed(state) {
|
||||||
|
return state.user && state.user.termsAndConditionsAgreedVersion === VERSION
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
@ -82,6 +86,7 @@ export const actions = {
|
|||||||
locationName
|
locationName
|
||||||
contributionsCount
|
contributionsCount
|
||||||
commentedCount
|
commentedCount
|
||||||
|
termsAndConditionsAgreedVersion
|
||||||
socialMedia {
|
socialMedia {
|
||||||
id
|
id
|
||||||
url
|
url
|
||||||
|
|||||||
@ -1,119 +0,0 @@
|
|||||||
import gql from 'graphql-tag'
|
|
||||||
|
|
||||||
export const state = () => {
|
|
||||||
return {
|
|
||||||
posts: [],
|
|
||||||
filteredByUsersFollowed: false,
|
|
||||||
filteredByCategories: false,
|
|
||||||
usersFollowedFilter: {},
|
|
||||||
categoriesFilter: {},
|
|
||||||
selectedCategoryIds: [],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const mutations = {
|
|
||||||
SET_POSTS(state, posts) {
|
|
||||||
state.posts = posts || null
|
|
||||||
},
|
|
||||||
SET_FILTERED_BY_FOLLOWERS(state, boolean) {
|
|
||||||
state.filteredByUsersFollowed = boolean || null
|
|
||||||
},
|
|
||||||
SET_FILTERED_BY_CATEGORIES(state, boolean) {
|
|
||||||
state.filteredByCategories = boolean || null
|
|
||||||
},
|
|
||||||
SET_USERS_FOLLOWED_FILTER(state, filter) {
|
|
||||||
state.usersFollowedFilter = filter || null
|
|
||||||
},
|
|
||||||
SET_CATEGORIES_FILTER(state, filter) {
|
|
||||||
state.categoriesFilter = filter || null
|
|
||||||
},
|
|
||||||
SET_SELECTED_CATEGORY_IDS(state, categoryId) {
|
|
||||||
if (!categoryId) {
|
|
||||||
state.selectedCategoryIds = []
|
|
||||||
} else {
|
|
||||||
const index = state.selectedCategoryIds.indexOf(categoryId)
|
|
||||||
if (index > -1) {
|
|
||||||
state.selectedCategoryIds.splice(index, 1)
|
|
||||||
} else {
|
|
||||||
state.selectedCategoryIds.push(categoryId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getters = {
|
|
||||||
posts(state) {
|
|
||||||
return state.posts || []
|
|
||||||
},
|
|
||||||
filteredByUsersFollowed(state) {
|
|
||||||
return state.filteredByUsersFollowed || false
|
|
||||||
},
|
|
||||||
filteredByCategories(state) {
|
|
||||||
return state.filteredByCategories || false
|
|
||||||
},
|
|
||||||
usersFollowedFilter(state) {
|
|
||||||
return state.usersFollowedFilter || {}
|
|
||||||
},
|
|
||||||
categoriesFilter(state) {
|
|
||||||
return state.categoriesFilter || {}
|
|
||||||
},
|
|
||||||
selectedCategoryIds(state) {
|
|
||||||
return state.selectedCategoryIds || []
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export const actions = {
|
|
||||||
async fetchPosts({ commit, dispatch }, { i18n, filter }) {
|
|
||||||
const client = this.app.apolloProvider.defaultClient
|
|
||||||
const {
|
|
||||||
data: { Post },
|
|
||||||
} = await client.query({
|
|
||||||
query: gql`
|
|
||||||
query Post($filter: _PostFilter, $first: Int, $offset: Int) {
|
|
||||||
Post(filter: $filter, first: $first, offset: $offset) {
|
|
||||||
id
|
|
||||||
title
|
|
||||||
contentExcerpt
|
|
||||||
createdAt
|
|
||||||
disabled
|
|
||||||
deleted
|
|
||||||
slug
|
|
||||||
image
|
|
||||||
author {
|
|
||||||
id
|
|
||||||
avatar
|
|
||||||
slug
|
|
||||||
name
|
|
||||||
disabled
|
|
||||||
deleted
|
|
||||||
contributionsCount
|
|
||||||
shoutedCount
|
|
||||||
commentedCount
|
|
||||||
followedByCount
|
|
||||||
followedByCurrentUser
|
|
||||||
location {
|
|
||||||
name: name${i18n.locale().toUpperCase()}
|
|
||||||
}
|
|
||||||
badges {
|
|
||||||
id
|
|
||||||
icon
|
|
||||||
}
|
|
||||||
}
|
|
||||||
categories {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
icon
|
|
||||||
}
|
|
||||||
shoutedCount
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
variables: {
|
|
||||||
filter,
|
|
||||||
first: 12,
|
|
||||||
offset: 0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
commit('SET_POSTS', Post)
|
|
||||||
return Post
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@ -742,6 +742,14 @@
|
|||||||
js-levenshtein "^1.1.3"
|
js-levenshtein "^1.1.3"
|
||||||
semver "^5.5.0"
|
semver "^5.5.0"
|
||||||
|
|
||||||
|
"@babel/runtime-corejs2@^7.2.0":
|
||||||
|
version "7.5.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.5.5.tgz#c3214c08ef20341af4187f1c9fbdc357fbec96b2"
|
||||||
|
integrity sha512-FYATQVR00NSNi7mUfpPDp7E8RYMXDuO8gaix7u/w3GekfUinKgX1AcTxs7SoiEmoEW9mbpjrwqWSW6zCmw5h8A==
|
||||||
|
dependencies:
|
||||||
|
core-js "^2.6.5"
|
||||||
|
regenerator-runtime "^0.13.2"
|
||||||
|
|
||||||
"@babel/runtime@7.3.4":
|
"@babel/runtime@7.3.4":
|
||||||
version "7.3.4"
|
version "7.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.4.tgz#73d12ba819e365fcf7fd152aed56d6df97d21c83"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.4.tgz#73d12ba819e365fcf7fd152aed56d6df97d21c83"
|
||||||
@ -15734,6 +15742,13 @@ xml-name-validator@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
||||||
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
|
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
|
||||||
|
|
||||||
|
xregexp@^4.2.4:
|
||||||
|
version "4.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.2.4.tgz#02a4aea056d65a42632c02f0233eab8e4d7e57ed"
|
||||||
|
integrity sha512-sO0bYdYeJAJBcJA8g7MJJX7UrOZIfJPd8U2SC7B2Dd/J24U0aQNoGp33shCaBSWeb0rD5rh6VBUIXOkGal1TZA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime-corejs2" "^7.2.0"
|
||||||
|
|
||||||
xtend@^4.0.0, xtend@~4.0.1:
|
xtend@^4.0.0, xtend@~4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
|
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user