mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge remote-tracking branch 'origin/master' into 236-list-social-media-accounts
This commit is contained in:
commit
102a03f552
@ -1,3 +1,3 @@
|
|||||||
FROM neo4j:3.5.0
|
FROM neo4j:3.5.3
|
||||||
RUN wget https://github.com/neo4j-contrib/neo4j-apoc-procedures/releases/download/3.5.0.1/apoc-3.5.0.1-all.jar -P plugins/
|
RUN wget https://github.com/neo4j-contrib/neo4j-apoc-procedures/releases/download/3.5.0.1/apoc-3.5.0.1-all.jar -P plugins/
|
||||||
COPY migrate.sh /usr/local/bin/migrate
|
COPY migrate.sh /usr/local/bin/migrate
|
||||||
|
|||||||
@ -16,8 +16,7 @@
|
|||||||
"test:cucumber:cmd": "wait-on tcp:4001 tcp:4123 && cucumber-js --require-module @babel/register --exit test/",
|
"test:cucumber:cmd": "wait-on tcp:4001 tcp:4123 && cucumber-js --require-module @babel/register --exit test/",
|
||||||
"test:jest:cmd:debug": "wait-on tcp:4001 tcp:4123 && node --inspect-brk ./node_modules/.bin/jest -i --forceExit --detectOpenHandles --runInBand",
|
"test:jest:cmd:debug": "wait-on tcp:4001 tcp:4123 && node --inspect-brk ./node_modules/.bin/jest -i --forceExit --detectOpenHandles --runInBand",
|
||||||
"test:jest": "run-p --race test:before:* 'test:jest:cmd {@}' --",
|
"test:jest": "run-p --race test:before:* 'test:jest:cmd {@}' --",
|
||||||
"test:cucumber": " cross-env CLIENT_URI=http://localhost:4123 run-p --race test:before:server test:cucumber:before:seeder 'test:cucumber:cmd {@}' --",
|
"test:cucumber": " cross-env CLIENT_URI=http://localhost:4123 run-p --race test:before:* 'test:cucumber:cmd {@}' --",
|
||||||
"test:cucumber:before:seeder": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 DISABLED_MIDDLEWARES=permissions yarn run dev",
|
|
||||||
"test:jest:debug": "run-p --race test:before:* 'test:jest:cmd:debug {@}' --",
|
"test:jest:debug": "run-p --race test:before:* 'test:jest:cmd:debug {@}' --",
|
||||||
"db:script:seed": "wait-on tcp:4001 && babel-node src/seed/seed-db.js",
|
"db:script:seed": "wait-on tcp:4001 && babel-node src/seed/seed-db.js",
|
||||||
"db:reset": "babel-node src/seed/reset-db.js",
|
"db:reset": "babel-node src/seed/reset-db.js",
|
||||||
@ -51,7 +50,7 @@
|
|||||||
"graphql-custom-directives": "~0.2.14",
|
"graphql-custom-directives": "~0.2.14",
|
||||||
"graphql-iso-date": "~3.6.1",
|
"graphql-iso-date": "~3.6.1",
|
||||||
"graphql-middleware": "~3.0.2",
|
"graphql-middleware": "~3.0.2",
|
||||||
"graphql-shield": "~5.3.1",
|
"graphql-shield": "~5.3.2",
|
||||||
"graphql-tag": "~2.10.1",
|
"graphql-tag": "~2.10.1",
|
||||||
"graphql-yoga": "~1.17.4",
|
"graphql-yoga": "~1.17.4",
|
||||||
"helmet": "~3.16.0",
|
"helmet": "~3.16.0",
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import posts from './resolvers/posts.js'
|
|||||||
import moderation from './resolvers/moderation.js'
|
import moderation from './resolvers/moderation.js'
|
||||||
import rewards from './resolvers/rewards.js'
|
import rewards from './resolvers/rewards.js'
|
||||||
import socialMedia from './resolvers/socialMedia.js'
|
import socialMedia from './resolvers/socialMedia.js'
|
||||||
|
import notifications from './resolvers/notifications'
|
||||||
|
|
||||||
export const typeDefs = fs
|
export const typeDefs = fs
|
||||||
.readFileSync(
|
.readFileSync(
|
||||||
@ -18,7 +19,8 @@ export const typeDefs = fs
|
|||||||
export const resolvers = {
|
export const resolvers = {
|
||||||
Query: {
|
Query: {
|
||||||
...statistics.Query,
|
...statistics.Query,
|
||||||
...userManagement.Query
|
...userManagement.Query,
|
||||||
|
...notifications.Query
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
...userManagement.Mutation,
|
...userManagement.Mutation,
|
||||||
@ -26,6 +28,7 @@ export const resolvers = {
|
|||||||
...posts.Mutation,
|
...posts.Mutation,
|
||||||
...moderation.Mutation,
|
...moderation.Mutation,
|
||||||
...rewards.Mutation,
|
...rewards.Mutation,
|
||||||
...socialMedia.Mutation
|
...socialMedia.Mutation,
|
||||||
|
...notifications.Mutation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,20 +11,22 @@ import userMiddleware from './userMiddleware'
|
|||||||
import includedFieldsMiddleware from './includedFieldsMiddleware'
|
import includedFieldsMiddleware from './includedFieldsMiddleware'
|
||||||
import orderByMiddleware from './orderByMiddleware'
|
import orderByMiddleware from './orderByMiddleware'
|
||||||
import validUrlMiddleware from './validUrlMiddleware'
|
import validUrlMiddleware from './validUrlMiddleware'
|
||||||
|
import notificationsMiddleware from './notificationsMiddleware'
|
||||||
|
|
||||||
export default schema => {
|
export default schema => {
|
||||||
let middleware = [
|
let middleware = [
|
||||||
passwordMiddleware,
|
passwordMiddleware,
|
||||||
dateTimeMiddleware,
|
dateTimeMiddleware,
|
||||||
|
validUrlMiddleware,
|
||||||
sluggifyMiddleware,
|
sluggifyMiddleware,
|
||||||
excerptMiddleware,
|
excerptMiddleware,
|
||||||
xssMiddleware,
|
xssMiddleware,
|
||||||
fixImageUrlsMiddleware,
|
fixImageUrlsMiddleware,
|
||||||
|
notificationsMiddleware,
|
||||||
softDeleteMiddleware,
|
softDeleteMiddleware,
|
||||||
userMiddleware,
|
userMiddleware,
|
||||||
includedFieldsMiddleware,
|
includedFieldsMiddleware,
|
||||||
orderByMiddleware,
|
orderByMiddleware
|
||||||
validUrlMiddleware
|
|
||||||
]
|
]
|
||||||
|
|
||||||
// add permisions middleware at the first position (unless we're seeding)
|
// add permisions middleware at the first position (unless we're seeding)
|
||||||
|
|||||||
10
backend/src/middleware/notifications/mentions.js
Normal file
10
backend/src/middleware/notifications/mentions.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
const MENTION_REGEX = /\s@([\w_-]+)/g
|
||||||
|
|
||||||
|
export function extractSlugs (content) {
|
||||||
|
let slugs = []
|
||||||
|
let match
|
||||||
|
while ((match = MENTION_REGEX.exec(content)) != null) {
|
||||||
|
slugs.push(match[1])
|
||||||
|
}
|
||||||
|
return slugs
|
||||||
|
}
|
||||||
30
backend/src/middleware/notifications/mentions.spec.js
Normal file
30
backend/src/middleware/notifications/mentions.spec.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { extractSlugs } from './mentions'
|
||||||
|
|
||||||
|
describe('extract', () => {
|
||||||
|
describe('finds mentions in the form of', () => {
|
||||||
|
it('@user', () => {
|
||||||
|
const content = 'Hello @user'
|
||||||
|
expect(extractSlugs(content)).toEqual(['user'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('@user-with-dash', () => {
|
||||||
|
const content = 'Hello @user-with-dash'
|
||||||
|
expect(extractSlugs(content)).toEqual(['user-with-dash'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('@user.', () => {
|
||||||
|
const content = 'Hello @user.'
|
||||||
|
expect(extractSlugs(content)).toEqual(['user'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('@user-With-Capital-LETTERS', () => {
|
||||||
|
const content = 'Hello @user-With-Capital-LETTERS'
|
||||||
|
expect(extractSlugs(content)).toEqual(['user-With-Capital-LETTERS'])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('ignores email addresses', () => {
|
||||||
|
const content = 'Hello somebody@example.org'
|
||||||
|
expect(extractSlugs(content)).toEqual([])
|
||||||
|
})
|
||||||
|
})
|
||||||
27
backend/src/middleware/notificationsMiddleware.js
Normal file
27
backend/src/middleware/notificationsMiddleware.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { extractSlugs } from './notifications/mentions'
|
||||||
|
|
||||||
|
const notify = async (resolve, root, args, context, resolveInfo) => {
|
||||||
|
const post = await resolve(root, args, context, resolveInfo)
|
||||||
|
|
||||||
|
const session = context.driver.session()
|
||||||
|
const { content, id: postId } = post
|
||||||
|
const slugs = extractSlugs(content)
|
||||||
|
const createdAt = (new Date()).toISOString()
|
||||||
|
const cypher = `
|
||||||
|
match(u:User) where u.slug in $slugs
|
||||||
|
match(p:Post) where p.id = $postId
|
||||||
|
create(n:Notification{id: apoc.create.uuid(), read: false, createdAt: $createdAt})
|
||||||
|
merge (n)-[:NOTIFIED]->(u)
|
||||||
|
merge (p)-[:NOTIFIED]->(n)
|
||||||
|
`
|
||||||
|
await session.run(cypher, { slugs, createdAt, postId })
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
return post
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
Mutation: {
|
||||||
|
CreatePost: notify
|
||||||
|
}
|
||||||
|
}
|
||||||
85
backend/src/middleware/notificationsMiddleware.spec.js
Normal file
85
backend/src/middleware/notificationsMiddleware.spec.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import Factory from '../seed/factories'
|
||||||
|
import { GraphQLClient } from 'graphql-request'
|
||||||
|
import { host, login } from '../jest/helpers'
|
||||||
|
|
||||||
|
const factory = Factory()
|
||||||
|
let client
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await factory.create('User', {
|
||||||
|
id: 'you',
|
||||||
|
name: 'Al Capone',
|
||||||
|
slug: 'al-capone',
|
||||||
|
email: 'test@example.org',
|
||||||
|
password: '1234'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await factory.cleanDatabase()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('currentUser { notifications }', () => {
|
||||||
|
const query = `query($read: Boolean) {
|
||||||
|
currentUser {
|
||||||
|
notifications(read: $read, orderBy: createdAt_desc) {
|
||||||
|
read
|
||||||
|
post {
|
||||||
|
content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
describe('authenticated', () => {
|
||||||
|
let headers
|
||||||
|
beforeEach(async () => {
|
||||||
|
headers = await login({ email: 'test@example.org', password: '1234' })
|
||||||
|
client = new GraphQLClient(host, { headers })
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('given another user', () => {
|
||||||
|
let authorClient
|
||||||
|
let authorParams
|
||||||
|
let authorHeaders
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
authorParams = {
|
||||||
|
email: 'author@example.org',
|
||||||
|
password: '1234',
|
||||||
|
id: 'author'
|
||||||
|
}
|
||||||
|
await factory.create('User', authorParams)
|
||||||
|
authorHeaders = await login(authorParams)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('who mentions me in a post', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
const content = 'Hey @al-capone how do you do?'
|
||||||
|
const title = 'Mentioning Al Capone'
|
||||||
|
const createPostMutation = `
|
||||||
|
mutation($title: String!, $content: String!) {
|
||||||
|
CreatePost(title: $title, content: $content) {
|
||||||
|
title
|
||||||
|
content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
authorClient = new GraphQLClient(host, { headers: authorHeaders })
|
||||||
|
await authorClient.request(createPostMutation, { title, content })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sends you a notification', async () => {
|
||||||
|
const expected = {
|
||||||
|
currentUser: {
|
||||||
|
notifications: [
|
||||||
|
{ read: false, post: { content: 'Hey @al-capone how do you do?' } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await expect(client.request(query, { read: false })).resolves.toEqual(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -20,6 +20,21 @@ const isMyOwn = rule({ cache: 'no_cache' })(async (parent, args, context, info)
|
|||||||
return context.user.id === parent.id
|
return context.user.id === parent.id
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const belongsToMe = rule({ cache: 'no_cache' })(async (_, args, context) => {
|
||||||
|
const { driver, user: { id: userId } } = context
|
||||||
|
const { id: notificationId } = args
|
||||||
|
const session = driver.session()
|
||||||
|
const result = await session.run(`
|
||||||
|
MATCH (u:User {id: $userId})<-[:NOTIFIED]-(n:Notification {id: $notificationId})
|
||||||
|
RETURN n
|
||||||
|
`, { userId, notificationId })
|
||||||
|
const [notification] = result.records.map((record) => {
|
||||||
|
return record.get('n')
|
||||||
|
})
|
||||||
|
session.close()
|
||||||
|
return Boolean(notification)
|
||||||
|
})
|
||||||
|
|
||||||
const onlyEnabledContent = rule({ cache: 'strict' })(async (parent, args, ctx, info) => {
|
const onlyEnabledContent = rule({ cache: 'strict' })(async (parent, args, ctx, info) => {
|
||||||
const { disabled, deleted } = args
|
const { disabled, deleted } = args
|
||||||
return !(disabled || deleted)
|
return !(disabled || deleted)
|
||||||
@ -50,6 +65,7 @@ const permissions = shield({
|
|||||||
Post: or(onlyEnabledContent, isModerator)
|
Post: or(onlyEnabledContent, isModerator)
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
|
UpdateNotification: belongsToMe,
|
||||||
CreatePost: isAuthenticated,
|
CreatePost: isAuthenticated,
|
||||||
UpdatePost: isAuthor,
|
UpdatePost: isAuthor,
|
||||||
DeletePost: isAuthor,
|
DeletePost: isAuthor,
|
||||||
|
|||||||
14
backend/src/resolvers/notifications.js
Normal file
14
backend/src/resolvers/notifications.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { neo4jgraphql } from 'neo4j-graphql-js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
Query: {
|
||||||
|
Notification: (object, params, context, resolveInfo) => {
|
||||||
|
return neo4jgraphql(object, params, context, resolveInfo, false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Mutation: {
|
||||||
|
UpdateNotification: (object, params, context, resolveInfo) => {
|
||||||
|
return neo4jgraphql(object, params, context, resolveInfo, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,13 +5,14 @@ import { host, login } from '../jest/helpers'
|
|||||||
|
|
||||||
const factory = Factory()
|
const factory = Factory()
|
||||||
let client
|
let client
|
||||||
|
let userParams = {
|
||||||
|
id: 'you',
|
||||||
|
email: 'test@example.org',
|
||||||
|
password: '1234'
|
||||||
|
}
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await factory.create('User', {
|
await factory.create('User', userParams)
|
||||||
id: 'you',
|
|
||||||
email: 'test@example.org',
|
|
||||||
password: '1234'
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
@ -118,3 +119,63 @@ describe('currentUser { notifications }', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('UpdateNotification', () => {
|
||||||
|
const mutation = `mutation($id: ID!, $read: Boolean){
|
||||||
|
UpdateNotification(id: $id, read: $read) {
|
||||||
|
id read
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
const variables = { id: 'to-be-updated', read: true }
|
||||||
|
|
||||||
|
describe('given a notifications', () => {
|
||||||
|
let headers
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const mentionedParams = {
|
||||||
|
id: 'mentioned-1',
|
||||||
|
email: 'mentioned@example.org',
|
||||||
|
password: '1234',
|
||||||
|
slug: 'mentioned'
|
||||||
|
}
|
||||||
|
await factory.create('User', mentionedParams)
|
||||||
|
await factory.create('Notification', { id: 'to-be-updated' })
|
||||||
|
await factory.authenticateAs(userParams)
|
||||||
|
await factory.create('Post', { id: 'p1' })
|
||||||
|
await Promise.all([
|
||||||
|
factory.relate('Notification', 'User', { from: 'to-be-updated', to: 'mentioned-1' }),
|
||||||
|
factory.relate('Notification', 'Post', { from: 'p1', to: 'to-be-updated' })
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('unauthenticated', () => {
|
||||||
|
it('throws authorization error', async () => {
|
||||||
|
client = new GraphQLClient(host)
|
||||||
|
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('authenticated', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
headers = await login({ email: 'test@example.org', password: '1234' })
|
||||||
|
client = new GraphQLClient(host, { headers })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws authorization error', async () => {
|
||||||
|
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('and owner', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
headers = await login({ email: 'mentioned@example.org', password: '1234' })
|
||||||
|
client = new GraphQLClient(host, { headers })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('updates notification', async () => {
|
||||||
|
const expected = { UpdateNotification: { id: 'to-be-updated', read: true } }
|
||||||
|
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
@ -32,7 +32,7 @@ export default {
|
|||||||
const session = driver.session()
|
const session = driver.session()
|
||||||
const result = await session.run(
|
const result = await session.run(
|
||||||
'MATCH (user:User {email: $userEmail}) ' +
|
'MATCH (user:User {email: $userEmail}) ' +
|
||||||
'RETURN user {.id, .slug, .name, .avatar, .email, .password, .role, .disabled} as user LIMIT 1',
|
'RETURN user {.id, .slug, .name, .avatar, .email, .password, .role, .disabled} as user LIMIT 1',
|
||||||
{
|
{
|
||||||
userEmail: email
|
userEmail: email
|
||||||
}
|
}
|
||||||
@ -102,4 +102,4 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,10 +29,10 @@ let schema = makeAugmentedSchema({
|
|||||||
resolvers,
|
resolvers,
|
||||||
config: {
|
config: {
|
||||||
query: {
|
query: {
|
||||||
exclude: ['Statistics', 'LoggedInUser']
|
exclude: ['Notfication', 'Statistics', 'LoggedInUser']
|
||||||
},
|
},
|
||||||
mutation: {
|
mutation: {
|
||||||
exclude: ['Statistics', 'LoggedInUser']
|
exclude: ['Notfication', 'Statistics', 'LoggedInUser']
|
||||||
},
|
},
|
||||||
debug: debug
|
debug: debug
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1104,6 +1104,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.9.tgz#693e76a52f61a2f1e7fb48c0eef167b95ea4ffd0"
|
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.9.tgz#693e76a52f61a2f1e7fb48c0eef167b95ea4ffd0"
|
||||||
integrity sha512-sCZy4SxP9rN2w30Hlmg5dtdRwgYQfYRiLo9usw8X9cxlf+H4FqM1xX7+sNH7NNKVdbXMJWqva7iyy+fxh/V7fA==
|
integrity sha512-sCZy4SxP9rN2w30Hlmg5dtdRwgYQfYRiLo9usw8X9cxlf+H4FqM1xX7+sNH7NNKVdbXMJWqva7iyy+fxh/V7fA==
|
||||||
|
|
||||||
|
"@types/yup@0.26.9":
|
||||||
|
version "0.26.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.26.9.tgz#8a619ac4d2b8dcacb0d81345746018303b479919"
|
||||||
|
integrity sha512-C7HdLLs1ZNPbYeNsSX++fMosxWAwzVeUs9wc76XlKJrKvLEyNwXMDUjag75EVAPxlZ36YiRJ6iTy4zc5Dbtndw==
|
||||||
|
|
||||||
"@types/zen-observable@^0.5.3":
|
"@types/zen-observable@^0.5.3":
|
||||||
version "0.5.4"
|
version "0.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.5.4.tgz#b863a4191e525206819e008097ebf0fb2e3a1cdc"
|
resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.5.4.tgz#b863a4191e525206819e008097ebf0fb2e3a1cdc"
|
||||||
@ -3738,11 +3743,12 @@ graphql-request@~1.8.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
cross-fetch "2.2.2"
|
cross-fetch "2.2.2"
|
||||||
|
|
||||||
graphql-shield@~5.3.1:
|
graphql-shield@~5.3.2:
|
||||||
version "5.3.1"
|
version "5.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/graphql-shield/-/graphql-shield-5.3.1.tgz#34cff4d1bfdcc3caa6fc348afb11503dde1893cd"
|
resolved "https://registry.yarnpkg.com/graphql-shield/-/graphql-shield-5.3.2.tgz#2d47907ed9882a0636cb8ade6087123309d215ef"
|
||||||
integrity sha512-vVJ7rjkR7miWi/Zspr7/ibmtdL2gEHagCtpsJY534DyRE70r+PurCp2kR/e1fZhb4JdmTYCS+sokyYfH974/+w==
|
integrity sha512-fib7rSr5aS/WHL3+Aa5LXhcCuPGEIDXmzfGtFjUXkUiZ6E5u+bDSL+9KRXo/p14A28GkJF+1Vu1hlg9H/QFG1w==
|
||||||
dependencies:
|
dependencies:
|
||||||
|
"@types/yup" "0.26.9"
|
||||||
lightercollective "^0.2.0"
|
lightercollective "^0.2.0"
|
||||||
object-hash "^1.3.1"
|
object-hash "^1.3.1"
|
||||||
yup "^0.27.0"
|
yup "^0.27.0"
|
||||||
|
|||||||
@ -111,9 +111,8 @@ export default {
|
|||||||
|
|
||||||
if (this.isOwner && this.resourceType === 'user') {
|
if (this.isOwner && this.resourceType === 'user') {
|
||||||
routes.push({
|
routes.push({
|
||||||
name: this.$t(`settings.data.name`),
|
name: this.$t(`settings.name`),
|
||||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
path: '/settings',
|
||||||
callback: () => this.$router.push('/settings'),
|
|
||||||
icon: 'edit'
|
icon: 'edit'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,7 +75,7 @@
|
|||||||
"eslint-plugin-vue": "~5.2.2",
|
"eslint-plugin-vue": "~5.2.2",
|
||||||
"jest": "~24.7.1",
|
"jest": "~24.7.1",
|
||||||
"node-sass": "~4.11.0",
|
"node-sass": "~4.11.0",
|
||||||
"nodemon": "~1.18.10",
|
"nodemon": "~1.18.11",
|
||||||
"prettier": "~1.14.3",
|
"prettier": "~1.14.3",
|
||||||
"sass-loader": "~7.1.0",
|
"sass-loader": "~7.1.0",
|
||||||
"vue-jest": "~3.0.4",
|
"vue-jest": "~3.0.4",
|
||||||
|
|||||||
@ -32,22 +32,26 @@ export default {
|
|||||||
name: this.$t('admin.dashboard.name'),
|
name: this.$t('admin.dashboard.name'),
|
||||||
path: `/admin`
|
path: `/admin`
|
||||||
},
|
},
|
||||||
{
|
// TODO implement
|
||||||
|
/* {
|
||||||
name: this.$t('admin.users.name'),
|
name: this.$t('admin.users.name'),
|
||||||
path: `/admin/users`
|
path: `/admin/users`
|
||||||
},
|
}, */
|
||||||
{
|
// TODO implement
|
||||||
|
/* {
|
||||||
name: this.$t('admin.organizations.name'),
|
name: this.$t('admin.organizations.name'),
|
||||||
path: `/admin/organizations`
|
path: `/admin/organizations`
|
||||||
},
|
}, */
|
||||||
{
|
// TODO implement
|
||||||
|
/* {
|
||||||
name: this.$t('admin.pages.name'),
|
name: this.$t('admin.pages.name'),
|
||||||
path: `/admin/pages`
|
path: `/admin/pages`
|
||||||
},
|
}, */
|
||||||
{
|
// TODO implement
|
||||||
|
/* {
|
||||||
name: this.$t('admin.notifications.name'),
|
name: this.$t('admin.notifications.name'),
|
||||||
path: `/admin/notifications`
|
path: `/admin/notifications`
|
||||||
},
|
}, */
|
||||||
{
|
{
|
||||||
name: this.$t('admin.categories.name'),
|
name: this.$t('admin.categories.name'),
|
||||||
path: `/admin/categories`
|
path: `/admin/categories`
|
||||||
@ -55,11 +59,12 @@ export default {
|
|||||||
{
|
{
|
||||||
name: this.$t('admin.tags.name'),
|
name: this.$t('admin.tags.name'),
|
||||||
path: `/admin/tags`
|
path: `/admin/tags`
|
||||||
},
|
}
|
||||||
{
|
// TODO implement
|
||||||
|
/* {
|
||||||
name: this.$t('admin.settings.name'),
|
name: this.$t('admin.settings.name'),
|
||||||
path: `/admin/settings`
|
path: `/admin/settings`
|
||||||
}
|
} */
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -149,8 +149,8 @@ export default {
|
|||||||
.post-add-button {
|
.post-add-button {
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 100vh;
|
top: 98vh;
|
||||||
left: 100vw;
|
left: 98vw;
|
||||||
transform: translate(-120%, -120%);
|
transform: translate(-120%, -120%);
|
||||||
box-shadow: $box-shadow-x-large;
|
box-shadow: $box-shadow-x-large;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,25 +58,28 @@ export default {
|
|||||||
{
|
{
|
||||||
name: this.$t('common.comment', null, 2),
|
name: this.$t('common.comment', null, 2),
|
||||||
path: `/post/${id}/${slug}#comments`
|
path: `/post/${id}/${slug}#comments`
|
||||||
},
|
}
|
||||||
{
|
// TODO implement
|
||||||
|
/* {
|
||||||
name: this.$t('common.letsTalk'),
|
name: this.$t('common.letsTalk'),
|
||||||
path: `/post/${id}/${slug}#lets-talk`
|
path: `/post/${id}/${slug}#lets-talk`
|
||||||
},
|
}, */
|
||||||
{
|
// TODO implement
|
||||||
|
/* {
|
||||||
name: this.$t('common.versus'),
|
name: this.$t('common.versus'),
|
||||||
path: `/post/${id}/${slug}#versus`
|
path: `/post/${id}/${slug}#versus`
|
||||||
}
|
} */
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: this.$t('common.moreInfo'),
|
name: this.$t('common.moreInfo'),
|
||||||
path: `/post/${id}/${slug}/more-info`
|
path: `/post/${id}/${slug}/more-info`
|
||||||
},
|
}
|
||||||
{
|
// TODO implement
|
||||||
|
/* {
|
||||||
name: this.$t('common.takeAction'),
|
name: this.$t('common.takeAction'),
|
||||||
path: `/post/${id}/${slug}/take-action`
|
path: `/post/${id}/${slug}/take-action`
|
||||||
}
|
} */
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,16 +62,18 @@
|
|||||||
>
|
>
|
||||||
{{ category.name }}
|
{{ category.name }}
|
||||||
</ds-tag>
|
</ds-tag>
|
||||||
</div>-->
|
</div>-->
|
||||||
<!-- Tags -->
|
<!-- Tags -->
|
||||||
<template v-if="post.tags && post.tags.length">
|
<template v-if="post.tags && post.tags.length">
|
||||||
<ds-space margin="xx-small" />
|
<ds-space margin="xx-small" />
|
||||||
<div class="tags">
|
<div class="tags">
|
||||||
<ds-icon name="tags" /> <ds-tag
|
<ds-icon name="tags" />
|
||||||
|
<ds-tag
|
||||||
v-for="tag in post.tags"
|
v-for="tag in post.tags"
|
||||||
:key="tag.id"
|
:key="tag.id"
|
||||||
>
|
>
|
||||||
<ds-icon name="tag" /> {{ tag.name }}
|
<ds-icon name="tag" />
|
||||||
|
{{ tag.name }}
|
||||||
</ds-tag>
|
</ds-tag>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -87,9 +89,7 @@
|
|||||||
color="primary"
|
color="primary"
|
||||||
size="small"
|
size="small"
|
||||||
round
|
round
|
||||||
>
|
>{{ post.commentsCount }}</ds-tag> Comments
|
||||||
{{ post.commentsCount }}
|
|
||||||
</ds-tag> Comments
|
|
||||||
</span>
|
</span>
|
||||||
</h3>
|
</h3>
|
||||||
<ds-space margin-bottom="large" />
|
<ds-space margin-bottom="large" />
|
||||||
|
|||||||
@ -42,7 +42,8 @@
|
|||||||
color="soft"
|
color="soft"
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
<ds-icon name="map-marker" /> {{ user.location.name }}
|
<ds-icon name="map-marker" />
|
||||||
|
{{ user.location.name }}
|
||||||
</ds-text>
|
</ds-text>
|
||||||
<ds-text
|
<ds-text
|
||||||
align="center"
|
align="center"
|
||||||
@ -56,9 +57,7 @@
|
|||||||
v-if="user.badges && user.badges.length"
|
v-if="user.badges && user.badges.length"
|
||||||
margin="x-small"
|
margin="x-small"
|
||||||
>
|
>
|
||||||
<hc-badges
|
<hc-badges :badges="user.badges" />
|
||||||
:badges="user.badges"
|
|
||||||
/>
|
|
||||||
</ds-space>
|
</ds-space>
|
||||||
<ds-flex>
|
<ds-flex>
|
||||||
<ds-flex-item>
|
<ds-flex-item>
|
||||||
@ -82,9 +81,7 @@
|
|||||||
</no-ssr>
|
</no-ssr>
|
||||||
</ds-flex-item>
|
</ds-flex-item>
|
||||||
</ds-flex>
|
</ds-flex>
|
||||||
<ds-space
|
<ds-space margin="small">
|
||||||
margin="small"
|
|
||||||
>
|
|
||||||
<hc-follow-button
|
<hc-follow-button
|
||||||
v-if="!myProfile"
|
v-if="!myProfile"
|
||||||
:follow-id="user.id"
|
:follow-id="user.id"
|
||||||
@ -227,32 +224,28 @@
|
|||||||
</no-ssr>
|
</no-ssr>
|
||||||
</ds-space>
|
</ds-space>
|
||||||
</ds-flex-item>
|
</ds-flex-item>
|
||||||
<ds-flex-item class="ds-tab-nav-item">
|
<!--<ds-flex-item class="ds-tab-nav-item">
|
||||||
<ds-space margin="small">
|
<ds-space margin="small">-->
|
||||||
<!-- TODO: find better solution for rendering errors -->
|
<!-- TODO: find better solution for rendering errors -->
|
||||||
|
<!--
|
||||||
<no-ssr>
|
<no-ssr>
|
||||||
<ds-number :label="$t('profile.commented')">
|
<ds-number :label="$t('profile.commented')">
|
||||||
<hc-count-to
|
<hc-count-to slot="count" :end-val="user.commentsCount"/>
|
||||||
slot="count"
|
|
||||||
:end-val="user.commentsCount"
|
|
||||||
/>
|
|
||||||
</ds-number>
|
</ds-number>
|
||||||
</no-ssr>
|
</no-ssr>
|
||||||
</ds-space>
|
</ds-space>
|
||||||
</ds-flex-item>
|
</ds-flex-item>
|
||||||
<ds-flex-item class="ds-tab-nav-item">
|
-->
|
||||||
<ds-space margin="small">
|
<!--<ds-flex-item class="ds-tab-nav-item">
|
||||||
<!-- TODO: find better solution for rendering errors -->
|
<ds-space margin="small">-->
|
||||||
<no-ssr>
|
<!-- TODO: find better solution for rendering errors -->
|
||||||
|
<!--<no-ssr>
|
||||||
<ds-number :label="$t('profile.shouted')">
|
<ds-number :label="$t('profile.shouted')">
|
||||||
<hc-count-to
|
<hc-count-to slot="count" :end-val="user.shoutedCount"/>
|
||||||
slot="count"
|
|
||||||
:end-val="user.shoutedCount"
|
|
||||||
/>
|
|
||||||
</ds-number>
|
</ds-number>
|
||||||
</no-ssr>
|
</no-ssr>
|
||||||
</ds-space>
|
</ds-space>
|
||||||
</ds-flex-item>
|
</ds-flex-item>-->
|
||||||
</ds-flex>
|
</ds-flex>
|
||||||
</ds-card>
|
</ds-card>
|
||||||
</ds-flex-item>
|
</ds-flex-item>
|
||||||
@ -273,9 +266,7 @@
|
|||||||
:key="post.id"
|
:key="post.id"
|
||||||
:width="{ base: '100%', md: '100%', xl: '50%' }"
|
:width="{ base: '100%', md: '100%', xl: '50%' }"
|
||||||
>
|
>
|
||||||
<hc-post-card
|
<hc-post-card :post="post" />
|
||||||
:post="post"
|
|
||||||
/>
|
|
||||||
</ds-flex-item>
|
</ds-flex-item>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|||||||
@ -35,30 +35,36 @@ export default {
|
|||||||
name: this.$t('settings.security.name'),
|
name: this.$t('settings.security.name'),
|
||||||
path: `/settings/security`
|
path: `/settings/security`
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: this.$t('settings.invites.name'),
|
|
||||||
path: `/settings/invites`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: this.$t('settings.download.name'),
|
|
||||||
path: `/settings/data-download`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: this.$t('settings.delete.name'),
|
|
||||||
path: `/settings/delete-account`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: this.$t('settings.organizations.name'),
|
|
||||||
path: `/settings/my-organizations`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: this.$t('settings.languages.name'),
|
|
||||||
path: `/settings/languages`
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: this.$t('settings.social-media.name'),
|
name: this.$t('settings.social-media.name'),
|
||||||
path: `/settings/my-social-media`
|
path: `/settings/my-social-media`
|
||||||
}
|
}
|
||||||
|
// TODO implement
|
||||||
|
/* {
|
||||||
|
name: this.$t('settings.invites.name'),
|
||||||
|
path: `/settings/invites`
|
||||||
|
}, */
|
||||||
|
// TODO implement
|
||||||
|
/* {
|
||||||
|
name: this.$t('settings.download.name'),
|
||||||
|
path: `/settings/data-download`
|
||||||
|
}, */
|
||||||
|
// TODO implement
|
||||||
|
/* {
|
||||||
|
name: this.$t('settings.delete.name'),
|
||||||
|
path: `/settings/delete-account`
|
||||||
|
}, */
|
||||||
|
// TODO implement
|
||||||
|
/* {
|
||||||
|
name: this.$t('settings.organizations.name'),
|
||||||
|
path: `/settings/my-organizations`
|
||||||
|
}, */
|
||||||
|
// TODO implement
|
||||||
|
/* {
|
||||||
|
name: this.$t('settings.languages.name'),
|
||||||
|
path: `/settings/languages`
|
||||||
|
},
|
||||||
|
} */
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2867,10 +2867,10 @@ cheerio@^1.0.0-rc.2:
|
|||||||
lodash "^4.15.0"
|
lodash "^4.15.0"
|
||||||
parse5 "^3.0.1"
|
parse5 "^3.0.1"
|
||||||
|
|
||||||
chokidar@^2.0.2, chokidar@^2.0.4, chokidar@^2.1.0:
|
chokidar@^2.0.2, chokidar@^2.0.4, chokidar@^2.1.5:
|
||||||
version "2.1.1"
|
version "2.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.1.tgz#adc39ad55a2adf26548bd2afa048f611091f9184"
|
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.5.tgz#0ae8434d962281a5f56c72869e79cb6d9d86ad4d"
|
||||||
integrity sha512-gfw3p2oQV2wEt+8VuMlNsPjCxDxvvgnm/kz+uATu805mWVF8IJN7uz9DN7iBz+RMJISmiVbCOBFs9qBGMjtPfQ==
|
integrity sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==
|
||||||
dependencies:
|
dependencies:
|
||||||
anymatch "^2.0.0"
|
anymatch "^2.0.0"
|
||||||
async-each "^1.0.1"
|
async-each "^1.0.1"
|
||||||
@ -2882,7 +2882,7 @@ chokidar@^2.0.2, chokidar@^2.0.4, chokidar@^2.1.0:
|
|||||||
normalize-path "^3.0.0"
|
normalize-path "^3.0.0"
|
||||||
path-is-absolute "^1.0.0"
|
path-is-absolute "^1.0.0"
|
||||||
readdirp "^2.2.1"
|
readdirp "^2.2.1"
|
||||||
upath "^1.1.0"
|
upath "^1.1.1"
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "^1.2.7"
|
fsevents "^1.2.7"
|
||||||
|
|
||||||
@ -7354,12 +7354,12 @@ node-sass@~4.11.0:
|
|||||||
stdout-stream "^1.4.0"
|
stdout-stream "^1.4.0"
|
||||||
"true-case-path" "^1.0.2"
|
"true-case-path" "^1.0.2"
|
||||||
|
|
||||||
nodemon@^1.18.9, nodemon@~1.18.10:
|
nodemon@^1.18.9, nodemon@~1.18.11:
|
||||||
version "1.18.10"
|
version "1.18.11"
|
||||||
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.18.10.tgz#3ba63f64eb4c283cf3e4f75f30817e9d4f393afe"
|
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.18.11.tgz#d836ab663776e7995570b963da5bfc807e53f6b8"
|
||||||
integrity sha512-we51yBb1TfEvZamFchRgcfLbVYgg0xlGbyXmOtbBzDwxwgewYS/YbZ5tnlnsH51+AoSTTsT3A2E/FloUbtH8cQ==
|
integrity sha512-KdN3tm1zkarlqNo4+W9raU3ihM4H15MVMSE/f9rYDZmFgDHAfAJsomYrHhApAkuUemYjFyEeXlpCOQ2v5gtBEw==
|
||||||
dependencies:
|
dependencies:
|
||||||
chokidar "^2.1.0"
|
chokidar "^2.1.5"
|
||||||
debug "^3.1.0"
|
debug "^3.1.0"
|
||||||
ignore-by-default "^1.0.1"
|
ignore-by-default "^1.0.1"
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
@ -10630,6 +10630,11 @@ upath@^1.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd"
|
resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd"
|
||||||
integrity sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==
|
integrity sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==
|
||||||
|
|
||||||
|
upath@^1.1.1:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068"
|
||||||
|
integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==
|
||||||
|
|
||||||
update-notifier@^2.5.0:
|
update-notifier@^2.5.0:
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.5.0.tgz#d0744593e13f161e406acb1d9408b72cad08aff6"
|
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.5.0.tgz#d0744593e13f161e406acb1d9408b72cad08aff6"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user