Merge remote-tracking branch 'origin/master' into 734-authorization-problem-disabling-post

# Conflicts:
#	webapp/pages/profile/_id/_slug.spec.js
#	webapp/pages/profile/_id/_slug.vue
This commit is contained in:
Wolfgang Huß 2019-06-12 18:22:50 +02:00
commit cd0074ec86
30 changed files with 592 additions and 686 deletions

View File

@ -43,16 +43,16 @@
},
"dependencies": {
"activitystrea.ms": "~2.1.3",
"apollo-cache-inmemory": "~1.6.1",
"apollo-client": "~2.6.1",
"apollo-cache-inmemory": "~1.6.2",
"apollo-client": "~2.6.2",
"apollo-link-context": "~1.0.14",
"apollo-link-http": "~1.5.14",
"apollo-server": "~2.6.1",
"apollo-server": "~2.6.2",
"bcryptjs": "~2.4.3",
"cheerio": "~1.0.0-rc.3",
"cors": "~2.8.5",
"cross-env": "~5.2.0",
"date-fns": "2.0.0-alpha.29",
"date-fns": "2.0.0-alpha.31",
"debug": "~4.1.1",
"dotenv": "~8.0.0",
"express": "~4.17.1",
@ -61,7 +61,7 @@
"graphql-custom-directives": "~0.2.14",
"graphql-iso-date": "~3.6.1",
"graphql-middleware": "~3.0.2",
"graphql-shield": "~5.3.6",
"graphql-shield": "~5.3.8",
"graphql-tag": "~2.10.1",
"graphql-yoga": "~1.17.4",
"helmet": "~3.18.0",
@ -69,7 +69,6 @@
"linkifyjs": "~2.1.8",
"lodash": "~4.17.11",
"merge-graphql-schemas": "^1.5.8",
"ms": "~2.1.1",
"neo4j-driver": "~1.7.4",
"neo4j-graphql-js": "git+https://github.com/Human-Connection/neo4j-graphql-js.git#temporary_fixes",
"node-fetch": "~2.6.0",
@ -88,7 +87,7 @@
"@babel/plugin-proposal-throw-expressions": "^7.2.0",
"@babel/preset-env": "~7.4.5",
"@babel/register": "~7.4.4",
"apollo-server-testing": "~2.6.1",
"apollo-server-testing": "~2.6.2",
"babel-core": "~7.0.0-0",
"babel-eslint": "~10.0.1",
"babel-jest": "~24.8.0",
@ -106,7 +105,7 @@
"graphql-request": "~1.8.2",
"jest": "~24.8.0",
"nodemon": "~1.19.1",
"prettier": "~1.17.1",
"prettier": "~1.18.2",
"supertest": "~4.0.2"
}
}

View File

