Merge branch 'master' of github.com:Human-Connection/Human-Connection into 404-delete-user-account-and-data

This commit is contained in:
Matt Rider 2019-06-06 14:15:02 -03:00
commit ffa15bf728
37 changed files with 956 additions and 441 deletions

View File

@ -17,7 +17,8 @@ before_install:
install:
- docker-compose -f docker-compose.yml -f docker-compose.travis.yml up --build -d
- wait-on http://localhost:7474 && docker-compose exec neo4j migrate
# avoid "Database constraints have changed after this transaction started"
- wait-on http://localhost:7474
script:
# Backend

View File

@ -3,6 +3,7 @@
* [Introduction](README.md)
* [Edit this Documentation](edit-this-documentation.md)
* [Installation](installation.md)
* [Neo4J](neo4j/README.md)
* [Backend](backend/README.md)
* [GraphQL](backend/graphql.md)
* [Webapp](webapp/README.md)

View File

@ -1,4 +1,4 @@
FROM node:12.3.1-alpine as base
FROM node:12.4-alpine as base
LABEL Description="Backend of the Social Network Human-Connection.org" Vendor="Human Connection gGmbH" Version="0.0.1" Maintainer="Human Connection gGmbH (developer@human-connection.org)"
EXPOSE 4000

View File