@ -505,9 +505,7 @@ export default class NitroDataSource {
const result2 = await this.client.mutate({
mutation: gql`
mutation {
AddCommentAuthor(from: {id: "${
result.data.CreateComment.id
}"}, to: {id: "${toUserId}"}) {
AddCommentAuthor(from: {id: "${result.data.CreateComment.id}"}, to: {id: "${toUserId}"}) {
id
}
}
@ -519,9 +517,7 @@ export default class NitroDataSource {
result = await this.client.mutate({
mutation: gql`
mutation {
AddCommentPost(from: { id: "${
result.data.CreateComment.id
}", to: { id: "${postId}" }}) {
AddCommentPost(from: { id: "${result.data.CreateComment.id}", to: { id: "${postId}" }}) {
id
}
}

View File

@ -1,11 +1,10 @@
import jwt from 'jsonwebtoken'
import ms from 'ms'
import CONFIG from './../config'
// Generate an Access Token for the given User ID
export default function encode(user) {
const token = jwt.sign(user, CONFIG.JWT_SECRET, {
expiresIn: ms('1d'),
expiresIn: 24 * 60 * 60 * 1000, // one day
issuer: CONFIG.GRAPHQL_URI,
audience: CONFIG.CLIENT_URI,
subject: user.id.toString(),

View File

@ -1,12 +0,0 @@
import replaceParams from './replaceParams'
const replaceFilterBubbleParams = async (resolve, root, args, context, resolveInfo) => {
args = await replaceParams(args, context)
return resolve(root, args, context, resolveInfo)
}
export default {
Query: {
Post: replaceFilterBubbleParams,
},
}

View File

@ -5,6 +5,7 @@ import Factory from '../../seed/factories'
const factory = Factory()
const currentUserParams = {
id: 'u1',
email: 'you@example.org',
name: 'This is you',
password: '1234',
@ -41,7 +42,7 @@ afterEach(async () => {
await factory.cleanDatabase()
})
describe('FilterBubble middleware', () => {
describe('Filter posts by author is followed by sb.', () => {
describe('given an authenticated user', () => {
let authenticatedClient
@ -52,7 +53,7 @@ describe('FilterBubble middleware', () => {
describe('no filter bubble', () => {
it('returns all posts', async () => {
const query = '{ Post( filterBubble: {}) { title } }'
const query = '{ Post(filter: { }) { title } }'
const expected = {
Post: [
{ title: 'This is some random post' },
@ -65,7 +66,7 @@ describe('FilterBubble middleware', () => {
describe('filtering for posts of followed users only', () => {
it('returns only posts authored by followed users', async () => {
const query = '{ Post( filterBubble: { author: following }) { title } }'
const query = '{ Post( filter: { author: { followedBy_some: { id: "u1" } } }) { title } }'
const expected = {
Post: [{ title: 'This is the post of a followed user' }],
}

View File

@ -1,31 +0,0 @@
import { UserInputError } from 'apollo-server'
export default async function replaceParams(args, context) {
const { author = 'all' } = args.filterBubble || {}
const { user } = context
if (author === 'following') {
if (!user)
throw new UserInputError(
"You are unauthenticated - I don't know any users you are following.",
)
const session = context.driver.session()
let { records } = await session.run(
'MATCH(followed:User)<-[:FOLLOWS]-(u {id: $userId}) RETURN followed.id',
{ userId: context.user.id },
)
const followedIds = records.map(record => record.get('followed.id'))
// carefully override `id_in`
args.filter = args.filter || {}
args.filter.author = args.filter.author || {}
args.filter.author.id_in = followedIds
session.close()
}
delete args.filterBubble
return args
}

View File

@ -1,129 +0,0 @@
import replaceParams from './replaceParams.js'
describe('replaceParams', () => {
let args
let context
let run
let action = () => {
return replaceParams(args, context)
}
beforeEach(() => {
args = {}
run = jest.fn().mockResolvedValue({
records: [{ get: () => 1 }, { get: () => 2 }, { get: () => 3 }],
})
context = {
driver: {
session: () => {
return {
run,
close: () => {},
}
},
},
}
})
describe('args == ', () => {
describe('{}', () => {
it('does not crash', async () => {
await expect(action()).resolves.toEqual({})
})
})
describe('unauthenticated user', () => {
beforeEach(() => {
context.user = null
})
describe('{ filterBubble: { author: following } }', () => {
it('throws error', async () => {
args = { filterBubble: { author: 'following' } }
await expect(action()).rejects.toThrow('You are unauthenticated')
})
})
describe('{ filterBubble: { author: all } }', () => {
it('removes filterBubble param', async () => {
const expected = {}
await expect(action()).resolves.toEqual(expected)
})
it('does not make database calls', async () => {
await action()
expect(run).not.toHaveBeenCalled()
})
})
})
describe('authenticated user', () => {
beforeEach(() => {
context.user = { id: 'u4711' }
})
describe('{ filterBubble: { author: following } }', () => {
beforeEach(() => {
args = { filterBubble: { author: 'following' } }
})
it('returns args object with resolved ids of followed users', async () => {
const expected = { filter: { author: { id_in: [1, 2, 3] } } }
await expect(action()).resolves.toEqual(expected)
})
it('makes database calls', async () => {
await action()
expect(run).toHaveBeenCalledTimes(1)
})
describe('given any additional filter args', () => {
describe('merges', () => {
it('empty filter object', async () => {
args.filter = {}
const expected = { filter: { author: { id_in: [1, 2, 3] } } }
await expect(action()).resolves.toEqual(expected)
})
it('filter.title', async () => {
args.filter = { title: 'bla' }
const expected = { filter: { title: 'bla', author: { id_in: [1, 2, 3] } } }
await expect(action()).resolves.toEqual(expected)
})
it('filter.author', async () => {
args.filter = { author: { name: 'bla' } }
const expected = { filter: { author: { name: 'bla', id_in: [1, 2, 3] } } }
await expect(action()).resolves.toEqual(expected)
})
})
})
})
describe('{ filterBubble: { } }', () => {
it('removes filterBubble param', async () => {
const expected = {}
await expect(action()).resolves.toEqual(expected)
})
it('does not make database calls', async () => {
await action()
expect(run).not.toHaveBeenCalled()
})
})
describe('{ filterBubble: { author: all } }', () => {
it('removes filterBubble param', async () => {
const expected = {}
await expect(action()).resolves.toEqual(expected)
})
it('does not make database calls', async () => {
await action()
expect(run).not.toHaveBeenCalled()
})
})
})
})
})

View File

@ -13,7 +13,6 @@ import includedFields from './includedFieldsMiddleware'
import orderBy from './orderByMiddleware'
import validation from './validation'
import notifications from './notifications'
import filterBubble from './filterBubble/filterBubble'
export default schema => {
const middlewares = {
@ -31,13 +30,11 @@ export default schema => {
user: user,
includedFields: includedFields,
orderBy: orderBy,
filterBubble: filterBubble,
}
let order = [
'permissions',
'activityPub',
'filterBubble',
'password',
'dateTime',
'validation',

View File

@ -87,9 +87,7 @@ describe('currentUser { notifications }', () => {
describe('who mentions me again', () => {
beforeEach(async () => {
const updatedContent = `${
post.content
} One more mention to <a href="/profile/you" class="mention">@al-capone</a>`
const updatedContent = `${post.content} One more mention to <a href="/profile/you" class="mention">@al-capone</a>`
// The response `post.content` contains a link but the XSSmiddleware
// should have the `mention` CSS class removed. I discovered this
// during development and thought: A feature not a bug! This way we

View File

@ -1,40 +1,3 @@
enum FilterBubbleAuthorEnum {
following
all
}
input FilterBubble {
author: FilterBubbleAuthorEnum
}
type Query {
Post(
id: ID
activityId: String
objectId: String
title: String
slug: String
content: String
contentExcerpt: String
image: String
imageUpload: Upload
visibility: Visibility
deleted: Boolean
disabled: Boolean
createdAt: String
updatedAt: String
commentsCount: Int
shoutedCount: Int
shoutedByCurrentUser: Boolean
_id: String
first: Int
offset: Int
orderBy: [_PostOrdering]
filter: _PostFilter
filterBubble: FilterBubble
): [Post]
}
type Post {
id: ID!
activityId: String

View File

@ -56,14 +56,14 @@ type User {
contributionsCount: Int! @cypher(
statement: """
MATCH (this)-[:WROTE]->(r:Post)
WHERE (NOT exists(r.deleted) OR r.deleted = false)
AND (NOT exists(r.disabled) OR r.disabled = false)
WHERE NOT r.deleted = true AND NOT r.disabled = true
RETURN COUNT(r)
"""
)
comments: [Comment]! @relation(name: "WROTE", direction: "OUT")
commentsCount: Int! @cypher(statement: "MATCH (this)-[:WROTE]->(r:Comment) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(r)")
commentedCount: Int! @cypher(statement: "MATCH (this)-[:WROTE]->(r:Comment)-[:COMMENTS]->(p:Post) WHERE NOT r.deleted = true AND NOT r.disabled = true AND NOT p.deleted = true AND NOT p.disabled = true RETURN COUNT(DISTINCT(p))")
shouted: [Post]! @relation(name: "SHOUTED", 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)")
@ -77,4 +77,4 @@ type User {
badges: [Badge]! @relation(name: "REWARDED", direction: "IN")
badgesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)")
}
}

View File

@ -1119,10 +1119,10 @@
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.9.tgz#693e76a52f61a2f1e7fb48c0eef167b95ea4ffd0"
integrity sha512-sCZy4SxP9rN2w30Hlmg5dtdRwgYQfYRiLo9usw8X9cxlf+H4FqM1xX7+sNH7NNKVdbXMJWqva7iyy+fxh/V7fA==
"@types/yup@0.26.14":
version "0.26.14"
resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.26.14.tgz#d31f3b9a04039cca70ebb4db4d6c7fc3f694e80b"
integrity sha512-OcBtVLHvYULVSltpuBdhFiVOKoSsOS58D872HydO93oBf3OdGq5zb+LnqGo18TNNSV2aW8hjIdS6H+wp68zFtQ==
"@types/yup@0.26.16":
version "0.26.16"
resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.26.16.tgz#75c428236207c48d9f8062dd1495cda8c5485a15"
integrity sha512-E2RNc7DSeQ+2EIJ1H3+yFjYu6YiyQBUJ7yNpIxomrYJ3oFizLZ5yDS3T1JTUNBC2OCRkgnhLS0smob5UuCHfNA==
"@types/zen-observable@^0.5.3":
version "0.5.4"
@ -1141,6 +1141,13 @@
dependencies:
tslib "^1.9.3"
"@wry/equality@^0.1.2":
version "0.1.7"
resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.1.7.tgz#512234d078341c32cabda66b89b5dddb5741d9b9"
integrity sha512-p1rhJ6PQzpsBr9cMJMHvvx3LQEA28HFX7fAQx6khAX+1lufFeBuk+iRCAyHwj3v6JbpGKvHNa66f+9cpU8c7ew==
dependencies:
tslib "^1.9.3"
abab@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f"
@ -1281,13 +1288,13 @@ anymatch@^2.0.0:
micromatch "^3.1.4"
normalize-path "^2.1.1"
apollo-cache-control@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.7.1.tgz#3d4fba232f561f096f61051e103bf58ee4bf8b54"
integrity sha512-3h1TEoMnzex6IIiFb5Ja3owTyLwT5YzK69cRgrSpSscdpYc3ID4KVs0Ht9cbOUmb/L/UKtYVkRC8KeVAYmHEjQ==
apollo-cache-control@0.7.2:
version "0.7.2"
resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.7.2.tgz#b8852422d973c582493e85c776abc9c660090162"
integrity sha512-7prjFN8H9lRE0npqGG8kM3XICvNCcgQt6eCy8kkcPOIZwM+F8m8ShjEfNF9UWW32i+poOk3G67HegPRyjCc6/Q==
dependencies:
apollo-server-env "2.4.0"
graphql-extensions "0.7.1"
graphql-extensions "0.7.2"
apollo-cache-control@^0.1.0:
version "0.1.1"
@ -1296,34 +1303,34 @@ apollo-cache-control@^0.1.0:
dependencies:
graphql-extensions "^0.0.x"
apollo-cache-inmemory@~1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.1.tgz#536b6f366461f6264250041f9146363e2faa1d4c"
integrity sha512-c/WJjh9MTWcdussCTjLKufpPjTx3qOFkBPHIDOOpQ+U0B7K1PczPl9N0LaC4ir3wAWL7s4A0t2EKtoR+6UP92g==
apollo-cache-inmemory@~1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.2.tgz#bbf2e4e1eacdf82b2d526f5c2f3b37e5acee3c5e"
integrity sha512-AyCl3PGFv5Qv1w4N9vlg63GBPHXgMCekZy5mhlS042ji0GW84uTySX+r3F61ZX3+KM1vA4m9hQyctrEGiv5XjQ==
dependencies:
apollo-cache "^1.3.1"
apollo-utilities "^1.3.1"
apollo-cache "^1.3.2"
apollo-utilities "^1.3.2"
optimism "^0.9.0"
ts-invariant "^0.4.0"
tslib "^1.9.3"
apollo-cache@1.3.1, apollo-cache@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/apollo-cache/-/apollo-cache-1.3.1.tgz#c015f93a9a7f32b3eeea0c471addd6e854da754c"
integrity sha512-BJ/Mehr3u6XCaHYSmgZ6DM71Fh30OkW6aEr828WjHvs+7i0RUuP51/PM7K6T0jPXtuw7UbArFFPZZsNgXnyyJA==
apollo-cache@1.3.2, apollo-cache@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/apollo-cache/-/apollo-cache-1.3.2.tgz#df4dce56240d6c95c613510d7e409f7214e6d26a"
integrity sha512-+KA685AV5ETEJfjZuviRTEImGA11uNBp/MJGnaCvkgr+BYRrGLruVKBv6WvyFod27WEB2sp7SsG8cNBKANhGLg==
dependencies:
apollo-utilities "^1.3.1"
apollo-utilities "^1.3.2"
tslib "^1.9.3"
apollo-client@~2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-2.6.1.tgz#fcf328618d6ad82b750a988bec113fe6edc8ba94"
integrity sha512-Tb6ZthPZUHlGqeoH1WC8Qg/tLnkk9H5+xj4e5nzOAC6dCOW3pVU9tYXscrWdmZ65UDUg1khvTNjrQgPhdf4aTQ==
apollo-client@~2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-2.6.2.tgz#03b6af651e09b6e413e486ddc87464c85bd6e514"
integrity sha512-oks1MaT5x7gHcPeC8vPC1UzzsKaEIC0tye+jg72eMDt5OKc7BobStTeS/o2Ib3e0ii40nKxGBnMdl/Xa/p56Yg==
dependencies:
"@types/zen-observable" "^0.8.0"
apollo-cache "1.3.1"
apollo-cache "1.3.2"
apollo-link "^1.0.0"
apollo-utilities "1.3.1"
apollo-utilities "1.3.2"
symbol-observable "^1.0.2"
ts-invariant "^0.4.0"
tslib "^1.9.3"
@ -1337,24 +1344,24 @@ apollo-datasource@0.5.0:
apollo-server-caching "0.4.0"
apollo-server-env "2.4.0"
apollo-engine-reporting-protobuf@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/apollo-engine-reporting-protobuf/-/apollo-engine-reporting-protobuf-0.3.0.tgz#2c764c054ff9968387cf16115546e0d5b04ee9f1"
integrity sha512-PYowpx/E+TJT/8nKpp3JmJuKh3x1SZcxDF6Cquj0soV205TUpFFCZQMi91i5ACiEp2AkYvM/GDBIrw+rfIwzTg==
apollo-engine-reporting-protobuf@0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/apollo-engine-reporting-protobuf/-/apollo-engine-reporting-protobuf-0.3.1.tgz#a581257fa8e3bb115ce38bf1b22e052d1475ad69"
integrity sha512-Ui3nPG6BSZF8BEqxFs6EkX6mj2OnFLMejxEHSOdM82bakyeouCGd7J0fiy8AD6liJoIyc4X7XfH4ZGGMvMh11A==
dependencies:
protobufjs "^6.8.6"
apollo-engine-reporting@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.2.1.tgz#0b77fad2e9221d62f4a29b8b4fab8f7f47dcc1d6"
integrity sha512-DVXZhz/nSZR4lphakjb1guAD0qJ7Wm1PVtZEBjN097cnOor4XSOzQlPfTaYtVuhlxUKUuCx1syiBbOuV8sKqXg==
apollo-engine-reporting@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.3.0.tgz#50151811a0f5e70f4a73e7092a61fec422d8e722"
integrity sha512-xP+Z+wdQH4ee7xfuP3WsJcIe30AH68gpp2lQm2+rnW5JfjIqD5YehSoO2Svi2jK3CSv8Y561i3QMW9i34P7hEQ==
dependencies:
apollo-engine-reporting-protobuf "0.3.0"
apollo-engine-reporting-protobuf "0.3.1"
apollo-graphql "^0.3.0"
apollo-server-core "2.6.1"
apollo-server-core "2.6.2"
apollo-server-env "2.4.0"
async-retry "^1.2.1"
graphql-extensions "0.7.1"
graphql-extensions "0.7.2"
apollo-env@0.5.1:
version "0.5.1"
@ -1424,24 +1431,24 @@ apollo-server-caching@0.4.0:
dependencies:
lru-cache "^5.0.0"
apollo-server-core@2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.6.1.tgz#d0d878b0a4959b6c661fc43300ce45b29996176a"
integrity sha512-jO2BtcP7ozSSK5qtw1gGDwO66WSNtzhvpDJD7erkA9byv8Z0jB2QIPNWN6iaj311LaPahM05k+8hMIhFy9oHWg==
apollo-server-core@2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.6.2.tgz#a792b50d4df9e26ec03759a31fbcbce38361b218"
integrity sha512-AbAnfoQ26NPsNIyBa/BVKBtA/wRsNL/E6eEem1VIhzitfgO25bVXFbEZDLxbgz6wvJ+veyRFpse7Qi1bvRpxOw==
dependencies:
"@apollographql/apollo-tools" "^0.3.6"
"@apollographql/graphql-playground-html" "1.6.20"
"@types/ws" "^6.0.0"
apollo-cache-control "0.7.1"
apollo-cache-control "0.7.2"
apollo-datasource "0.5.0"
apollo-engine-reporting "1.2.1"
apollo-engine-reporting "1.3.0"
apollo-server-caching "0.4.0"
apollo-server-env "2.4.0"
apollo-server-errors "2.3.0"
apollo-server-plugin-base "0.5.1"
apollo-tracing "0.7.1"
apollo-server-plugin-base "0.5.2"
apollo-tracing "0.7.2"
fast-json-stable-stringify "^2.0.0"
graphql-extensions "0.7.1"
graphql-extensions "0.7.2"
graphql-subscriptions "^1.0.0"
graphql-tag "^2.9.2"
graphql-tools "^4.0.0"
@ -1472,10 +1479,10 @@ apollo-server-errors@2.3.0:
resolved "https://registry.yarnpkg.com/apollo-server-errors/-/apollo-server-errors-2.3.0.tgz#700622b66a16dffcad3b017e4796749814edc061"
integrity sha512-rUvzwMo2ZQgzzPh2kcJyfbRSfVKRMhfIlhY7BzUfM4x6ZT0aijlgsf714Ll3Mbf5Fxii32kD0A/DmKsTecpccw==
apollo-server-express@2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-2.6.1.tgz#1e2649d3fd38c0c0a2c830090fd41e086b259c9f"
integrity sha512-TVu68LVp+COMGOXuxc0OFeCUQiPApxy7Isv2Vk85nikZV4t4FXlODB6PrRKf5rfvP31dvGsfE6GHPJTLLbKfyg==
apollo-server-express@2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-2.6.2.tgz#526297c01a7a32fe9215566f9fd7ff92e82f1fa0"
integrity sha512-nbL3noJ5KxKGg+hT8UsAA7++oHWq/KNSevfdCluWTfUNqH1vYRTvAnARx/6JM06S9zcPTfOLcqwHnDnY9zYFxA==
dependencies:
"@apollographql/graphql-playground-html" "1.6.20"
"@types/accepts" "^1.3.5"
@ -1483,7 +1490,7 @@ apollo-server-express@2.6.1:
"@types/cors" "^2.8.4"
"@types/express" "4.16.1"
accepts "^1.3.5"
apollo-server-core "2.6.1"
apollo-server-core "2.6.2"
body-parser "^1.18.3"
cors "^2.8.4"
graphql-subscriptions "^1.0.0"
@ -1511,36 +1518,36 @@ apollo-server-module-graphiql@^1.3.4, apollo-server-module-graphiql@^1.4.0:
resolved "https://registry.yarnpkg.com/apollo-server-module-graphiql/-/apollo-server-module-graphiql-1.4.0.tgz#c559efa285578820709f1769bb85d3b3eed3d8ec"
integrity sha512-GmkOcb5he2x5gat+TuiTvabnBf1m4jzdecal3XbXBh/Jg+kx4hcvO3TTDFQ9CuTprtzdcVyA11iqG7iOMOt7vA==
apollo-server-plugin-base@0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.5.1.tgz#b81056666763879bdc98d8d58f3c4c43cbb30da6"
integrity sha512-UejnBk6XDqYQ+Ydkbm+gvlOzP+doQA8glVUULs8rCi0/MshsFSsBVl6rtzruELDBVgZhJgGsd4pUexcvNc3aZA==
apollo-server-plugin-base@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.5.2.tgz#f97ba983f1e825fec49cba8ff6a23d00e1901819"
integrity sha512-j81CpadRLhxikBYHMh91X4aTxfzFnmmebEiIR9rruS6dywWCxV2aLW87l9ocD1MiueNam0ysdwZkX4F3D4csNw==
apollo-server-testing@~2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/apollo-server-testing/-/apollo-server-testing-2.6.1.tgz#447f34980819fa52b120f26c632fab4efc55435b"
integrity sha512-Qq0u79uKw3g14bq0nGxtUUiueFOv2ETkAax2mum+3f9Lm85VXELkY6c9bCWDVGjkUtt9Aog5qwSzWELb1KiUug==
apollo-server-testing@~2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/apollo-server-testing/-/apollo-server-testing-2.6.2.tgz#e0ecddd565fce1c38a346f9fbe6118f543ccf6a6"
integrity sha512-I9QLFk4I/z9oOIXfnLc8RPBYAKih6Olrg3RDeRvWhDjLQ8gfALXVhCO+7WuvM35wNZcZVn7aXBeZ8Y3mlgkj8w==
dependencies:
apollo-server-core "2.6.1"
apollo-server-core "2.6.2"
apollo-server@~2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/apollo-server/-/apollo-server-2.6.1.tgz#1b1fc6020b75c0913550da5fa0f2005c62f1bc53"
integrity sha512-Ed0zZjluRYPMC3Yr6oXQjcR11izu86nkjiS2MhjJA1mF8IXJfxbPp2hnX4Jf4vXPSkOP2e5ZHw0cdaIcu9GnRw==
apollo-server@~2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/apollo-server/-/apollo-server-2.6.2.tgz#33fe894b740588f059a7679346516ffce50377d5"
integrity sha512-fMXaAKIb0dX0lzcZ4zlu7ay1L596d9HTNkdn8cKuM7zmTpugZSAL966COguJUDSjUS9CaB1Kh5hl1yRuRqHXSA==
dependencies:
apollo-server-core "2.6.1"
apollo-server-express "2.6.1"
apollo-server-core "2.6.2"
apollo-server-express "2.6.2"
express "^4.0.0"
graphql-subscriptions "^1.0.0"
graphql-tools "^4.0.0"
apollo-tracing@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.7.1.tgz#6a7356b619f3aa0ca22c623b5d8bb1af5ca1c74c"
integrity sha512-1BYQua+jCWFkZZJP0/rSpzY4XbLLbCrRHCYu8sJn0RCH/hs34BMdFXscS9uSglgIpXwUAIafgsU0hAVCrJjbTw==
apollo-tracing@0.7.2:
version "0.7.2"
resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.7.2.tgz#7730159a4670bca465ac1bfa01f9902610a7aba4"
integrity sha512-bT4/n8Vy9DweC3+XWJelJD41FBlKMXR0OVxjLMiCe9clb4yTgKhYxRGTyh9KjmhWsng9gG/DphO0ixWsOgdXmA==
dependencies:
apollo-server-env "2.4.0"
graphql-extensions "0.7.1"
graphql-extensions "0.7.2"
apollo-tracing@^0.1.0:
version "0.1.4"
@ -1559,13 +1566,13 @@ apollo-upload-server@^7.0.0:
http-errors "^1.7.0"
object-path "^0.11.4"
apollo-utilities@1.3.1, apollo-utilities@^1.0.1, apollo-utilities@^1.2.1, apollo-utilities@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.1.tgz#4c45f9b52783c324e2beef822700bdea374f82d1"
integrity sha512-P5cJ75rvhm9hcx9V/xCW0vlHhRd0S2icEcYPoRYNTc5djbynpuO+mQuJ4zMHgjNDpvvDxDfZxXTJ6ZUuJZodiQ==
apollo-utilities@1.3.2, apollo-utilities@^1.0.1, apollo-utilities@^1.2.1, apollo-utilities@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.2.tgz#8cbdcf8b012f664cd6cb5767f6130f5aed9115c9"
integrity sha512-JWNHj8XChz7S4OZghV6yc9FNnzEXj285QYp/nLNh943iObycI5GTDO3NGR9Dth12LRrSFMeDOConPfPln+WGfg==
dependencies:
"@wry/equality" "^0.1.2"
fast-json-stable-stringify "^2.0.0"
lodash.isequal "^4.5.0"
ts-invariant "^0.4.0"
tslib "^1.9.3"
@ -2579,10 +2586,10 @@ data-urls@^1.0.0:
whatwg-mimetype "^2.2.0"
whatwg-url "^7.0.0"
date-fns@2.0.0-alpha.29:
version "2.0.0-alpha.29"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.29.tgz#9d4a36e3ebba63d009e957fea8fdfef7921bc6cb"
integrity sha512-AIFZ0hG/1fdb7HZHTDyiEJdNiaFyZxXcx/kF8z3I9wxbhkN678KrrLSneKcsb0Xy5KqCA4wCIxmGpdVWSNZnpA==
date-fns@2.0.0-alpha.31:
version "2.0.0-alpha.31"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.31.tgz#51bcfdca25dfc9bea334a556ab33dfc0bb00421c"
integrity sha512-S19PwMqnbYsqcbCg02Yj9gv4veVNZ0OX7v2+zcd+Mq0RI7LoDKJipJjnMrTZ3Cc6blDuTce5G/pHXcVIGRwJWQ==
debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
version "2.6.9"
@ -3704,10 +3711,10 @@ graphql-deduplicator@^2.0.1:
resolved "https://registry.yarnpkg.com/graphql-deduplicator/-/graphql-deduplicator-2.0.2.tgz#d8608161cf6be97725e178df0c41f6a1f9f778f3"
integrity sha512-0CGmTmQh4UvJfsaTPppJAcHwHln8Ayat7yXXxdnuWT+Mb1dBzkbErabCWzjXyKh/RefqlGTTA7EQOZHofMaKJA==
graphql-extensions@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.7.1.tgz#f55b01ac8ddf09a215e21f34caeee3ae66a88f21"
integrity sha512-4NkAz/f0j5a1DSPl3V77OcesBmwhHz56Soj0yTImlcDdRv9knyO2e+ehi1TIeKBOyIKS7d3A7zqOW/4ieGxlVA==
graphql-extensions@0.7.2:
version "0.7.2"
resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.7.2.tgz#8711543f835661eaf24b48d6ac2aad44dbbd5506"
integrity sha512-TuVINuAOrEtzQAkAlCZMi9aP5rcZ+pVaqoBI5fD2k5O9fmb8OuXUQOW028MUhC66tg4E7h4YSF1uYUIimbu4SQ==
dependencies:
"@apollographql/apollo-tools" "^0.3.6"
@ -3765,12 +3772,12 @@ graphql-request@~1.8.2:
dependencies:
cross-fetch "2.2.2"
graphql-shield@~5.3.6:
version "5.3.6"
resolved "https://registry.yarnpkg.com/graphql-shield/-/graphql-shield-5.3.6.tgz#20061b02f77056c0870a623c530ef28a1bf4fff4"
integrity sha512-ihw/i4X+d1kpj1SVA6iBkVl2DZhPsI+xV08geR2TX3FWhpU7zakk/16yBzDRJTTCUgKsWfgyebrgIBsuhTwMnA==
graphql-shield@~5.3.8:
version "5.3.8"
resolved "https://registry.yarnpkg.com/graphql-shield/-/graphql-shield-5.3.8.tgz#f9e7ad2285f6cfbe20a8a49154ce6c1b184e3893"
integrity sha512-33rQ8U5jMurHIapctHk7hBcUg3nxC7fmMIMtyWiomJXhBmztFq/SG7jNaapnL5M7Q/0BmoaSQd3FLSpelP9KPw==
dependencies:
"@types/yup" "0.26.14"
"@types/yup" "0.26.16"
lightercollective "^0.3.0"
object-hash "^1.3.1"
yup "^0.27.0"
@ -5230,11 +5237,6 @@ lodash.isboolean@^3.0.3:
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=
lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
lodash.isinteger@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
@ -5520,7 +5522,7 @@ ms@2.0.0:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
ms@2.1.1, ms@^2.1.1, ms@~2.1.1:
ms@2.1.1, ms@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
@ -6228,10 +6230,10 @@ prettier-linter-helpers@^1.0.0:
dependencies:
fast-diff "^1.1.2"
prettier@~1.17.1:
version "1.17.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.17.1.tgz#ed64b4e93e370cb8a25b9ef7fef3e4fd1c0995db"
integrity sha512-TzGRNvuUSmPgwivDqkZ9tM/qTGW9hqDKWOE9YHiyQdixlKbv7kvEqsmDPrcHJTKwthU774TQwZXVtaQ/mMsvjg==
prettier@~1.18.2:
version "1.18.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea"
integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==
pretty-format@^24.8.0:
version "24.8.0"

View File

@ -12,14 +12,16 @@ Then('I should be able to change my profile picture', () => {
cy.fixture(avatarUpload, 'base64').then(fileContent => {
cy.get('#customdropzone').upload(
{ fileContent, fileName: avatarUpload, mimeType: 'image/png' },
{ subjectType: 'drag-n-drop' },
{ subjectType: 'drag-n-drop' }
)
})
cy.get('#customdropzone')
.should('have.attr', 'style')
cy.get('.profile-avatar img')
.should('have.attr', 'src')
.and('contains', 'onourjourney')
cy.contains('.iziToast-message', 'Upload successful')
.should('have.length', 1)
cy.contains('.iziToast-message', 'Upload successful').should(
'have.length',
1
)
})
When("I visit another user's profile page", () => {
@ -31,4 +33,4 @@ Then('I cannot upload a picture', () => {
.children()
.should('not.have.id', 'customdropzone')
.should('have.class', 'ds-avatar')
})
})

View File

@ -9,9 +9,11 @@ localVue.use(Styleguide)
describe('FilterMenu.vue', () => {
let wrapper
let mocks
let propsData
const createWrapper = mountMethod => {
return mountMethod(FilterMenu, {
propsData,
mocks,
localVue,
})
@ -19,35 +21,48 @@ describe('FilterMenu.vue', () => {
beforeEach(() => {
mocks = { $t: () => {} }
propsData = {}
})
describe('mount', () => {
describe('given a user', () => {
beforeEach(() => {
wrapper = createWrapper(mount)
propsData = {
user: {
id: '4711',
},
}
})
it('renders a card', () => {
expect(wrapper.is('.ds-card')).toBe(true)
})
describe('click "filter-by-followed-authors-only" button', () => {
it('emits filterBubble object', () => {
wrapper.find({ name: 'filter-by-followed-authors-only' }).trigger('click')
expect(wrapper.emitted('changeFilterBubble')).toBeTruthy()
describe('mount', () => {
beforeEach(() => {
wrapper = createWrapper(mount)
})
it('toggles filterBubble.author property', () => {
wrapper.find({ name: 'filter-by-followed-authors-only' }).trigger('click')
expect(wrapper.emitted('changeFilterBubble')[0]).toEqual([{ author: 'following' }])
wrapper.find({ name: 'filter-by-followed-authors-only' }).trigger('click')
expect(wrapper.emitted('changeFilterBubble')[1]).toEqual([{ author: 'all' }])
it('renders a card', () => {
expect(wrapper.is('.ds-card')).toBe(true)
})
it('makes button primary', () => {
wrapper.find({ name: 'filter-by-followed-authors-only' }).trigger('click')
expect(
wrapper.find({ name: 'filter-by-followed-authors-only' }).classes('ds-button-primary'),
).toBe(true)
describe('click "filter-by-followed-authors-only" button', () => {
it('emits filterBubble object', () => {
wrapper.find({ name: 'filter-by-followed-authors-only' }).trigger('click')
expect(wrapper.emitted('changeFilterBubble')).toBeTruthy()
})
it('toggles filterBubble.author property', () => {
wrapper.find({ name: 'filter-by-followed-authors-only' }).trigger('click')
expect(wrapper.emitted('changeFilterBubble')[0]).toEqual([
{ author: { followedBy_some: { id: '4711' } } },
])
wrapper.find({ name: 'filter-by-followed-authors-only' }).trigger('click')
expect(wrapper.emitted('changeFilterBubble')[1]).toEqual([{}])
})
it('makes button primary', () => {
wrapper.find({ name: 'filter-by-followed-authors-only' }).trigger('click')
expect(
wrapper.find({ name: 'filter-by-followed-authors-only' }).classes('ds-button-primary'),
).toBe(true)
})
})
})
})

View File

@ -11,7 +11,7 @@
<ds-button
name="filter-by-followed-authors-only"
icon="user-plus"
:primary="onlyFollowed"
:primary="!!filterAuthorIsFollowedById"
@click="toggleOnlyFollowed"
/>
</div>
@ -22,24 +22,30 @@
<script>
export default {
props: {
user: { type: Object, required: true },
},
data() {
// We have to fix styleguide here. It uses .includes wich will always be
// false for arrays of objects.
return {
filterBubble: {
author: 'all',
},
filter: {},
}
},
computed: {
onlyFollowed() {
return this.filterBubble.author === 'following'
filterAuthorIsFollowedById() {
const { author = {} } = this.filter
/* eslint-disable camelcase */
const { followedBy_some = {} } = author
const { id } = followedBy_some
/* eslint-enable */
return id
},
},
methods: {
toggleOnlyFollowed() {
this.filterBubble.author = this.onlyFollowed ? 'all' : 'following'
this.$emit('changeFilterBubble', this.filterBubble)
this.filter = this.filterAuthorIsFollowedById
? {}
: { author: { followedBy_some: { id: this.user.id } } }
this.$emit('changeFilterBubble', this.filter)
},
},
}

View File

@ -1,5 +1,5 @@
<template>
<ds-space margin-top="large" style="text-align: center">
<ds-space class="load-more" margin-top="large" style="text-align: center">
<ds-button :loading="loading" icon="arrow-down" ghost @click="$emit('click')">
{{ $t('actions.loadMore') }}
</ds-button>

View File

@ -26,12 +26,15 @@ describe('DisableModal.vue', () => {
truncate: a => a,
},
$toast: {
success: () => {},
error: () => {},
success: jest.fn(),
error: jest.fn(),
},
$t: jest.fn(),
$apollo: {
mutate: jest.fn().mockResolvedValue(),
mutate: jest
.fn()
.mockResolvedValueOnce({ enable: 'u4711' })
.mockRejectedValue({ message: 'Not Authorised!' }),
},
location: {
reload: jest.fn(),
@ -151,6 +154,10 @@ describe('DisableModal.vue', () => {
await wrapper.find('button.confirm').trigger('click')
})
afterEach(() => {
jest.clearAllMocks()
})
it('calls mutation', () => {
expect(mocks.$apollo.mutate).toHaveBeenCalled()
})
@ -174,6 +181,18 @@ describe('DisableModal.vue', () => {
expect(wrapper.emitted().close).toBeTruthy()
})
})
describe('handles errors', () => {
beforeEach(() => {
wrapper = Wrapper()
// second submission causes mutation to reject
wrapper.find('button.confirm').trigger('click')
})
it('shows an error toaster when mutation rejects', async () => {
await expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorised!')
})
})
})
})
})

View File

@ -65,9 +65,6 @@ export default {
setTimeout(() => {
this.$emit('close')
}, 1000)
setTimeout(() => {
location.reload()
}, 250)
} catch (err) {
this.$toast.error(err.message)
}

View File

@ -23,12 +23,15 @@ describe('ReleaseModal.vue', () => {
truncate: a => a,
},
$toast: {
success: () => {},
error: () => {},
success: jest.fn(),
error: jest.fn(),
},
$t: jest.fn(),
$apollo: {
mutate: jest.fn().mockResolvedValue(),
mutate: jest
.fn()
.mockResolvedValueOnce({ enable: 'u4711' })
.mockRejectedValue({ message: 'Not Authorised!' }),
},
location: {
reload: jest.fn(),
@ -146,6 +149,10 @@ describe('ReleaseModal.vue', () => {
wrapper.find('button.confirm').trigger('click')
})
afterEach(() => {
jest.clearAllMocks()
})
it('calls mutation', () => {
expect(mocks.$apollo.mutate).toHaveBeenCalled()
})
@ -169,6 +176,18 @@ describe('ReleaseModal.vue', () => {
expect(wrapper.emitted().close).toBeTruthy()
})
})
describe('handles errors', () => {
beforeEach(() => {
wrapper = Wrapper()
// second submission causes mutation to reject
wrapper.find('button.confirm').trigger('click')
})
it('shows an error toaster when mutation rejects', async () => {
await expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorised!')
})
})
})
})
})

View File

@ -61,17 +61,9 @@ export default {
})
this.$toast.success(this.$t('release.success'))
this.isOpen = false
/*
setTimeout(() => {
location.reload()
}, 1500)
*/
setTimeout(() => {
this.$emit('close')
}, 1000)
setTimeout(() => {
location.reload()
}, 250)
} catch (err) {
this.$toast.error(err.message)
}

View File

@ -4,21 +4,30 @@
id="customdropzone"
:key="user.avatar"
ref="el"
:use-custom-slot="true"
:options="dropzoneOptions"
:include-styling="false"
:style="backgroundImage"
@vdropzone-thumbnail="thumbnail"
@vdropzone-error="verror"
/>
>
<div class="dz-message" @mouseover="hover = true" @mouseleave="hover = false">
<hc-avatar :user="user" class="profile-avatar" size="x-large"></hc-avatar>
<div class="hc-attachments-upload-area">
<div class="hc-drag-marker">
<ds-icon v-if="hover" name="image" size="xxx-large" />
</div>
</div>
</div>
</vue-dropzone>
</div>
</template>
<script>
import vueDropzone from 'nuxt-dropzone'
import gql from 'graphql-tag'
import HcAvatar from '~/components/Avatar/Avatar.vue'
export default {
components: {
vueDropzone,
HcAvatar,
},
props: {
user: { type: Object, default: null },
@ -29,22 +38,11 @@ export default {
url: this.vddrop,
maxFilesize: 5.0,
previewTemplate: this.template(),
dictDefaultMessage: '',
},
error: false,
hover: false,
}
},
computed: {
backgroundImage() {
const avatar =
this.user.avatar ||
'https://human-connection.org/wp-content/uploads/2019/03/human-connection-logo.svg'
const userAvatar = avatar.startsWith('/') ? avatar.replace('/', '/api/') : avatar
return {
backgroundImage: `url(${userAvatar})`,
}
},
},
watch: {
error() {
let that = this
@ -62,20 +60,6 @@ export default {
</div>
`
},
thumbnail(file, dataUrl) {
let j, len, ref, thumbnailElement
if (file.previewElement) {
this.$refs.el.$el.style.backgroundImage = ''
file.previewElement.classList.remove('dz-file-preview')
ref = file.previewElement.querySelectorAll('[data-dz-thumbnail-bg]')
for (j = 0, len = ref.length; j < len; j++) {
thumbnailElement = ref[j]
thumbnailElement.alt = file.name
thumbnailElement.style.backgroundImage = 'url("' + dataUrl + '")'
}
file.previewElement.classList.add('dz-image-preview')
}
},
vddrop(file) {
const avatarUpload = file[0]
this.$apollo
@ -107,25 +91,7 @@ export default {
},
}
</script>
<style>
#customdropzone {
margin: -60px auto auto;
width: 122px;
min-height: 122px;
background-size: cover;
background-repeat: no-repeat;
border-radius: 50%;
font-family: 'Arial', sans-serif;
letter-spacing: 0.2px;
color: #777;
transition: background-color 0.2s linear;
padding: 40px;
}
#customdropzone:hover {
cursor: pointer;
}
<style lang="scss">
#customdropzone .dz-preview {
transition: all 0.2s ease-out;
width: 160px;
@ -150,15 +116,59 @@ export default {
width: 100%;
}
#customdropzone .dz-preview .dz-details {
color: white;
transition: opacity 0.2s linear;
text-align: center;
.hc-attachments-upload-area {
position: relative;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
#customdropzone .dz-success-mark,
.dz-error-mark,
.dz-remove {
display: none;
.hc-attachments-upload-button {
pointer-events: none;
}
.hc-drag-marker {
position: relative;
width: 122px;
height: 122px;
border-radius: 100%;
display: flex;
align-items: center;
justify-content: center;
color: hsl(0, 0%, 25%);
transition: all 0.2s ease-out;
font-size: 60px;
margin: -120px auto 5px;
background-color: rgba(255, 255, 255, 0.3);
opacity: 0.1;
&:before {
position: absolute;
content: '';
top: 0;
left: 0;
bottom: 0;
right: 0;
border-radius: 100%;
border: 20px solid rgba(255, 255, 255, 0.4);
visibility: hidden;
}
&:after {
position: absolute;
content: '';
top: 10px;
left: 10px;
bottom: 10px;
right: 10px;
border-radius: 100%;
border: 1px dashed hsl(0, 0%, 25%);
}
.hc-attachments-upload-area:hover & {
opacity: 1;
}
}
</style>

View File

@ -35,26 +35,6 @@ describe('Upload', () => {
},
}
const fileSuccess = {
filename: 'avatar.jpg',
previewElement: {
classList: {
remove: jest.fn(),
add: jest.fn(),
},
querySelectorAll: jest.fn().mockReturnValue([
{
alt: '',
style: {
'background-image': '/api/generic.jpg',
},
},
]),
},
}
const dataUrl = 'avatar.jpg'
beforeEach(() => {
jest.useFakeTimers()
wrapper = shallowMount(Upload, { localVue, propsData, mocks })
@ -69,11 +49,6 @@ describe('Upload', () => {
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
})
it('thumbnail', () => {
wrapper.vm.thumbnail(fileSuccess, dataUrl)
expect(fileSuccess.previewElement.classList.add).toHaveBeenCalledTimes(1)
})
describe('error handling', () => {
const message = 'File upload failed'
const fileError = { status: 'error' }
@ -93,5 +68,15 @@ describe('Upload', () => {
jest.runAllTimers()
expect(wrapper.vm.error).toEqual(false)
})
it('shows an error toaster when the apollo mutation rejects', async () => {
// calls vddrop twice because of how mockResolvedValueOnce works in jest
// the first time the mock function is called it will resolve, calling it a
// second time will cause it to fail(with this implementation)
// https://jestjs.io/docs/en/mock-function-api.html#mockfnmockresolvedvalueoncevalue
await wrapper.vm.vddrop([{ filename: 'avatar.jpg' }])
await wrapper.vm.vddrop([{ filename: 'avatar.jpg' }])
expect(mocks.$toast.error).toHaveBeenCalledTimes(1)
})
})
})

View File

@ -0,0 +1,38 @@
import gql from 'graphql-tag'
export default i18n => {
const lang = i18n.locale().toUpperCase()
return gql(`
query Post($filter: _PostFilter, $first: Int, $offset: Int) {
Post(filter: $filter, first: $first, offset: $offset, orderBy: createdAt_desc) {
id
slug
title
contentExcerpt
shoutedCount
commentsCount
deleted
image
createdAt
disabled
deleted
categories {
id
name
icon
}
author {
id
slug
avatar
name
disabled
deleted
location {
name: name${lang}
}
}
}
}
`)
}

View File

@ -1,10 +1,10 @@
import gql from 'graphql-tag'
export default app => {
const lang = app.$i18n.locale().toUpperCase()
export default i18n => {
const lang = i18n.locale().toUpperCase()
return gql(`
query User($slug: String!, $first: Int, $offset: Int) {
User(slug: $slug) {
query User($id: ID!) {
User(id: $id) {
id
slug
name
@ -24,7 +24,7 @@ export default app => {
}
badgesCount
shoutedCount
commentsCount
commentedCount
followingCount
following(first: 7) {
id
@ -69,35 +69,6 @@ export default app => {
}
}
contributionsCount
contributions(first: $first, offset: $offset, orderBy: createdAt_desc) {
id
slug
title
contentExcerpt
shoutedCount
commentsCount
deleted
image
createdAt
disabled
deleted
categories {
id
name
icon
}
author {
id
slug
avatar
name
disabled
deleted
location {
name: name${lang}
}
}
}
socialMedia {
id
url

View File

@ -25,7 +25,16 @@
"shouted": "Empfohlen",
"commented": "Kommentiert",
"userAnonym": "Anonymus",
"socialMedia": "Wo sonst finde ich"
"socialMedia": "Wo sonst finde ich",
"network": {
"title": "Netzwerk",
"following": "folgt:",
"followingNobody": "folgt niemandem.",
"followedBy": "wird gefolgt von:",
"followedByNobody": "wird von niemandem gefolgt.",
"and": "und",
"more": "weitere"
}
},
"notifications": {
"menu": {
@ -163,11 +172,6 @@
}
},
"common": {
"your": {
"post": "Dein Beitrag ::: Deine Beiträge",
"comment": "Dein Kommentar ::: Deine Kommentare",
"shouted": "Deine Empfehlung ::: Deine Empfehlungen"
},
"post": "Beitrag ::: Beiträge",
"comment": "Kommentar ::: Kommentare",
"letsTalk": "Miteinander reden",

View File

@ -25,7 +25,16 @@
"shouted": "Shouted",
"commented": "Commented",
"userAnonym": "Anonymous",
"socialMedia": "Where else can I find"
"socialMedia": "Where else can I find",
"network": {
"title": "Network",
"following": "is following:",
"followingNobody": "follows nobody.",
"followedBy": "is followed by:",
"followedByNobody": "is not followed by anyone.",
"and": "and",
"more": "more"
}
},
"notifications": {
"menu": {
@ -163,11 +172,6 @@
}
},
"common": {
"your": {
"post": "Your Post ::: Your Posts",
"comment": "Your Comment ::: Your Comments",
"shout": "Your Shout ::: Your Shouts"
},
"post": "Post ::: Posts",
"comment": "Comment ::: Comments",
"letsTalk": "Let`s Talk",

View File

@ -51,15 +51,15 @@
"dependencies": {
"@human-connection/styleguide": "0.5.17",
"@nuxtjs/apollo": "4.0.0-rc4.2",
"@nuxtjs/axios": "~5.5.3",
"@nuxtjs/axios": "~5.5.4",
"@nuxtjs/dotenv": "~1.3.0",
"@nuxtjs/style-resources": "~0.1.2",
"accounting": "~0.4.1",
"apollo-cache-inmemory": "~1.5.1",
"apollo-client": "~2.6.1",
"cookie-universal-nuxt": "~2.0.14",
"apollo-client": "~2.6.2",
"cookie-universal-nuxt": "~2.0.16",
"cross-env": "~5.2.0",
"date-fns": "2.0.0-alpha.29",
"date-fns": "2.0.0-alpha.31",
"express": "~4.17.1",
"graphql": "~14.3.1",
"jsonwebtoken": "~8.5.1",
@ -70,12 +70,11 @@
"stack-utils": "^1.0.2",
"string-hash": "^1.1.3",
"tiptap": "1.20.1",
"tiptap-extensions": "1.21.0",
"tiptap-extensions": "1.22.2",
"v-tooltip": "~2.0.2",
"vue-count-to": "~1.0.13",
"vue-izitoast": "1.1.2",
"vue-sweetalert-icons": "~3.2.0",
"vue2-dropzone": "^3.5.9",
"vuex-i18n": "~1.11.0",
"zxcvbn": "^4.4.2"
},
@ -105,9 +104,9 @@
"jest": "~24.8.0",
"node-sass": "~4.12.0",
"nodemon": "~1.19.1",
"prettier": "~1.17.1",
"prettier": "~1.18.2",
"sass-loader": "~7.1.0",
"tippy.js": "^4.3.2",
"tippy.js": "^4.3.4",
"vue-jest": "~3.0.4",
"vue-svg-loader": "~0.12.0"
}

View File

@ -2,7 +2,7 @@
<div>
<ds-flex :width="{ base: '100%' }" gutter="base">
<ds-flex-item>
<filter-menu @changeFilterBubble="changeFilterBubble" />
<filter-menu :user="currentUser" @changeFilterBubble="changeFilterBubble" />
</ds-flex-item>
<hc-post-card
v-for="(post, index) in uniq(Post)"
@ -32,6 +32,7 @@ import gql from 'graphql-tag'
import uniqBy from 'lodash/uniqBy'
import HcPostCard from '~/components/PostCard'
import HcLoadMore from '~/components/LoadMore.vue'
import { mapGetters } from 'vuex'
export default {
components: {
@ -45,10 +46,13 @@ export default {
Post: [],
page: 1,
pageSize: 10,
filterBubble: { author: 'all' },
filter: {},
}
},
computed: {
...mapGetters({
currentUser: 'auth/user',
}),
tags() {
return this.Post ? this.Post[0].tags.map(tag => tag.name) : '-'
},
@ -57,8 +61,8 @@ export default {
},
},
methods: {
changeFilterBubble(filterBubble) {
this.filterBubble = filterBubble
changeFilterBubble(filter) {
this.filter = filter
this.$apollo.queries.Post.refresh()
},
uniq(items, field = 'id') {
@ -76,7 +80,7 @@ export default {
this.page++
this.$apollo.queries.Post.fetchMore({
variables: {
filterBubble: this.filterBubble,
filter: this.filter,
first: this.pageSize,
offset: this.offset,
},
@ -102,8 +106,8 @@ export default {
Post: {
query() {
return gql(`
query Post($filterBubble: FilterBubble, $first: Int, $offset: Int) {
Post(filterBubble: $filterBubble, first: $first, offset: $offset) {
query Post($filter: _PostFilter, $first: Int, $offset: Int) {
Post(filter: $filter, first: $first, offset: $offset) {
id
title
contentExcerpt
@ -146,7 +150,7 @@ export default {
},
variables() {
return {
filterBubble: this.filterBubble,
filter: this.filter,
first: this.pageSize,
offset: 0,
}

View File

@ -69,11 +69,13 @@
</ds-card>
<ds-space />
<ds-heading tag="h3" soft style="text-align: center; margin-bottom: 10px;">
Netzwerk
{{ $t('profile.network.title') }}
</ds-heading>
<ds-card style="position: relative; height: auto;">
<ds-space v-if="user.following && user.following.length" margin="x-small">
<ds-text tag="h5" color="soft">Wem folgt {{ userName | truncate(15) }}?</ds-text>
<ds-text tag="h5" color="soft">
{{ userName | truncate(15) }} {{ $t('profile.network.following') }}
</ds-text>
</ds-space>
<template v-if="user.following && user.following.length">
<ds-space v-for="follow in uniq(user.following)" :key="follow.id" margin="x-small">
@ -84,18 +86,23 @@
</ds-space>
<ds-space v-if="user.followingCount - user.following.length" margin="small">
<ds-text size="small" color="softer">
und {{ user.followingCount - user.following.length }} weitere
{{ $t('profile.network.and') }} {{ user.followingCount - user.following.length }}
{{ $t('profile.network.more') }}
</ds-text>
</ds-space>
</template>
<template v-else>
<p style="text-align: center; opacity: .5;">{{ userName }} folgt niemandem</p>
<p style="text-align: center; opacity: .5;">
{{ userName }} {{ $t('profile.network.followingNobody') }}
</p>
</template>
</ds-card>
<ds-space />
<ds-card style="position: relative; height: auto;">
<ds-space v-if="user.followedBy && user.followedBy.length" margin="x-small">
<ds-text tag="h5" color="soft">Wer folgt {{ userName | truncate(15) }}?</ds-text>
<ds-text tag="h5" color="soft">
{{ userName | truncate(15) }} {{ $t('profile.network.followedBy') }}
</ds-text>
</ds-space>
<template v-if="user.followedBy && user.followedBy.length">
<ds-space v-for="follow in uniq(user.followedBy)" :key="follow.id" margin="x-small">
@ -106,12 +113,15 @@
</ds-space>
<ds-space v-if="user.followedByCount - user.followedBy.length" margin="small">
<ds-text size="small" color="softer">
und {{ user.followedByCount - user.followedBy.length }} weitere
{{ $t('profile.network.and') }} {{ user.followedByCount - user.followedBy.length }}
{{ $t('profile.network.more') }}
</ds-text>
</ds-space>
</template>
<template v-else>
<p style="text-align: center; opacity: .5;">niemand folgt {{ userName }}</p>
<p style="text-align: center; opacity: .5;">
{{ userName }} {{ $t('profile.network.followedByNobody') }}
</p>
</template>
</ds-card>
<ds-space v-if="user.socialMedia && user.socialMedia.length" margin="large">
@ -132,69 +142,50 @@
</ds-card>
</ds-space>
</ds-flex-item>
<ds-flex-item :width="{ base: '100%', sm: 3, md: 5, lg: 3 }">
<ds-flex :width="{ base: '100%' }" gutter="small">
<ds-flex class="user-profile-posts-list" :width="{ base: '100%' }" gutter="small">
<ds-flex-item class="profile-top-navigation">
<ds-card class="ds-tab-nav">
<ds-flex>
<ds-flex-item
v-tooltip="{
content: $t('common.your.post', null, user.contributionsCount),
placement: 'right',
delay: { show: 500 },
}"
class="ds-tab-nav-item pointer ds-tab-nav-item-active"
@click="tabActivity('posts', $event)"
>
<ds-space margin="small">
<!-- TODO: find better solution for rendering errors -->
<no-ssr>
<ds-number :label="$t('common.post', null, user.contributionsCount)">
<hc-count-to slot="count" :end-val="user.contributionsCount" />
</ds-number>
</no-ssr>
</ds-space>
</ds-flex-item>
<ds-flex-item
v-tooltip="{
content: $t('common.your.comment', null, user.commentsCount),
placement: 'right',
delay: { show: 500 },
}"
class="ds-tab-nav-item pointer"
@click="tabActivity('commented', $event)"
>
<ds-space margin="small">
<!-- TODO: find better solution for rendering errors -->
<no-ssr>
<ds-number :label="$t('profile.commented')">
<hc-count-to slot="count" :end-val="user.commentsCount" />
</ds-number>
</no-ssr>
</ds-space>
</ds-flex-item>
<ds-flex-item
v-tooltip="{
content: $t('common.your.shouted', null, user.shoutedCount),
placement: 'right',
delay: { show: 500 },
}"
class="ds-tab-nav-item pointer"
>
<ds-space margin="small">
<!-- TODO: find better solution for rendering errors -->
<no-ssr>
<ds-number :label="$t('profile.shouted')">
<hc-count-to slot="count" :end-val="user.shoutedCount" />
</ds-number>
</no-ssr>
</ds-space>
</ds-flex-item>
</ds-flex>
<ul class="Tabs">
<li class="Tabs__tab Tab pointer" :class="{ active: tabActive === 'post' }">
<a @click="handleTab('post')">
<ds-space margin="small">
<no-ssr placeholder="Loading...">
<ds-number :label="$t('common.post', null, user.contributionsCount)">
<hc-count-to slot="count" :end-val="user.contributionsCount" />
</ds-number>
</no-ssr>
</ds-space>
</a>
</li>
<li class="Tabs__tab Tab pointer" :class="{ active: tabActive === 'comment' }">
<a @click="handleTab('comment')">
<ds-space margin="small">
<no-ssr placeholder="Loading...">
<ds-number :label="$t('profile.commented')">
<hc-count-to slot="count" :end-val="user.commentedCount" />
</ds-number>
</no-ssr>
</ds-space>
</a>
</li>
<li class="Tabs__tab Tab pointer" :class="{ active: tabActive === 'shout' }">
<a @click="handleTab('shout')">
<ds-space margin="small">
<no-ssr placeholder="Loading...">
<ds-number :label="$t('profile.shouted')">
<hc-count-to slot="count" :end-val="user.shoutedCount" />
</ds-number>
</no-ssr>
</ds-space>
</a>
</li>
<li class="Tabs__presentation-slider" role="presentation"></li>
</ul>
</ds-card>
</ds-flex-item>
<ds-flex-item style="text-align: center">
<ds-button
v-if="myProfile"
@ -206,6 +197,7 @@
primary
/>
</ds-flex-item>
<template v-if="activePosts.length">
<hc-post-card
v-for="(post, index) in activePosts"
@ -215,6 +207,13 @@
@removePostFromList="user.contributions.splice(index, 1)"
/>
</template>
<template v-else-if="$apollo.loading">
<ds-flex-item>
<ds-section centered>
<ds-spinner size="base"></ds-spinner>
</ds-section>
</ds-flex-item>
</template>
<template v-else>
<ds-flex-item :width="{ base: '100%' }">
<hc-empty margin="xx-large" icon="file" />
@ -241,6 +240,17 @@ import ContentMenu from '~/components/ContentMenu'
import HcUpload from '~/components/Upload'
import HcAvatar from '~/components/Avatar/Avatar.vue'
import PostQuery from '~/graphql/UserProfile/Post.js'
import UserQuery from '~/graphql/UserProfile/User.js'
const tabToFilterMapping = ({ tab, id }) => {
return {
post: { author: { id } },
comment: { comments_some: { author: { id } } },
shout: { shoutedBy_some: { id } },
}[tab]
}
export default {
components: {
User,
@ -259,16 +269,28 @@ export default {
mode: 'out-in',
},
data() {
const filter = tabToFilterMapping({ tab: 'post', id: this.$route.params.id })
return {
User: [],
Post: [],
voted: false,
page: 1,
pageSize: 6,
tabActive: 'post',
filter,
}
},
computed: {
hasMore() {
const total = {
post: this.user.contributionsCount,
shout: this.user.shoutedCount,
comment: this.user.commentedCount,
}[this.tabActive]
return this.Post && this.Post.length < total
},
myProfile() {
return this.$route.params.slug === this.$store.getters['auth/user'].slug
return this.$route.params.id === this.$store.getters['auth/user'].id
},
followedByCount() {
let count = Number(this.user.followedByCount) || 0
@ -280,16 +302,11 @@ export default {
offset() {
return (this.page - 1) * this.pageSize
},
hasMore() {
return (
this.user.contributions && this.user.contributions.length < this.user.contributionsCount
)
},
activePosts() {
if (!this.user.contributions) {
if (!this.Post) {
return []
}
return this.uniq(this.user.contributions.filter(post => !post.deleted))
return this.uniq(this.Post.filter(post => !post.deleted))
},
socialMediaLinks() {
const { socialMedia = [] } = this.user
@ -315,6 +332,11 @@ export default {
},
},
methods: {
handleTab(tab) {
this.tabActive = tab
this.Post = null
this.filter = tabToFilterMapping({ tab, id: this.$route.params.id })
},
uniq(items, field = 'id') {
return uniqBy(items, field)
},
@ -326,19 +348,16 @@ export default {
// this.page++
// Fetch more data and transform the original result
this.page++
this.$apollo.queries.User.fetchMore({
this.$apollo.queries.Post.fetchMore({
variables: {
slug: this.$route.params.slug,
filter: this.filter,
first: this.pageSize,
offset: this.offset,
},
// Transform the previous result with new data
updateQuery: (previousResult, { fetchMoreResult }) => {
let output = { User: this.User }
output.User[0].contributions = [
...previousResult.User[0].contributions,
...fetchMoreResult.User[0].contributions,
]
let output = { Post: this.Post }
output.Post = [...previousResult.Post, ...fetchMoreResult.Post]
return output
},
fetchPolicy: 'cache-and-network',
@ -346,19 +365,28 @@ export default {
},
},
apollo: {
User: {
Post: {
query() {
return require('~/graphql/UserProfileQuery.js').default(this)
return PostQuery(this.$i18n)
},
variables() {
return {
slug: this.$route.params.slug,
filter: this.filter,
first: this.pageSize,
offset: 0,
}
},
fetchPolicy: 'cache-and-network',
},
User: {
query() {
return UserQuery(this.$i18n)
},
variables() {
return { id: this.$route.params.id }
},
fetchPolicy: 'cache-and-network',
},
},
}
</script>
@ -368,8 +396,48 @@ export default {
cursor: pointer;
}
.ds-tab-nav .ds-card-content .ds-tab-nav-item:hover {
border-bottom: 3px solid #c9c6ce;
.Tab {
border-collapse: collapse;
padding-bottom: 5px;
}
.Tab:hover {
border-bottom: 2px solid #c9c6ce;
}
.Tabs {
position: relative;
background-color: #fff;
&:after {
content: ' ';
display: table;
clear: both;
}
margin: 0;
padding: 0;
list-style: none;
&__tab {
float: left;
width: 33.333%;
text-align: center;
&:first-child.active ~ .Tabs__presentation-slider {
left: 0;
}
&:nth-child(2).active ~ .Tabs__presentation-slider {
left: 33.333%;
}
&:nth-child(3).active ~ .Tabs__presentation-slider {
left: calc(33.333% * 2);
}
}
&__presentation-slider {
position: absolute;
bottom: 0;
left: 0;
width: 33.333%;
height: 2px;
background-color: #17b53f;
transition: left 0.25s;
}
}
.profile-avatar.ds-avatar {

View File

@ -1141,10 +1141,10 @@
vue-cli-plugin-apollo "^0.20.0"
webpack-node-externals "^1.7.2"
"@nuxtjs/axios@~5.5.3":
version "5.5.3"
resolved "https://registry.yarnpkg.com/@nuxtjs/axios/-/axios-5.5.3.tgz#1893609fc4a3e845516a45c1aaf428104cb3d524"
integrity sha512-WEgWcmgJqWLHWw8ZTieQvuV+I6vcaVgdnCmtzrsKliPsCTtbhDu1i7iFkM/dYAqppIxpL2v46lH6sQ4FfOOxTA==
"@nuxtjs/axios@~5.5.4":
version "5.5.4"
resolved "https://registry.yarnpkg.com/@nuxtjs/axios/-/axios-5.5.4.tgz#c4aee2322901b19d4072670c03144662a73ea6f4"
integrity sha512-/Ljsyh5VIc9paXGrQue7RQ+PpBNES1oit0g4l+ya1tfyKnZMpHSbghuLcv0xq+BpXlSEr690uemHbz54/N6U5w==
dependencies:
"@nuxtjs/proxy" "^1.3.3"
axios "^0.19.0"
@ -1684,6 +1684,13 @@
"@webassemblyjs/wast-parser" "1.8.5"
"@xtuc/long" "4.2.2"
"@wry/equality@^0.1.2":
version "0.1.7"
resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.1.7.tgz#512234d078341c32cabda66b89b5dddb5741d9b9"
integrity sha512-p1rhJ6PQzpsBr9cMJMHvvx3LQEA28HFX7fAQx6khAX+1lufFeBuk+iRCAyHwj3v6JbpGKvHNa66f+9cpU8c7ew==
dependencies:
tslib "^1.9.3"
"@xtuc/ieee754@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
@ -1878,23 +1885,23 @@ apollo-cache-inmemory@^1.5.1, apollo-cache-inmemory@~1.5.1:
ts-invariant "^0.2.1"
tslib "^1.9.3"
apollo-cache@1.3.1, apollo-cache@^1.2.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/apollo-cache/-/apollo-cache-1.3.1.tgz#c015f93a9a7f32b3eeea0c471addd6e854da754c"
integrity sha512-BJ/Mehr3u6XCaHYSmgZ6DM71Fh30OkW6aEr828WjHvs+7i0RUuP51/PM7K6T0jPXtuw7UbArFFPZZsNgXnyyJA==
apollo-cache@1.3.2, apollo-cache@^1.2.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/apollo-cache/-/apollo-cache-1.3.2.tgz#df4dce56240d6c95c613510d7e409f7214e6d26a"
integrity sha512-+KA685AV5ETEJfjZuviRTEImGA11uNBp/MJGnaCvkgr+BYRrGLruVKBv6WvyFod27WEB2sp7SsG8cNBKANhGLg==
dependencies:
apollo-utilities "^1.3.1"
apollo-utilities "^1.3.2"
tslib "^1.9.3"
apollo-client@^2.5.1, apollo-client@~2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-2.6.1.tgz#fcf328618d6ad82b750a988bec113fe6edc8ba94"
integrity sha512-Tb6ZthPZUHlGqeoH1WC8Qg/tLnkk9H5+xj4e5nzOAC6dCOW3pVU9tYXscrWdmZ65UDUg1khvTNjrQgPhdf4aTQ==
apollo-client@^2.5.1, apollo-client@~2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-2.6.2.tgz#03b6af651e09b6e413e486ddc87464c85bd6e514"
integrity sha512-oks1MaT5x7gHcPeC8vPC1UzzsKaEIC0tye+jg72eMDt5OKc7BobStTeS/o2Ib3e0ii40nKxGBnMdl/Xa/p56Yg==
dependencies:
"@types/zen-observable" "^0.8.0"
apollo-cache "1.3.1"
apollo-cache "1.3.2"
apollo-link "^1.0.0"
apollo-utilities "1.3.1"
apollo-utilities "1.3.2"
symbol-observable "^1.0.2"
ts-invariant "^0.4.0"
tslib "^1.9.3"
@ -2081,13 +2088,13 @@ apollo-upload-client@^10.0.0:
apollo-link-http-common "^0.2.13"
extract-files "^5.0.1"
apollo-utilities@1.3.1, apollo-utilities@^1.0.1, apollo-utilities@^1.0.8, apollo-utilities@^1.2.1, apollo-utilities@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.1.tgz#4c45f9b52783c324e2beef822700bdea374f82d1"
integrity sha512-P5cJ75rvhm9hcx9V/xCW0vlHhRd0S2icEcYPoRYNTc5djbynpuO+mQuJ4zMHgjNDpvvDxDfZxXTJ6ZUuJZodiQ==
apollo-utilities@1.3.2, apollo-utilities@^1.0.1, apollo-utilities@^1.0.8, apollo-utilities@^1.2.1, apollo-utilities@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.2.tgz#8cbdcf8b012f664cd6cb5767f6130f5aed9115c9"
integrity sha512-JWNHj8XChz7S4OZghV6yc9FNnzEXj285QYp/nLNh943iObycI5GTDO3NGR9Dth12LRrSFMeDOConPfPln+WGfg==
dependencies:
"@wry/equality" "^0.1.2"
fast-json-stable-stringify "^2.0.0"
lodash.isequal "^4.5.0"
ts-invariant "^0.4.0"
tslib "^1.9.3"
@ -3298,18 +3305,18 @@ cookie-signature@1.0.6:
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
cookie-universal-nuxt@~2.0.14:
version "2.0.14"
resolved "https://registry.yarnpkg.com/cookie-universal-nuxt/-/cookie-universal-nuxt-2.0.14.tgz#6fdf8e928eadd7611c04a57614fe2e29b60eb971"
integrity sha512-ih9Z0Z2K6eLaugTttGCVN85nogKseIFF/dqup3klvYC4mQS3+1IloqBqzTL/N7degBBAols2oppwYNDmaRtVig==
cookie-universal-nuxt@~2.0.16:
version "2.0.16"
resolved "https://registry.yarnpkg.com/cookie-universal-nuxt/-/cookie-universal-nuxt-2.0.16.tgz#8d528098c973162b352199240e40da0e5429b13f"
integrity sha512-wRK2zw8w+a5xPehb5kLbgOic/4mbjl2exUCxWZwGuttcwsFgOymiwDrCOzmQslqrDevPDL2SsBbH6wtOm7dB9g==
dependencies:
"@types/cookie" "^0.3.1"
cookie-universal "^2.0.14"
cookie-universal "^2.0.16"
cookie-universal@^2.0.14:
version "2.0.14"
resolved "https://registry.yarnpkg.com/cookie-universal/-/cookie-universal-2.0.14.tgz#1b4f27cffccfc2e47703fa235c1f67f931213041"
integrity sha512-m6J0DQa4/RQvXhzUG37EY1ynK3Uq1BKzp5hotST9olrzjrRx+B0vNPx7azg0/X0XrYQvL7MMbPXwou8m0BNDwg==
cookie-universal@^2.0.16:
version "2.0.16"
resolved "https://registry.yarnpkg.com/cookie-universal/-/cookie-universal-2.0.16.tgz#ec8b55789b502a377ef02ad230923c1dfa5c1061"
integrity sha512-EHtQ5Tg3UoUHG7LmeV3rlV3iYthkhUuYZ0y86EseypxGcUuvzxuHExEb6mHKDhDPrIrdewAHdG/aCHuG/T4zEg==
dependencies:
"@types/cookie" "^0.3.1"
cookie "^0.3.1"
@ -3747,10 +3754,10 @@ data-urls@^1.0.0:
whatwg-mimetype "^2.2.0"
whatwg-url "^7.0.0"
date-fns@2.0.0-alpha.29:
version "2.0.0-alpha.29"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.29.tgz#9d4a36e3ebba63d009e957fea8fdfef7921bc6cb"
integrity sha512-AIFZ0hG/1fdb7HZHTDyiEJdNiaFyZxXcx/kF8z3I9wxbhkN678KrrLSneKcsb0Xy5KqCA4wCIxmGpdVWSNZnpA==
date-fns@2.0.0-alpha.31:
version "2.0.0-alpha.31"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.31.tgz#51bcfdca25dfc9bea334a556ab33dfc0bb00421c"
integrity sha512-S19PwMqnbYsqcbCg02Yj9gv4veVNZ0OX7v2+zcd+Mq0RI7LoDKJipJjnMrTZ3Cc6blDuTce5G/pHXcVIGRwJWQ==
date-now@^0.1.4:
version "0.1.4"
@ -6851,11 +6858,6 @@ lodash.isboolean@^3.0.3:
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=
lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
lodash.isinteger@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
@ -8857,15 +8859,10 @@ prettier@1.16.3:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.3.tgz#8c62168453badef702f34b45b6ee899574a6a65d"
integrity sha512-kn/GU6SMRYPxUakNXhpP0EedT/KmaPzr0H5lIsDogrykbaxOpOfAFfk5XA7DZrJyMAv1wlMV3CPcZruGXVVUZw==
prettier@^1.15.2:
version "1.17.0"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.17.0.tgz#53b303676eed22cc14a9f0cec09b477b3026c008"
integrity sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw==
prettier@~1.17.1:
version "1.17.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.17.1.tgz#ed64b4e93e370cb8a25b9ef7fef3e4fd1c0995db"
integrity sha512-TzGRNvuUSmPgwivDqkZ9tM/qTGW9hqDKWOE9YHiyQdixlKbv7kvEqsmDPrcHJTKwthU774TQwZXVtaQ/mMsvjg==
prettier@^1.15.2, prettier@~1.18.2:
version "1.18.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea"
integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==
pretty-bytes@^5.2.0:
version "5.2.0"
@ -10561,10 +10558,10 @@ timsort@^0.3.0:
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
tippy.js@^4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-4.3.2.tgz#f785d96fd03d890aa118646e1a873f851bd1c8b4"
integrity sha512-vSdVU8zkhsdCFegwtKq7WJfF29xo4Qiq5GWPZEjKbW4knogI43HJHPAOCUkxbi28gKTTgiWF+GveZgTqhS9QOw==
tippy.js@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-4.3.4.tgz#9a91fd5ce8c401f181b7adaa6b2c27f3d105f3ba"
integrity sha512-O2ukxHOJTLVYZ/TfHjNd8WgAWoefX9uk5QiWRdHfX2PR2lBpUU4BJQLl7U2Ykc8K7o16gTeHEElpuRfgD5b0aA==
dependencies:
popper.js "^1.14.7"
@ -10582,10 +10579,10 @@ tiptap-commands@^1.10.5, tiptap-commands@^1.10.7:
prosemirror-utils "^0.9.0"
tiptap-utils "^1.5.5"
tiptap-extensions@1.21.0:
version "1.21.0"
resolved "https://registry.yarnpkg.com/tiptap-extensions/-/tiptap-extensions-1.21.0.tgz#c2f228144b6943755d2de4617e11febe08c78a42"
integrity sha512-dIm9Q/G1qL1+sEqQiPXElUzSBcRhXXPuyOdtHgMrncUaCbnaDxsHdkJl700OXfA/GCu7AlhUpd3R67Rmb+voCQ==
tiptap-extensions@1.22.2:
version "1.22.2"
resolved "https://registry.yarnpkg.com/tiptap-extensions/-/tiptap-extensions-1.22.2.tgz#66b24ef63653481da16419efdf6459c7a9f518f7"
integrity sha512-JJe2yAnbaKBWO/16+lfFphePphG9UYhp3wAUkzhSefJx3dtIdDfCmp5jBzZ6NLahUHNT/Z9WeYdhQmngAeVhoA==
dependencies:
lowlight "^1.12.1"
prosemirror-collab "^1.1.2"
@ -10596,7 +10593,7 @@ tiptap-extensions@1.21.0:
prosemirror-transform "^1.1.3"
prosemirror-utils "^0.9.0"
prosemirror-view "^1.9.8"
tiptap "^1.21.1"
tiptap "^1.22.2"
tiptap-commands "^1.10.7"
tiptap-utils@^1.5.3:
@ -10635,10 +10632,10 @@ tiptap@1.20.1:
tiptap-commands "^1.10.5"
tiptap-utils "^1.5.3"
tiptap@^1.21.1:
version "1.21.1"
resolved "https://registry.yarnpkg.com/tiptap/-/tiptap-1.21.1.tgz#c0340375795088b899541b64ce86ae45e98d0369"
integrity sha512-vCKT/UGorAx1SSX5+9vmtZa+WC+LKfJIArgkEJFXmxfZeyBhNXRSwR+rR+UtdPYi8V4CtmIwtv6eRPP+bH6SWA==
tiptap@^1.22.2:
version "1.22.2"
resolved "https://registry.yarnpkg.com/tiptap/-/tiptap-1.22.2.tgz#f3f2b822d9ed087a853520c86593b2e6bd822bdd"
integrity sha512-qMFQJ358Ga8gXzUAMaHGrZDab/IqZf28N8BYYo4hyFIMoEYQVWmBGWuSBHOgpxiueXpC9zMuCoIgrNWN3VpoNQ==
dependencies:
prosemirror-commands "^1.0.8"
prosemirror-dropcursor "^1.1.1"
@ -11318,13 +11315,6 @@ vue2-dropzone@3.5.8:
dependencies:
dropzone "^5.5.1"
vue2-dropzone@^3.5.9:
version "3.5.9"
resolved "https://registry.yarnpkg.com/vue2-dropzone/-/vue2-dropzone-3.5.9.tgz#a63999a45a7aad24d4c21e3d35be409b4e6bdce8"
integrity sha512-nJz6teulVKlZIAeKgvPU7wBI/gzfIgqDOrEp1okSkQIkdprDVCoM0U7XWM0NOM4AAVX+4XGRtMoocYWdTYb3bQ==
dependencies:
dropzone "^5.5.1"
vue@^2.6.10, vue@^2.6.6:
version "2.6.10"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.10.tgz#a72b1a42a4d82a721ea438d1b6bf55e66195c637"