@ -1,8 +1,6 @@
# Backend
## Installation
{% tabs %}
{% tab title="Docker" %}
## Installation with Docker
Run the following command to install everything through docker.
@ -14,28 +12,15 @@ $ docker-compose up
# rebuild the containers for a cleanup
$ docker-compose up --build
```
Open another terminal and create unique indices with:
```bash
$ docker-compose exec neo4j migrate
```
Wait a little until your backend is up and running at [http://localhost:4000/](http://localhost:4000/).
{% endtab %}
{% tab title="Without Docker" %}
## Installation without Docker
For the local installation you need a recent version of [node](https://nodejs.org/en/)
(>= `v10.12.0`) and [Neo4J](https://neo4j.com/) along with
[Apoc](https://github.com/neo4j-contrib/neo4j-apoc-procedures) plugin installed
on your system.
(>= `v10.12.0`).
Download [Neo4j Community Edition](https://neo4j.com/download-center/#releases) and unpack the files.
Download [Neo4j Apoc](https://github.com/neo4j-contrib/neo4j-apoc-procedures/releases) and drop the file into the `plugins` folder of the just extracted Neo4j-Server
Note that grand-stack-starter does not currently bundle a distribution of Neo4j. You can download [Neo4j Desktop](https://neo4j.com/download/) and run locally for development, spin up a [hosted Neo4j Sandbox instance](https://neo4j.com/download/), run Neo4j in one of the [many cloud options](https://neo4j.com/developer/guide-cloud-deployment/), [spin up Neo4j in a Docker container](https://neo4j.com/developer/docker/) or on Debian-based systems install [Neo4j from the Debian Repository](http://debian.neo4j.org/). Just be sure to update the Neo4j connection string and credentials accordingly in `.env`.
Start Neo4J and confirm the database is running at [http://localhost:7474](http://localhost:7474).
Now install node dependencies with [yarn](https://yarnpkg.com/en/):
Install node dependencies with [yarn](https://yarnpkg.com/en/):
```bash
$ cd backend
$ yarn install
@ -46,14 +31,8 @@ Copy Environment Variables:
# in backend/
$ cp .env.template .env
```
Configure the new files according to your needs and your local setup.
Create unique indices with:
```bash
$ ./neo4j/migrate.sh
```
Configure the new file according to your needs and your local setup. Make sure
a [local Neo4J](http://localhost:7474) instance is up and running.
Start the backend for development with:
```bash
@ -65,17 +44,12 @@ or start the backend in production environment with:
yarn run start
```
{% endtab %}
{% endtabs %}
Your backend is up and running at [http://localhost:4000/](http://localhost:4000/)
This will start the GraphQL service \(by default on localhost:4000\) where you can issue GraphQL requests or access GraphQL Playground in the browser.
This will start the GraphQL service \(by default on localhost:4000\) where you
can issue GraphQL requests or access GraphQL Playground in the browser.
![GraphQL Playground](../.gitbook/assets/graphql-playground.png)
You can access Neo4J through [http://localhost:7474/](http://localhost:7474/)
for an interactive `cypher` shell and a visualization of the graph.
#### Seed Database
@ -114,7 +88,8 @@ $ yarn run db:reset
# Testing
**Beware**: We have no multiple database setup at the moment. We clean the database after each test, running the tests will wipe out all your data!
**Beware**: We have no multiple database setup at the moment. We clean the
database after each test, running the tests will wipe out all your data!
{% tabs %}

View File

@ -11,7 +11,7 @@
"lint": "eslint src --config .eslintrc.js",
"test": "run-s test:jest test:cucumber",
"test:before:server": "cross-env GRAPHQL_URI=http://localhost:4123 GRAPHQL_PORT=4123 yarn run dev 2> /dev/null",
"test:before:seeder": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 DEBUG=true DISABLED_MIDDLEWARES=permissions,activityPub yarn run dev 2> /dev/null",
"test:before:seeder": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 DISABLED_MIDDLEWARES=permissions,activityPub yarn run dev 2> /dev/null",
"test:jest:cmd": "wait-on tcp:4001 tcp:4123 && jest --forceExit --detectOpenHandles --runInBand",
"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",
@ -19,8 +19,8 @@
"test:cucumber": " cross-env CLIENT_URI=http://localhost:4123 run-p --race test:before:* 'test:cucumber:cmd {@}' --",
"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:reset": "cross-env DEBUG=true babel-node src/seed/reset-db.js",
"db:seed": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 DEBUG=true DISABLED_MIDDLEWARES=permissions run-p --race dev db:script:seed"
"db:reset": "cross-env babel-node src/seed/reset-db.js",
"db:seed": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 DISABLED_MIDDLEWARES=permissions run-p --race dev db:script:seed"
},
"author": "Human Connection gGmbH",
"license": "MIT",
@ -43,8 +43,8 @@
},
"dependencies": {
"activitystrea.ms": "~2.1.3",
"apollo-cache-inmemory": "~1.6.0",
"apollo-client": "~2.5.1",
"apollo-cache-inmemory": "~1.6.1",
"apollo-client": "~2.6.1",
"apollo-link-context": "~1.0.14",
"apollo-link-http": "~1.5.14",
"apollo-server": "~2.6.1",
@ -52,7 +52,7 @@
"cheerio": "~1.0.0-rc.3",
"cors": "~2.8.5",
"cross-env": "~5.2.0",
"date-fns": "2.0.0-alpha.27",
"date-fns": "2.0.0-alpha.29",
"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.5",
"graphql-shield": "~5.3.6",
"graphql-tag": "~2.10.1",
"graphql-yoga": "~1.17.4",
"helmet": "~3.18.0",
@ -109,4 +109,4 @@
"prettier": "~1.17.1",
"supertest": "~4.0.2"
}
}
}

View File

@ -23,7 +23,8 @@ export const serverConfigs = {
export const developmentConfigs = {
DEBUG: process.env.NODE_ENV !== 'production' && process.env.DEBUG === 'true',
MOCKS: process.env.MOCKS === 'true',
DISABLED_MIDDLEWARES: process.env.DISABLED_MIDDLEWARES || '',
DISABLED_MIDDLEWARES:
(process.env.NODE_ENV !== 'production' && process.env.DISABLED_MIDDLEWARES) || '',
}
export default {

View File

@ -0,0 +1,12 @@
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

@ -0,0 +1,76 @@
import { GraphQLClient } from 'graphql-request'
import { host, login } from '../../jest/helpers'
import Factory from '../../seed/factories'
const factory = Factory()
const currentUserParams = {
email: 'you@example.org',
name: 'This is you',
password: '1234',
}
const followedAuthorParams = {
id: 'u2',
email: 'followed@example.org',
name: 'Followed User',
password: '1234',
}
const randomAuthorParams = {
email: 'someone@example.org',
name: 'Someone else',
password: 'else',
}
beforeEach(async () => {
await Promise.all([
factory.create('User', currentUserParams),
factory.create('User', followedAuthorParams),
factory.create('User', randomAuthorParams),
])
const [asYourself, asFollowedUser, asSomeoneElse] = await Promise.all([
Factory().authenticateAs(currentUserParams),
Factory().authenticateAs(followedAuthorParams),
Factory().authenticateAs(randomAuthorParams),
])
await asYourself.follow({ id: 'u2', type: 'User' })
await asFollowedUser.create('Post', { title: 'This is the post of a followed user' })
await asSomeoneElse.create('Post', { title: 'This is some random post' })
})
afterEach(async () => {
await factory.cleanDatabase()
})
describe('FilterBubble middleware', () => {
describe('given an authenticated user', () => {
let authenticatedClient
beforeEach(async () => {
const headers = await login(currentUserParams)
authenticatedClient = new GraphQLClient(host, { headers })
})
describe('no filter bubble', () => {
it('returns all posts', async () => {
const query = '{ Post( filterBubble: {}) { title } }'
const expected = {
Post: [
{ title: 'This is some random post' },
{ title: 'This is the post of a followed user' },
],
}
await expect(authenticatedClient.request(query)).resolves.toEqual(expected)
})
})
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 expected = {
Post: [{ title: 'This is the post of a followed user' }],
}
await expect(authenticatedClient.request(query)).resolves.toEqual(expected)
})
})
})
})

View File

@ -0,0 +1,31 @@
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

@ -0,0 +1,129 @@
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,6 +13,7 @@ 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 = {
@ -30,11 +31,13 @@ export default schema => {
user: user,
includedFields: includedFields,
orderBy: orderBy,
filterBubble: filterBubble,
}
let order = [
'permissions',
'activityPub',
'filterBubble',
'password',
'dateTime',
'validation',
@ -50,7 +53,7 @@ export default schema => {
]
// add permisions middleware at the first position (unless we're seeding)
if (CONFIG.DEBUG) {
if (CONFIG.DISABLED_MIDDLEWARES) {
const disabledMiddlewares = CONFIG.DISABLED_MIDDLEWARES.split(',')
order = order.filter(key => {
return !disabledMiddlewares.includes(key)

View File

@ -1,3 +1,40 @@
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
@ -40,4 +77,4 @@ type Post {
RETURN COUNT(u) >= 1
"""
)
}
}

View File

@ -1,8 +1,7 @@
import { cleanDatabase } from './factories'
import CONFIG from './../config'
if (!CONFIG.DEBUG) {
throw new Error(`YOU CAN'T CLEAN THE DATABASE WITH DEBUG=${CONFIG.DEBUG}`)
if (process.env.NODE_ENV === 'production') {
throw new Error(`You cannot clean the database in production environment!`)
}
;(async function() {

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.13":
version "0.26.13"
resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.26.13.tgz#0aeeba85231a34ddc68c74b3a2c64eeb2ccf68bf"
integrity sha512-sMMtb+c2xxf/FcK0kW36+0uuSWpNwvCBZYI7vpnD9J9Z6OYk09P4TmDkMWV+NWdi9Nzt2tUJjtpnPpkiUklBaw==
"@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/zen-observable@^0.5.3":
version "0.5.4"
@ -1296,45 +1296,36 @@ apollo-cache-control@^0.1.0:
dependencies:
graphql-extensions "^0.0.x"
apollo-cache-inmemory@~1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.0.tgz#a106cdc520f0a043be2575372d5dbb7e4790254c"
integrity sha512-Mr86ucMsXnRH9YRvcuuy6kc3dtyRBuVSo8gdxp2sJVuUAtvQ6r/8E+ok2qX84em9ZBAYxoyvPnKeShhvcKiiDw==
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==
dependencies:
apollo-cache "^1.3.0"
apollo-utilities "^1.3.0"
apollo-cache "^1.3.1"
apollo-utilities "^1.3.1"
optimism "^0.9.0"
ts-invariant "^0.4.0"
tslib "^1.9.3"
apollo-cache@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/apollo-cache/-/apollo-cache-1.2.1.tgz#aae71eb4a11f1f7322adc343f84b1a39b0693644"
integrity sha512-nzFmep/oKlbzUuDyz6fS6aYhRmfpcHWqNkkA9Bbxwk18RD6LXC4eZkuE0gXRX0IibVBHNjYVK+Szi0Yied4SpQ==
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==
dependencies:
apollo-utilities "^1.2.1"
apollo-utilities "^1.3.1"
tslib "^1.9.3"
apollo-cache@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/apollo-cache/-/apollo-cache-1.3.0.tgz#de5c907cbd329440c9b0aafcbe8436391b9e6142"
integrity sha512-voPlvSIDA2pY3+7QwtXPs7o5uSNAVjUKwimyHWoiW0MIZtPxawtOV/Y+BL85R227JqcjPic1El+QToVR8l4ytQ==
dependencies:
apollo-utilities "^1.3.0"
tslib "^1.9.3"
apollo-client@~2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-2.5.1.tgz#36126ed1d32edd79c3713c6684546a3bea80e6d1"
integrity sha512-MNcQKiqLHdGmNJ0rZ0NXaHrToXapJgS/5kPk0FygXt+/FmDCdzqcujI7OPxEC6e9Yw5S/8dIvOXcRNuOMElHkA==
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==
dependencies:
"@types/zen-observable" "^0.8.0"
apollo-cache "1.2.1"
apollo-cache "1.3.1"
apollo-link "^1.0.0"
apollo-link-dedup "^1.0.0"
apollo-utilities "1.2.1"
apollo-utilities "1.3.1"
symbol-observable "^1.0.2"
ts-invariant "^0.2.1"
ts-invariant "^0.4.0"
tslib "^1.9.3"
zen-observable "^0.8.0"
@ -1398,13 +1389,6 @@ apollo-link-context@~1.0.14:
apollo-link "^1.2.11"
tslib "^1.9.3"
apollo-link-dedup@^1.0.0:
version "1.0.11"
resolved "https://registry.yarnpkg.com/apollo-link-dedup/-/apollo-link-dedup-1.0.11.tgz#6f34ea748d2834850329ad03111ef18445232b05"
integrity sha512-RcvkXR0CNbQcsw6LdrPksGa+9YjZ1ghk0k2PKal6rSBCyyqzokcBawXOtoMN8q+0FLR1dGs5GnAQVeucQuY28g==
dependencies:
apollo-link "^1.2.4"
apollo-link-http-common@^0.2.13:
version "0.2.13"
resolved "https://registry.yarnpkg.com/apollo-link-http-common/-/apollo-link-http-common-0.2.13.tgz#c688f6baaffdc7b269b2db7ae89dae7c58b5b350"
@ -1423,7 +1407,7 @@ apollo-link-http@~1.5.14:
apollo-link-http-common "^0.2.13"
tslib "^1.9.3"
apollo-link@^1.0.0, apollo-link@^1.2.11, apollo-link@^1.2.3, apollo-link@^1.2.4:
apollo-link@^1.0.0, apollo-link@^1.2.11, apollo-link@^1.2.3:
version "1.2.11"
resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.11.tgz#493293b747ad3237114ccd22e9f559e5e24a194d"
integrity sha512-PQvRCg13VduLy3X/0L79M6uOpTh5iHdxnxYuo8yL7sJlWybKRJwsv4IcRBJpMFbChOOaHY7Og9wgPo6DLKDKDA==
@ -1575,21 +1559,13 @@ apollo-upload-server@^7.0.0:
http-errors "^1.7.0"
object-path "^0.11.4"
apollo-utilities@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.2.1.tgz#1c3a1ebf5607d7c8efe7636daaf58e7463b41b3c"
integrity sha512-Zv8Udp9XTSFiN8oyXOjf6PMHepD4yxxReLsl6dPUy5Ths7jti3nmlBzZUOxuTWRwZn0MoclqL7RQ5UEJN8MAxg==
dependencies:
fast-json-stable-stringify "^2.0.0"
ts-invariant "^0.2.1"
tslib "^1.9.3"
apollo-utilities@^1.0.1, apollo-utilities@^1.2.1, apollo-utilities@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.0.tgz#9803724c07ac94ca11dc26397edb58735d2b0211"
integrity sha512-wQjV+FdWcTWmWUFlChG5rS0vHKy5OsXC6XlV9STRstQq6VbXANwHy6DHnTEQAfLXWAbNcPgBu+nBUpR3dFhwrA==
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==
dependencies:
fast-json-stable-stringify "^2.0.0"
lodash.isequal "^4.5.0"
ts-invariant "^0.4.0"
tslib "^1.9.3"
@ -2603,10 +2579,10 @@ data-urls@^1.0.0:
whatwg-mimetype "^2.2.0"
whatwg-url "^7.0.0"
date-fns@2.0.0-alpha.27:
version "2.0.0-alpha.27"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.27.tgz#5ecd4204ef0e7064264039570f6e8afbc014481c"
integrity sha512-cqfVLS+346P/Mpj2RpDrBv0P4p2zZhWWvfY5fuWrXNR/K38HaAGEkeOwb47hIpQP9Jr/TIxjZ2/sNMQwdXuGMg==
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==
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"
@ -3789,12 +3765,12 @@ graphql-request@~1.8.2:
dependencies:
cross-fetch "2.2.2"
graphql-shield@~5.3.5:
version "5.3.5"
resolved "https://registry.yarnpkg.com/graphql-shield/-/graphql-shield-5.3.5.tgz#cba409f4c1714e107212cff0a1cb2d934273392b"
integrity sha512-3kmL9x+b85NK2ipH3VGudUgUo1vXy0Z44WXhnGi3b0T0peg53DOSlXBbZOO4PNh1AcULnUjYf+DpDrP8Uc97Gw==
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==
dependencies:
"@types/yup" "0.26.13"
"@types/yup" "0.26.14"
lightercollective "^0.3.0"
object-hash "^1.3.1"
yup "^0.27.0"
@ -5254,6 +5230,11 @@ 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"
@ -7560,13 +7541,6 @@ trunc-text@1.0.1:
resolved "https://registry.yarnpkg.com/trunc-text/-/trunc-text-1.0.1.tgz#58f876d8ac59b224b79834bb478b8656e69622b5"
integrity sha1-WPh22KxZsiS3mDS7R4uGVuaWIrU=
ts-invariant@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.2.1.tgz#3d587f9d6e3bded97bf9ec17951dd9814d5a9d3f"
integrity sha512-Z/JSxzVmhTo50I+LKagEISFJW3pvPCqsMWLamCTX8Kr3N5aMrnGOqcflbe5hLUzwjvgPfnLzQtHZv0yWQ+FIHg==
dependencies:
tslib "^1.9.3"
ts-invariant@^0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.3.2.tgz#89a2ffeb70879b777258df1df1c59383c35209b0"

View File

@ -1,25 +1,28 @@
import { When, Then } from 'cypress-cucumber-preprocessor/steps'
import { When, Then } from "cypress-cucumber-preprocessor/steps";
const narratorAvatar = 'https://s3.amazonaws.com/uifaces/faces/twitter/nerrsoft/128.jpg'
const narratorAvatar =
"https://s3.amazonaws.com/uifaces/faces/twitter/nerrsoft/128.jpg";
Then('I click on the {string} button', text => {
cy.get('button').contains(text).click()
})
Then("I click on the {string} button", text => {
cy.get("button")
.contains(text)
.click();
});
Then('my comment should be successfully created', () => {
cy.get('.iziToast-message')
.contains('Comment Submitted')
})
Then("my comment should be successfully created", () => {
cy.get(".iziToast-message").contains("Comment Submitted");
});
Then('I should see my comment', () => {
cy.get('div.comment p')
.should('contain', 'Human Connection rocks')
.get('.ds-avatar img')
.should('have.attr', 'src')
.and('contain', narratorAvatar)
})
Then("I should see my comment", () => {
cy.get("div.comment p")
.should("contain", "Human Connection rocks")
.get(".ds-avatar img")
.should("have.attr", "src")
.and("contain", narratorAvatar)
.get("div p.ds-text span")
.should("contain", "today at");
});
Then('the editor should be cleared', () => {
cy.get('.ProseMirror p')
.should('have.class', 'is-empty')
})
Then("the editor should be cleared", () => {
cy.get(".ProseMirror p").should("have.class", "is-empty");
});

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

@ -230,7 +230,7 @@ When('I type in the following text:', text => {
Then('the post shows up on the landing page at position {int}', index => {
cy.openPage('landing')
const selector = `:nth-child(${index}) > .ds-card > .ds-card-content`
const selector = `.post-card:nth-child(${index}) > .ds-card-content`
cy.get(selector).should('contain', lastPost.title)
cy.get(selector).should('contain', lastPost.content)
})

2
neo4j/.env.template Normal file
View File

@ -0,0 +1,2 @@
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=letmein

1
neo4j/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.env

View File

@ -1,3 +1,11 @@
FROM neo4j:3.5.5
LABEL Description="Neo4J database of the Social Network Human-Connection.org with preinstalled database constraints and indices" Vendor="Human Connection gGmbH" Version="0.0.1" Maintainer="Human Connection gGmbH (developer@human-connection.org)"
ARG BUILD_COMMIT
ENV BUILD_COMMIT=$BUILD_COMMIT
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
RUN apk add --no-cache --quiet procps
COPY db_setup.sh /usr/local/bin/db_setup
COPY entrypoint.sh /docker-entrypoint-wrapper.sh
ENTRYPOINT ["/docker-entrypoint-wrapper.sh"]

64
neo4j/README.md Normal file
View File

@ -0,0 +1,64 @@
# Neo4J
Human Connection is a social network. Using a graph based database which can
model nodes and edges natively - a network - feels like an obvious choice. We
decided to use [Neo4j](https://neo4j.com/), the currently most used graph
database available. The community edition of Neo4J is Free and Open Source and
we try our best to keep our application compatible with the community edition
only.
## Installation with Docker
Run:
```bash
docker-compose up
```
You can access Neo4J through [http://localhost:7474/](http://localhost:7474/)
for an interactive cypher shell and a visualization of the graph.
## Installation without Docker
Install community edition of [Neo4J]() along with the plugin
[Apoc](https://github.com/neo4j-contrib/neo4j-apoc-procedures) on your system.
To do so, go to [releases](https://neo4j.com/download-center/#releases), choose
"Community Server", download the installation files for you operation system
and unpack the files.
Download [Neo4j Apoc](https://github.com/neo4j-contrib/neo4j-apoc-procedures/releases)
and drop the file into the `plugins` folder of the just extracted Neo4j-Server.
### Alternatives
You can download [Neo4j Desktop](https://neo4j.com/download/) and run locally
for development, spin up a
[hosted Neo4j Sandbox instance](https://neo4j.com/download/), run Neo4j in one
of the [many cloud options](https://neo4j.com/developer/guide-cloud-deployment/),
[spin up Neo4j in a Docker container](https://neo4j.com/developer/docker/),
on Archlinux you can install [neo4j-community from AUR](https://aur.archlinux.org/packages/neo4j-community/)
or on Debian-based systems install [Neo4j from the Debian Repository](http://debian.neo4j.org/).
Just be sure to update the Neo4j connection string and credentials accordingly
in `backend/.env`.
Start Neo4J and confirm the database is running at [http://localhost:7474](http://localhost:7474).
## Database Indices and Constraints
If you are not running our dedicated Neo4J [docker image](https://hub.docker.com/r/humanconnection/neo4j),
which is the case if you setup Neo4J locally without docker, then you have to
setup unique indices and database constraints manually.
If you have `cypher-shell` available with your local installation of neo4j you
can run:
```bash
# in folder neo4j/
$ cp .env.template .env
$ ./db_setup.sh
```
Otherwise if you don't have `cypher-shell` available, simply copy the cypher
statements [from the script](./neo4j/db_setup.sh) and paste the scripts into your
database [browser frontend](http://localhost:7474).

42
neo4j/db_setup.sh Executable file
View File

@ -0,0 +1,42 @@
#!/usr/bin/env bash
ENV_FILE=$(dirname "$0")/.env
[[ -f "$ENV_FILE" ]] && source "$ENV_FILE"
if [ -z "$NEO4J_USERNAME" ] || [ -z "$NEO4J_PASSWORD" ]; then
echo "Please set NEO4J_USERNAME and NEO4J_PASSWORD environment variables."
echo "Setting up database constraints and indexes will probably fail because of authentication errors."
echo "E.g. you could \`cp .env.template .env\` unless you run the script in a docker container"
fi
until echo 'RETURN "Connection successful" as info;' | cypher-shell
do
echo "Connecting to neo4j failed, trying again..."
sleep 1
done
echo '
RETURN "Here is a list of indexes and constraints BEFORE THE SETUP:" as info;
CALL db.indexes();
' | cypher-shell
echo '
CALL db.index.fulltext.createNodeIndex("full_text_search",["Post"],["title", "content"]);
CREATE CONSTRAINT ON (p:Post) ASSERT p.id IS UNIQUE;
CREATE CONSTRAINT ON (c:Comment) ASSERT c.id IS UNIQUE;
CREATE CONSTRAINT ON (c:Category) ASSERT c.id IS UNIQUE;
CREATE CONSTRAINT ON (u:User) ASSERT u.id IS UNIQUE;
CREATE CONSTRAINT ON (o:Organization) ASSERT o.id IS UNIQUE;
CREATE CONSTRAINT ON (t:Tag) ASSERT t.id IS UNIQUE;
CREATE CONSTRAINT ON (p:Post) ASSERT p.slug IS UNIQUE;
CREATE CONSTRAINT ON (c:Category) ASSERT c.slug IS UNIQUE;
CREATE CONSTRAINT ON (u:User) ASSERT u.slug IS UNIQUE;
CREATE CONSTRAINT ON (o:Organization) ASSERT o.slug IS UNIQUE;
' | cypher-shell
echo '
RETURN "Setting up all the indexes and constraints seems to have been successful. Here is a list AFTER THE SETUP:" as info;
CALL db.indexes();
' | cypher-shell

21
neo4j/entrypoint.sh Executable file
View File

@ -0,0 +1,21 @@
#!/bin/bash
# credits: https://github.com/javamonkey79
# https://github.com/neo4j/docker-neo4j/issues/166
# turn on bash's job control
set -m
# Start the primary process and put it in the background
/docker-entrypoint.sh neo4j &
# Start the helper process
db_setup
# the my_helper_process might need to know how to wait on the
# primary process to start before it does its work and returns
# now we bring the primary process back into the foreground
# and leave it there
fg %1

View File

@ -1,33 +0,0 @@
#!/usr/bin/env bash
# If the user has the password `neo4j` this is a strong indicator, that we are
# the initial default user. Before we can create constraints, we have to change
# the default password. This is a security feature of neo4j.
if echo ":exit" | cypher-shell --password neo4j 2> /dev/null ; then
if [[ -z "${NEO4J_PASSWORD}" ]]; then
echo "NEO4J_PASSWORD environment variable is undefined. I cannot set the initial password."
else
echo "CALL dbms.security.changePassword('${NEO4J_PASSWORD}');" | cypher-shell --password neo4j
fi
fi
set -e
echo '
CALL db.index.fulltext.createNodeIndex("full_text_search",["Post"],["title", "content"]);
CREATE CONSTRAINT ON (p:Post) ASSERT p.id IS UNIQUE;
CREATE CONSTRAINT ON (c:Comment) ASSERT c.id IS UNIQUE;
CREATE CONSTRAINT ON (c:Category) ASSERT c.id IS UNIQUE;
CREATE CONSTRAINT ON (u:User) ASSERT u.id IS UNIQUE;
CREATE CONSTRAINT ON (o:Organization) ASSERT o.id IS UNIQUE;
CREATE CONSTRAINT ON (t:Tag) ASSERT t.id IS UNIQUE;
CREATE CONSTRAINT ON (p:Post) ASSERT p.slug IS UNIQUE;
CREATE CONSTRAINT ON (c:Category) ASSERT c.slug IS UNIQUE;
CREATE CONSTRAINT ON (u:User) ASSERT u.slug IS UNIQUE;
CREATE CONSTRAINT ON (o:Organization) ASSERT o.slug IS UNIQUE;
' | cypher-shell
echo "Successfully created all indices and unique constraints:"
echo 'CALL db.indexes();' | cypher-shell

View File

@ -2,7 +2,9 @@
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
docker build --build-arg BUILD_COMMIT=$TRAVIS_COMMIT --target production -t humanconnection/nitro-backend:latest $TRAVIS_BUILD_DIR/backend
docker build --build-arg BUILD_COMMIT=$TRAVIS_COMMIT --target production -t humanconnection/nitro-web:latest $TRAVIS_BUILD_DIR/webapp
docker build -t humanconnection/nitro-maintenance-worker:latest $TRAVIS_BUILD_DIR/deployment/legacy-migration/maintenance-worker
docker build --build-arg BUILD_COMMIT=$TRAVIS_COMMIT -t humanconnection/neo4j:latest $TRAVIS_BUILD_DIR/neo4j
docker build -t humanconnection/maintenance-worker:latest $TRAVIS_BUILD_DIR/deployment/legacy-migration/maintenance-worker
docker push humanconnection/nitro-backend:latest
docker push humanconnection/nitro-web:latest
docker push humanconnection/nitro-maintenance-worker:latest
docker push humanconnection/neo4j:latest
docker push humanconnection/maintenance-worker:latest

View File

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

View File

@ -7,7 +7,7 @@
</div>
<div v-else :class="{ comment: true, 'disabled-content': comment.deleted || comment.disabled }">
<ds-space margin-bottom="x-small">
<hc-user :user="author" />
<hc-user :user="author" :date-time="comment.createdAt" />
</ds-space>
<no-ssr>
<content-menu
@ -45,6 +45,7 @@ export default {
return {}
},
},
dateTime: { type: [Date, String], default: null },
},
computed: {
...mapGetters({

View File

@ -0,0 +1,54 @@
import { mount, createLocalVue } from '@vue/test-utils'
import FilterMenu from './FilterMenu.vue'
import Styleguide from '@human-connection/styleguide'
const localVue = createLocalVue()
localVue.use(Styleguide)
describe('FilterMenu.vue', () => {
let wrapper
let mocks
const createWrapper = mountMethod => {
return mountMethod(FilterMenu, {
mocks,
localVue,
})
}
beforeEach(() => {
mocks = { $t: () => {} }
})
describe('mount', () => {
beforeEach(() => {
wrapper = createWrapper(mount)
})
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()
})
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('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

@ -0,0 +1,57 @@
<template>
<ds-card>
<ds-flex>
<ds-flex-item class="filter-menu-title">
<ds-heading size="h3">
{{ $t('filter-menu.title') }}
</ds-heading>
</ds-flex-item>
<ds-flex-item>
<div class="filter-menu-buttons">
<ds-button
name="filter-by-followed-authors-only"
icon="user-plus"
:primary="onlyFollowed"
@click="toggleOnlyFollowed"
/>
</div>
</ds-flex-item>
</ds-flex>
</ds-card>
</template>
<script>
export default {
data() {
// We have to fix styleguide here. It uses .includes wich will always be
// false for arrays of objects.
return {
filterBubble: {
author: 'all',
},
}
},
computed: {
onlyFollowed() {
return this.filterBubble.author === 'following'
},
},
methods: {
toggleOnlyFollowed() {
this.filterBubble.author = this.onlyFollowed ? 'all' : 'following'
this.$emit('changeFilterBubble', this.filterBubble)
},
},
}
</script>
<style lang="scss">
.filter-menu-title {
display: flex;
align-items: center;
}
.filter-menu-buttons {
float: right;
}
</style>

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

@ -1,4 +1,7 @@
{
"filter-menu": {
"title": "Deine Filterblase"
},
"login": {
"copy": "Wenn Du bereits ein Konto bei Human Connection hast, melde Dich bitte hier an.",
"login": "Einloggen",
@ -160,6 +163,11 @@
}
},
"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

@ -1,4 +1,7 @@
{
"filter-menu": {
"title": "Your filter bubble"
},
"login": {
"copy": "If you already have a human-connection account, login here.",
"login": "Login",
@ -160,6 +163,11 @@
}
},
"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,31 +51,30 @@
"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.0",
"apollo-client": "~2.6.1",
"cookie-universal-nuxt": "~2.0.14",
"cross-env": "~5.2.0",
"date-fns": "2.0.0-alpha.27",
"date-fns": "2.0.0-alpha.29",
"express": "~4.17.1",
"graphql": "~14.3.1",
"jsonwebtoken": "~8.5.1",
"linkify-it": "~2.1.0",
"nuxt": "~2.8.0",
"nuxt": "~2.8.1",
"nuxt-dropzone": "^1.0.2",
"nuxt-env": "~0.1.0",
"stack-utils": "^1.0.2",
"string-hash": "^1.1.3",
"tiptap": "1.20.1",
"tiptap-extensions": "1.20.2",
"tiptap-extensions": "1.21.0",
"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"
},
@ -107,7 +106,7 @@
"nodemon": "~1.19.1",
"prettier": "~1.17.1",
"sass-loader": "~7.1.0",
"tippy.js": "^4.3.2",
"tippy.js": "^4.3.3",
"vue-jest": "~3.0.4",
"vue-svg-loader": "~0.12.0"
}

View File

@ -1,6 +1,9 @@
<template>
<div>
<ds-flex v-if="Post && Post.length" :width="{ base: '100%' }" gutter="base">
<ds-flex :width="{ base: '100%' }" gutter="base">
<ds-flex-item>
<filter-menu @changeFilterBubble="changeFilterBubble" />
</ds-flex-item>
<hc-post-card
v-for="(post, index) in uniq(Post)"
:key="post.id"
@ -24,6 +27,7 @@
</template>
<script>
import FilterMenu from '~/components/FilterMenu/FilterMenu.vue'
import gql from 'graphql-tag'
import uniqBy from 'lodash/uniqBy'
import HcPostCard from '~/components/PostCard'
@ -31,6 +35,7 @@ import HcLoadMore from '~/components/LoadMore.vue'
export default {
components: {
FilterMenu,
HcPostCard,
HcLoadMore,
},
@ -40,6 +45,7 @@ export default {
Post: [],
page: 1,
pageSize: 10,
filterBubble: { author: 'all' },
}
},
computed: {
@ -51,6 +57,10 @@ export default {
},
},
methods: {
changeFilterBubble(filterBubble) {
this.filterBubble = filterBubble
this.$apollo.queries.Post.refresh()
},
uniq(items, field = 'id') {
return uniqBy(items, field)
},
@ -66,6 +76,7 @@ export default {
this.page++
this.$apollo.queries.Post.fetchMore({
variables: {
filterBubble: this.filterBubble,
first: this.pageSize,
offset: this.offset,
},
@ -91,8 +102,8 @@ export default {
Post: {
query() {
return gql(`
query Post($first: Int, $offset: Int) {
Post(first: $first, offset: $offset) {
query Post($filterBubble: FilterBubble, $first: Int, $offset: Int) {
Post(filterBubble: $filterBubble, first: $first, offset: $offset) {
id
title
contentExcerpt
@ -135,6 +146,7 @@ export default {
},
variables() {
return {
filterBubble: this.filterBubble,
first: this.pageSize,
offset: 0,
}

View File

@ -137,7 +137,15 @@
<ds-flex-item class="profile-top-navigation">
<ds-card class="ds-tab-nav">
<ds-flex>
<ds-flex-item class="ds-tab-nav-item ds-tab-nav-item-active">
<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>
@ -147,28 +155,43 @@
</no-ssr>
</ds-space>
</ds-flex-item>
<!--<ds-flex-item class="ds-tab-nav-item">
<ds-space margin="small">-->
<!-- TODO: find better solution for rendering errors -->
<!--
<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"/>
<hc-count-to slot="count" :end-val="user.commentsCount" />
</ds-number>
</no-ssr>
</ds-space>
</ds-flex-item>
-->
<!--<ds-flex-item class="ds-tab-nav-item">
<ds-space margin="small">-->
<!-- TODO: find better solution for rendering errors -->
<!--<no-ssr>
<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"/>
<hc-count-to slot="count" :end-val="user.shoutedCount" />
</ds-number>
</no-ssr>
</ds-space>
</ds-flex-item>-->
</ds-flex-item>
</ds-flex>
</ds-card>
</ds-flex-item>
@ -343,6 +366,14 @@ export default {
</script>
<style lang="scss">
.pointer {
cursor: pointer;
}
.ds-tab-nav .ds-card-content .ds-tab-nav-item:hover {
border-bottom: 3px solid #c9c6ce;
}
.profile-avatar.ds-avatar {
display: block;
margin: auto;

View File

@ -889,10 +889,10 @@
"@types/istanbul-reports" "^1.1.1"
"@types/yargs" "^12.0.9"
"@nuxt/babel-preset-app@2.8.0":
version "2.8.0"
resolved "https://registry.yarnpkg.com/@nuxt/babel-preset-app/-/babel-preset-app-2.8.0.tgz#ceee59de38868f899a38ad5f754ff41496a6987d"
integrity sha512-bTInLw8ex+DeKU2XO91hHZBGH9cyjE2wr11oD327ZJ4Un5VCUYN9nkDw7l77I/l3FYxY/DveOyQCmUV2UJ1NZA==
"@nuxt/babel-preset-app@2.8.1":
version "2.8.1"
resolved "https://registry.yarnpkg.com/@nuxt/babel-preset-app/-/babel-preset-app-2.8.1.tgz#4b6b9ec293d66426034b30409ebda8b17e625014"
integrity sha512-5dmzk8nYzRdUG1cx7Y0L0ekYNiJlLkl0uomo4s7PPZUvWq0GhyMGYcxiEelLzaFMUu9XSPeu7oX1LWCj9aS8lw==
dependencies:
"@babel/core" "^7.4.5"
"@babel/plugin-proposal-class-properties" "^7.4.4"
@ -904,15 +904,15 @@
"@vue/babel-preset-jsx" "^1.0.0"
core-js "^2.6.5"
"@nuxt/builder@2.8.0":
version "2.8.0"
resolved "https://registry.yarnpkg.com/@nuxt/builder/-/builder-2.8.0.tgz#f78f702d9f713dacb0e5d3b798e210b4e579398b"
integrity sha512-1iNF4jcjdjkibyOWvFh08VnZjSWvMopjfyUBzcn+i4vJ+R3jX2i4ZAmAnp1TLNAbuMIS50o3+I4bXQGCPZCm4g==
"@nuxt/builder@2.8.1":
version "2.8.1"
resolved "https://registry.yarnpkg.com/@nuxt/builder/-/builder-2.8.1.tgz#56bf2147fb8523e13f9a6036b5acd12560d8cfa5"
integrity sha512-sPzP0uTZvRQ0+vizythI6zYv8qFoQDQKZB7uh4+g9AcM2yLxwco2R0hf4axzyBajyGVrlhZ23nONuSzZ6zdB0w==
dependencies:
"@nuxt/devalue" "^1.2.3"
"@nuxt/utils" "2.8.0"
"@nuxt/vue-app" "2.8.0"
chokidar "^3.0.0"
"@nuxt/utils" "2.8.1"
"@nuxt/vue-app" "2.8.1"
chokidar "^3.0.1"
consola "^2.7.1"
fs-extra "^8.0.1"
glob "^7.1.4"
@ -924,13 +924,13 @@
serialize-javascript "^1.7.0"
upath "^1.1.2"
"@nuxt/cli@2.8.0":
version "2.8.0"
resolved "https://registry.yarnpkg.com/@nuxt/cli/-/cli-2.8.0.tgz#6664917fb76237ca4741c22d6c3eb64e1f16b7cb"
integrity sha512-FtsTLRzP4IW32CVMGZM3v0qDn9e9PnQjYRol2XdGrrHIOeTl4L4yacKpWtsdyLr9M6P68j9CcrA/4TiezwQavw==
"@nuxt/cli@2.8.1":
version "2.8.1"
resolved "https://registry.yarnpkg.com/@nuxt/cli/-/cli-2.8.1.tgz#398a3d843462aa6da89fa3c324c9cd4d43453493"
integrity sha512-gYySEW2fRHPodB5dh+NyTulltdezWk8csNVvnqskEOdXGj9+0lQ7zCjZk4nXMQAxWkRGG+r5RM2KgfTUzF3f+g==
dependencies:
"@nuxt/config" "2.8.0"
"@nuxt/utils" "2.8.0"
"@nuxt/config" "2.8.1"
"@nuxt/utils" "2.8.1"
boxen "^4.0.0"
chalk "^2.4.2"
consola "^2.7.1"
@ -944,25 +944,25 @@
std-env "^2.2.1"
wrap-ansi "^5.1.0"
"@nuxt/config@2.8.0":
version "2.8.0"
resolved "https://registry.yarnpkg.com/@nuxt/config/-/config-2.8.0.tgz#56bdb23d4df6c08e9d103b6f50832b1fef6a99ee"
integrity sha512-38uFD/nGRl7y8I3IJthp7b+EmAyieqDpKFNLSk6DVUjGPy+y2m55n8Eu3vCshU3O/yLmI9sxKzz6J73t9vxaEA==
"@nuxt/config@2.8.1":
version "2.8.1"
resolved "https://registry.yarnpkg.com/@nuxt/config/-/config-2.8.1.tgz#cb4c5b2e0d6f85501b45eb4a46489f0661e8deb2"
integrity sha512-kzVYTC4VJP+noVOPb9d0cFBWbyOjLZyDpD8oJL6fUV4FLdEyOAD9Z14mypGOloeCAsc90brNXtqV9gnZiePXvQ==
dependencies:
"@nuxt/utils" "2.8.0"
"@nuxt/utils" "2.8.1"
consola "^2.7.1"
std-env "^2.2.1"
"@nuxt/core@2.8.0":
version "2.8.0"
resolved "https://registry.yarnpkg.com/@nuxt/core/-/core-2.8.0.tgz#e2efb7eda04a2414829bb8ef7ffa89f416474330"
integrity sha512-cNpGB+aLNmlGMIBJpOcCquTs8TQhB3xtTxjFjf40bi4ILH1EU5/oMFU3EokyXARQOYdSbzbKcVRcF/9yaKN1HA==
"@nuxt/core@2.8.1":
version "2.8.1"
resolved "https://registry.yarnpkg.com/@nuxt/core/-/core-2.8.1.tgz#83c872dcef227be8fcd9c8193ad40c35c17d7c53"
integrity sha512-r702kRRX9P7CvU2m5d2AG5+GG5KC0HNYOy+P+1fjxjQhSD8TkHsP28TzgLx1vEgamJsqvdhAiOew60o54lWzaw==
dependencies:
"@nuxt/config" "2.8.0"
"@nuxt/config" "2.8.1"
"@nuxt/devalue" "^1.2.3"
"@nuxt/server" "2.8.0"
"@nuxt/utils" "2.8.0"
"@nuxt/vue-renderer" "2.8.0"
"@nuxt/server" "2.8.1"
"@nuxt/utils" "2.8.1"
"@nuxt/vue-renderer" "2.8.1"
consola "^2.7.1"
debug "^4.1.1"
esm "^3.2.25"
@ -987,12 +987,12 @@
error-stack-parser "^2.0.0"
string-width "^2.0.0"
"@nuxt/generator@2.8.0":
version "2.8.0"
resolved "https://registry.yarnpkg.com/@nuxt/generator/-/generator-2.8.0.tgz#15aaef4add635f009f24c019d5c6fd168b2df297"
integrity sha512-dmgd01tbcqSIBn2HCXW9RxCI43IYtWwB5+F/fX2wZK1ZPRWoiYRpvOM6vxw/4U0vdD2bl+0Hv2e0a/KY+c/C0A==
"@nuxt/generator@2.8.1":
version "2.8.1"
resolved "https://registry.yarnpkg.com/@nuxt/generator/-/generator-2.8.1.tgz#4a94842b6c9ebec455279518a23dfdce99fca2b1"
integrity sha512-K866F2en/0BLnouUbR9bFgJPIiKIfZK9+E/ORSYqPcwPNMHWMFS6Lf1orwPct11nxcv8mzqBUodXEZLcW01feA==
dependencies:
"@nuxt/utils" "2.8.0"
"@nuxt/utils" "2.8.1"
chalk "^2.4.2"
consola "^2.7.1"
fs-extra "^8.0.1"
@ -1017,13 +1017,13 @@
consola "^2.3.0"
node-fetch "^2.3.0"
"@nuxt/server@2.8.0":
version "2.8.0"
resolved "https://registry.yarnpkg.com/@nuxt/server/-/server-2.8.0.tgz#ed27748c455541ef3d4cb607afa726fca56d88ed"
integrity sha512-2lrvBxmrFkY0Tf34xJkwiCZGfCtcZ3xJvaRJPrGkdao0AgWzYnYvgecyfKxyV+DdIz6lDFqVygFvuJPjzATWkA==
"@nuxt/server@2.8.1":
version "2.8.1"
resolved "https://registry.yarnpkg.com/@nuxt/server/-/server-2.8.1.tgz#f907e83446e2cd65203390197cf0d9b11536d20a"
integrity sha512-B7/AnrhTxARN0EDHrQzgoDEG1UBUqhJ0PZcvajeUX8RIgQuW+wF16hqUwsNEhtMtIHpYq6TqH58lCt/ylsiw4Q==
dependencies:
"@nuxt/config" "2.8.0"
"@nuxt/utils" "2.8.0"
"@nuxt/config" "2.8.1"
"@nuxt/utils" "2.8.1"
"@nuxtjs/youch" "^4.2.3"
chalk "^2.4.2"
compression "^1.7.4"
@ -1040,10 +1040,10 @@
serve-static "^1.14.1"
server-destroy "^1.0.1"
"@nuxt/utils@2.8.0":
version "2.8.0"
resolved "https://registry.yarnpkg.com/@nuxt/utils/-/utils-2.8.0.tgz#974d9539532007b328c2cb5b16d1c3605b0d232f"
integrity sha512-Nl/hes3JcHE/IwnDWPIXgtyTS6FLHD0x7qrqqJFwQgiixGDq6S3H5NHm1GeiaFq5HTaaNWeW/4WHjfnXGkytIQ==
"@nuxt/utils@2.8.1":
version "2.8.1"
resolved "https://registry.yarnpkg.com/@nuxt/utils/-/utils-2.8.1.tgz#0310e2db8e5591e7f164da3931624133847c0e0c"
integrity sha512-yw2vJLSwj37BTCwfJv1GkiTdDqDWLvC/QpUnL/tDq362iy7DjPl0v3c4qG5YNEQ7GxiIiWAj4ZzCFuwxa/ToDw==
dependencies:
consola "^2.7.1"
fs-extra "^8.0.1"
@ -1054,10 +1054,10 @@
signal-exit "^3.0.2"
ua-parser-js "^0.7.19"
"@nuxt/vue-app@2.8.0":
version "2.8.0"
resolved "https://registry.yarnpkg.com/@nuxt/vue-app/-/vue-app-2.8.0.tgz#c2e1d960b6bc68257e7fd45f9a9c4650571bf982"
integrity sha512-LrdfeP4Qdh4LKAG0SbVlgxqwqURlhSQIWDrduu29qzZgWp0xeyoSsFWBPjFx8h95/gFzY2isF8hkyPrUM7UPIg==
"@nuxt/vue-app@2.8.1":
version "2.8.1"
resolved "https://registry.yarnpkg.com/@nuxt/vue-app/-/vue-app-2.8.1.tgz#1ecfc566cb9ab4679f4521cd409f4c4b78c5f79b"
integrity sha512-LNIrgWmXxyDf3o8fAF9rWJgmzAGbmsPaBWWPw7iUUADVPAeQP0CI+NDoxbIwcIOpo4nP3oqrtUfdj/xNd89CNw==
dependencies:
node-fetch "^2.6.0"
unfetch "^4.1.0"
@ -1068,13 +1068,13 @@
vue-template-compiler "^2.6.10"
vuex "^3.1.1"
"@nuxt/vue-renderer@2.8.0":
version "2.8.0"
resolved "https://registry.yarnpkg.com/@nuxt/vue-renderer/-/vue-renderer-2.8.0.tgz#383d03c0705f15669fc67d2d167ac49efc4a6e31"
integrity sha512-J4JJ6SIvEuZegQs3bR2HB3cmG+jADd2FvasFCISPUnHeJrka3iRvatpgS1m9a6mHqoHQrUlevQFjMZbKqMUBlg==
"@nuxt/vue-renderer@2.8.1":
version "2.8.1"
resolved "https://registry.yarnpkg.com/@nuxt/vue-renderer/-/vue-renderer-2.8.1.tgz#7e9dac6cafef15f4aa7b6d4efda15d4202b6fff0"
integrity sha512-Ew4vPZ76iGgoVsWBr87ncD53y8B6tXqSW43u8i6+Ii3XY1q1zElQFk9pU882k+mSm4FEojJ1OgY8Gp3IKP8S9g==
dependencies:
"@nuxt/devalue" "^1.2.3"
"@nuxt/utils" "2.8.0"
"@nuxt/utils" "2.8.1"
consola "^2.7.1"
fs-extra "^8.0.1"
lru-cache "^5.1.1"
@ -1082,18 +1082,18 @@
vue-meta "^1.6.0"
vue-server-renderer "^2.6.10"
"@nuxt/webpack@2.8.0":
version "2.8.0"
resolved "https://registry.yarnpkg.com/@nuxt/webpack/-/webpack-2.8.0.tgz#1e09ffa3c694703c376d393a52563786ea88fa2d"
integrity sha512-ONBxjADOhaiM7ojNiYdtrxogGwUukdMRCdRp/0krrKvP1tBIRo9nr2kb4hYk4cRGcSmhYL/DrqcEVMYcoyMprA==
"@nuxt/webpack@2.8.1":
version "2.8.1"
resolved "https://registry.yarnpkg.com/@nuxt/webpack/-/webpack-2.8.1.tgz#7ec040fa9f0b262ec5528b016fb3814892bf7e4b"
integrity sha512-eyD32UMBENA3YF/U7Wpu2xoIRU5oPcNTPRiUmXDCoZVQ3E0YOrn2xzp9CriovH8fOBNsb6KHm4XcC8dM5NIKsw==
dependencies:
"@babel/core" "^7.4.5"
"@nuxt/babel-preset-app" "2.8.0"
"@nuxt/babel-preset-app" "2.8.1"
"@nuxt/friendly-errors-webpack-plugin" "^2.5.0"
"@nuxt/utils" "2.8.0"
"@nuxt/utils" "2.8.1"
babel-loader "^8.0.6"
cache-loader "^3.0.1"
caniuse-lite "^1.0.30000971"
caniuse-lite "^1.0.30000973"
chalk "^2.4.2"
consola "^2.7.1"
css-loader "^2.1.1"
@ -1122,7 +1122,7 @@
time-fix-plugin "^2.0.6"
url-loader "^1.1.2"
vue-loader "^15.7.0"
webpack "^4.32.2"
webpack "^4.33.0"
webpack-bundle-analyzer "^3.3.2"
webpack-dev-middleware "^3.7.0"
webpack-hot-middleware "^2.25.0"
@ -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"
@ -1878,23 +1878,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.0, apollo-cache@^1.2.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/apollo-cache/-/apollo-cache-1.3.0.tgz#de5c907cbd329440c9b0aafcbe8436391b9e6142"
integrity sha512-voPlvSIDA2pY3+7QwtXPs7o5uSNAVjUKwimyHWoiW0MIZtPxawtOV/Y+BL85R227JqcjPic1El+QToVR8l4ytQ==
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==
dependencies:
apollo-utilities "^1.3.0"
apollo-utilities "^1.3.1"
tslib "^1.9.3"
apollo-client@^2.5.1, apollo-client@~2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-2.6.0.tgz#9b66c04cd96d622cd72f92e584e7403c17532831"
integrity sha512-Z6oSD45vyw6maktMABXTaJliWdFJy4ihZGxbRh7rY65RWNz0HSm3IX66shLavdNqd4lpOcVuAufJl+w8u6RhLQ==
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==
dependencies:
"@types/zen-observable" "^0.8.0"
apollo-cache "1.3.0"
apollo-cache "1.3.1"
apollo-link "^1.0.0"
apollo-utilities "1.3.0"
apollo-utilities "1.3.1"
symbol-observable "^1.0.2"
ts-invariant "^0.4.0"
tslib "^1.9.3"
@ -2081,12 +2081,13 @@ apollo-upload-client@^10.0.0:
apollo-link-http-common "^0.2.13"
extract-files "^5.0.1"
apollo-utilities@1.3.0, apollo-utilities@^1.0.1, apollo-utilities@^1.0.8, apollo-utilities@^1.2.1, apollo-utilities@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.0.tgz#9803724c07ac94ca11dc26397edb58735d2b0211"
integrity sha512-wQjV+FdWcTWmWUFlChG5rS0vHKy5OsXC6XlV9STRstQq6VbXANwHy6DHnTEQAfLXWAbNcPgBu+nBUpR3dFhwrA==
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==
dependencies:
fast-json-stable-stringify "^2.0.0"
lodash.isequal "^4.5.0"
ts-invariant "^0.4.0"
tslib "^1.9.3"
@ -2861,10 +2862,10 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000939, caniuse-lite@^1.0.30000957, can
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000967.tgz#a5039577806fccee80a04aaafb2c0890b1ee2f73"
integrity sha512-rUBIbap+VJfxTzrM4akJ00lkvVb5/n5v3EGXfWzSH5zT8aJmGzjA8HWhJ4U6kCpzxozUSnB+yvAYDRPY6mRpgQ==
caniuse-lite@^1.0.30000971:
version "1.0.30000971"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000971.tgz#d1000e4546486a6977756547352bc96a4cfd2b13"
integrity sha512-TQFYFhRS0O5rdsmSbF1Wn+16latXYsQJat66f7S7lizXW1PVpWJeZw9wqqVLIjuxDRz7s7xRUj13QCfd8hKn6g==
caniuse-lite@^1.0.30000973:
version "1.0.30000973"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000973.tgz#2f8e8e54f9e6c5b7a631c9e69bfa1093d8cfd360"
integrity sha512-/F3t/Yo8LEdRSEPCmI15fLu5vepVh9UCg/9inJXF5AAfW7xRRJkbaM2ut52iRMQMnGCLQouLbFdbOA+VEFOIsg==
capture-exit@^2.0.0:
version "2.0.0"
@ -2944,10 +2945,10 @@ chokidar@^2.0.2, chokidar@^2.1.5:
optionalDependencies:
fsevents "^1.2.7"
chokidar@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.0.0.tgz#6b538f0fd6d5d31d5dd2b59e05426bec0f49aa40"
integrity sha512-ebzWopcacB2J19Jsb5RPtMrzmjUZ5VAQnsL0Ztrix3lswozHbiDp+1Lg3AWSKHdwsps/W2vtshA/x3I827F78g==
chokidar@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.0.1.tgz#98fe9aa476c55d9aea7841d6325ffdb30e95b40c"
integrity sha512-2ww34sJWehnbpV0Q4k4V5Hh7juo7po6z7LUWkcIQnSGN1lHOL8GGtLtfwabKvLFQw/hbSUQ0u6V7OgGYgBzlkQ==
dependencies:
anymatch "^3.0.1"
async-each "^1.0.3"
@ -2956,7 +2957,7 @@ chokidar@^3.0.0:
is-binary-path "^2.1.0"
is-glob "^4.0.1"
normalize-path "^3.0.0"
readdirp "^3.0.1"
readdirp "^3.0.2"
optionalDependencies:
fsevents "^2.0.6"
@ -3746,10 +3747,10 @@ data-urls@^1.0.0:
whatwg-mimetype "^2.2.0"
whatwg-url "^7.0.0"
date-fns@2.0.0-alpha.27:
version "2.0.0-alpha.27"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.27.tgz#5ecd4204ef0e7064264039570f6e8afbc014481c"
integrity sha512-cqfVLS+346P/Mpj2RpDrBv0P4p2zZhWWvfY5fuWrXNR/K38HaAGEkeOwb47hIpQP9Jr/TIxjZ2/sNMQwdXuGMg==
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-now@^0.1.4:
version "0.1.4"
@ -6850,6 +6851,11 @@ 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"
@ -7660,18 +7666,18 @@ nuxt-env@~0.1.0:
resolved "https://registry.yarnpkg.com/nuxt-env/-/nuxt-env-0.1.0.tgz#8ac50b9ff45391ad3044ea932cbd05f06a585f87"
integrity sha512-7mTao3qG0zfN0hahk3O6SuDy0KEwYmNojammWQsMwhqMn3aUjX4nMYnWDa0pua+2/rwAY9oG53jQtLgJdG7f9w==
nuxt@~2.8.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/nuxt/-/nuxt-2.8.0.tgz#34e01bb9eeec02324f8e446d0c03995f174b49ce"
integrity sha512-EGgSJOWSVmH8pOPmEPkf5V8C3IpfGReHziqEdTK8JrrAb1tvGKbTrfbNwaXV0qJ62vdVSSiHtFyWqOFEHXhJ1g==
nuxt@~2.8.1:
version "2.8.1"
resolved "https://registry.yarnpkg.com/nuxt/-/nuxt-2.8.1.tgz#10a307e51973fa7b55abfeb41b84c76cbdd6f2f6"
integrity sha512-y0XsKvusxmtWVsBB4kt+yA7+KoALPnUvkfLBxSy2kV2S1AudGNYAJK0IdUAP4xKd0QJSc9IzSDVat8UZi4FEQA==
dependencies:
"@nuxt/builder" "2.8.0"
"@nuxt/cli" "2.8.0"
"@nuxt/core" "2.8.0"
"@nuxt/generator" "2.8.0"
"@nuxt/builder" "2.8.1"
"@nuxt/cli" "2.8.1"
"@nuxt/core" "2.8.1"
"@nuxt/generator" "2.8.1"
"@nuxt/loading-screen" "^0.5.2"
"@nuxt/opencollective" "^0.2.2"
"@nuxt/webpack" "2.8.0"
"@nuxt/webpack" "2.8.1"
nwsapi@^2.0.7:
version "2.1.4"
@ -8991,10 +8997,10 @@ prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.0.1:
prosemirror-state "^1.0.0"
w3c-keyname "^1.1.8"
prosemirror-model@^1.0.0, prosemirror-model@^1.1.0, prosemirror-model@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.7.0.tgz#1fde0fd7cb2f9ead0be1581ad9f04593002a65aa"
integrity sha512-/6ul6guiqyAl5I+0qbnL7SlmuX0DEfYqjvzeLUVEnb7nwF/vmKZuWqbjEG2tqi/9SSudvd3UxQTBDHvxy9hQwA==
prosemirror-model@^1.0.0, prosemirror-model@^1.1.0, prosemirror-model@^1.7.0, prosemirror-model@^1.7.1:
version "1.7.1"
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.7.1.tgz#f140a6e366e1e283aa7a94dbb8c2c7d13139689e"
integrity sha512-hYrZPbJvdo2QWERmkCuS80BEf5Rcf3+S28ETr4xu8XKPYjmU6aeQn23G1Fu/2rwqUmk5ZyWYo2nyEsN+Cdv2Qg==
dependencies:
orderedmap "^1.0.0"
@ -9322,10 +9328,10 @@ readdirp@^2.2.1:
micromatch "^3.1.10"
readable-stream "^2.0.2"
readdirp@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.0.1.tgz#14a8875883c5575c235579624a1e177cb0b1ec58"
integrity sha512-emMp13NEwWQQX1yeDgrzDNCSY7NHV6k9HTW0OhyQqOAzYacbqQhnmWiCYjxNPcqMTQ9k77oXQJp28jkytm3+jg==
readdirp@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.0.2.tgz#cba63348e9e42fc1bd334b1d2ef895b6a043cbd6"
integrity sha512-LbyJYv48eywrhOlScq16H/VkCiGKGPC2TpOdZCJ7QXnYEjn3NN/Oblh8QEU3vqfSRBB7OGvh5x45NKiVeNujIQ==
dependencies:
picomatch "^2.0.4"
@ -10555,43 +10561,43 @@ 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.3:
version "4.3.3"
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-4.3.3.tgz#396304bea577bbff03f2700a1761329e8c1fce86"
integrity sha512-2fPMlquzVQxpLoOd0eJA1sPZ86/R6zD/9985wV0d2zhhX52DiO3aeg7TTS/mBrUjgFwVZh19YLb4l2c8bJkQPw==
dependencies:
popper.js "^1.14.7"
tiptap-commands@^1.10.5, tiptap-commands@^1.10.6:
version "1.10.6"
resolved "https://registry.yarnpkg.com/tiptap-commands/-/tiptap-commands-1.10.6.tgz#46f972aacbc8d175248ab7ed7e6183ebc6cc72ed"
integrity sha512-62GrTo3Mmev3AmN0rFDa0gzUFQyN9yTjpuH6xMTo0OqMx6iTluqxdiROB2Hc+9qVCHj6qFwJIG4t8jPrYiuKuw==
tiptap-commands@^1.10.5, tiptap-commands@^1.10.7:
version "1.10.7"
resolved "https://registry.yarnpkg.com/tiptap-commands/-/tiptap-commands-1.10.7.tgz#c177f875e8a90e05171fdd441190b4b7a769bd27"
integrity sha512-Ij62dHkYvOFUcaj+xlbahhoqcY8bFIEho6fXKrxcjjtGzS2DyJnvXmWN6Ow65uZWXkf5Zf8ae6XnYNaKqP3Pyg==
dependencies:
prosemirror-commands "^1.0.8"
prosemirror-inputrules "^1.0.4"
prosemirror-model "^1.7.0"
prosemirror-model "^1.7.1"
prosemirror-schema-list "^1.0.3"
prosemirror-state "^1.2.3"
prosemirror-tables "^0.8.1"
prosemirror-utils "^0.9.0"
tiptap-utils "^1.5.4"
tiptap-utils "^1.5.5"
tiptap-extensions@1.20.2:
version "1.20.2"
resolved "https://registry.yarnpkg.com/tiptap-extensions/-/tiptap-extensions-1.20.2.tgz#820c8b1c0c3d2f2176a634c428c22b425f7bbc5f"
integrity sha512-DG5ba2DRKJS11T9B8RMtR/YMkU0PNM25pgDVJ/9MRWBFdD6WFZjyi+fEB9u0uaXRlnyJnYUiV3BScj97UsWl0g==
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==
dependencies:
lowlight "^1.12.1"
prosemirror-collab "^1.1.2"
prosemirror-history "^1.0.4"
prosemirror-model "^1.7.0"
prosemirror-model "^1.7.1"
prosemirror-state "^1.2.3"
prosemirror-tables "^0.8.1"
prosemirror-transform "^1.1.3"
prosemirror-utils "^0.9.0"
prosemirror-view "^1.9.8"
tiptap "^1.21.0"
tiptap-commands "^1.10.6"
tiptap "^1.21.1"
tiptap-commands "^1.10.7"
tiptap-utils@^1.5.3:
version "1.5.3"
@ -10603,12 +10609,12 @@ tiptap-utils@^1.5.3:
prosemirror-tables "^0.8.0"
prosemirror-utils "^0.8.2"
tiptap-utils@^1.5.4:
version "1.5.4"
resolved "https://registry.yarnpkg.com/tiptap-utils/-/tiptap-utils-1.5.4.tgz#c64b65d305ee70793376c9cec1da242ebf6e1884"
integrity sha512-Tl74RM7HZ3v9Eut+cE3DKb+uWM6k0sGvurs9eyCgokrgYmxMMa3CzH1e2c2cmyMB4ErLiY/5v5eMgBKmIZK5Vg==
tiptap-utils@^1.5.5:
version "1.5.5"
resolved "https://registry.yarnpkg.com/tiptap-utils/-/tiptap-utils-1.5.5.tgz#f06c70f0319d9215433b3466b6ece369a078de0f"
integrity sha512-lnaCmIIkyK050qfsPeMDjoTW0sTqBHxfw1h2GXaX5Nr6JF4VQe0izgf2MI6LCd3PKecJBoW4ce5lNkDpV99+WA==
dependencies:
prosemirror-model "^1.7.0"
prosemirror-model "^1.7.1"
prosemirror-state "^1.2.3"
prosemirror-tables "^0.8.1"
prosemirror-utils "^0.9.0"
@ -10629,21 +10635,21 @@ tiptap@1.20.1:
tiptap-commands "^1.10.5"
tiptap-utils "^1.5.3"
tiptap@^1.21.0:
version "1.21.0"
resolved "https://registry.yarnpkg.com/tiptap/-/tiptap-1.21.0.tgz#4d8c1365c611e41c8d4f3d7aa195ddaf891e605b"
integrity sha512-MoOj/8OPMlmoAotIZjAIlUZ59yMMR83xReOw2rGjqbFOooncoY1rLEBp0xz5oe5FLYqoe8dKb+kzOoFERqckVQ==
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==
dependencies:
prosemirror-commands "^1.0.8"
prosemirror-dropcursor "^1.1.1"
prosemirror-gapcursor "^1.0.3"
prosemirror-inputrules "^1.0.4"
prosemirror-keymap "^1.0.1"
prosemirror-model "^1.7.0"
prosemirror-model "^1.7.1"
prosemirror-state "^1.2.3"
prosemirror-view "^1.9.8"
tiptap-commands "^1.10.6"
tiptap-utils "^1.5.4"
tiptap-commands "^1.10.7"
tiptap-utils "^1.5.5"
tmp@^0.0.33:
version "0.0.33"
@ -11312,13 +11318,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"
@ -11434,10 +11433,10 @@ webpack-sources@^1.0.1, webpack-sources@^1.1.0, webpack-sources@^1.3.0:
source-list-map "^2.0.0"
source-map "~0.6.1"
webpack@^4.32.2:
version "4.32.2"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.32.2.tgz#3639375364a617e84b914ddb2c770aed511e5bc8"
integrity sha512-F+H2Aa1TprTQrpodRAWUMJn7A8MgDx82yQiNvYMaj3d1nv3HetKU0oqEulL9huj8enirKi8KvEXQ3QtuHF89Zg==
webpack@^4.33.0:
version "4.33.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.33.0.tgz#c30fc4307db432e5c5e3333aaa7c16a15a3b277e"
integrity sha512-ggWMb0B2QUuYso6FPZKUohOgfm+Z0sVFs8WwWuSH1IAvkWs428VDNmOlAxvHGTB9Dm/qOB/qtE5cRx5y01clxw==
dependencies:
"@webassemblyjs/ast" "1.8.5"
"@webassemblyjs/helper-module-context" "1.8.5"