Merge branch 'master' of github.com:Ocelot-Social-Community/Ocelot-Social into 5137-refactor-social-media-and-mysomethinglist
# Conflicts: # webapp/pages/settings/my-social-media.vue
6
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,8 +1,10 @@
|
||||
---
|
||||
name: 🐛 Bug Report
|
||||
name: "\U0001F41B Bug Report"
|
||||
about: Create a report to help us to improve.
|
||||
title: "\U0001F41B [Bug] XXX"
|
||||
labels: bug
|
||||
title: 🐛 [Bug]
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## :bug: Bug Report
|
||||
|
||||
6
.github/ISSUE_TEMPLATE/devops_ticket.md
vendored
@ -1,8 +1,10 @@
|
||||
---
|
||||
name: 💥 DevOps Ticket
|
||||
name: "\U0001F4A5 DevOps Ticket"
|
||||
about: Help us manage our deployed app.
|
||||
title: "\U0001F4A5 [DevOps] XXX"
|
||||
labels: devops
|
||||
title: 💥 [DevOps]
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## 💥 DevOps Ticket
|
||||
|
||||
7
.github/ISSUE_TEMPLATE/epic.md
vendored
@ -1,9 +1,12 @@
|
||||
---
|
||||
name: 🌟 Epic
|
||||
name: "\U0001F31F Epic"
|
||||
about: Define a big development step.
|
||||
title: "\U0001F31F [EPIC] XXX"
|
||||
labels: epic
|
||||
title: 🌟 [EPIC]
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- THIS ISSUE-TYPE IS NOT FOR YOU! -->
|
||||
<!-- If you need an answer right away, visit the ocelot.social Discord:
|
||||
https://discord.gg/AJSX9DCSUA -->
|
||||
|
||||
6
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,8 +1,10 @@
|
||||
---
|
||||
name: 🚀 Feature Request
|
||||
name: "\U0001F680 Feature Request"
|
||||
about: Suggest an idea for this project.
|
||||
title: "\U0001F680 [Feature] XXX"
|
||||
labels: feature
|
||||
title: 🚀 [Feature]
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## :rocket: Feature Request
|
||||
|
||||
7
.github/ISSUE_TEMPLATE/question.md
vendored
@ -1,9 +1,12 @@
|
||||
---
|
||||
name: 💬 Question
|
||||
name: "\U0001F4AC Question"
|
||||
about: If you need help understanding ocelot.social.
|
||||
title: "\U0001F4AC [Question] XXX"
|
||||
labels: question
|
||||
title: 💬 [Question]
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Chat with ocelot.social team -->
|
||||
<!-- If you need an answer right away, visit the ocelot.social Discord:
|
||||
https://discord.gg/AJSX9DCSUA -->
|
||||
|
||||
7
.github/ISSUE_TEMPLATE/refactor_tickets.md
vendored
@ -1,10 +1,11 @@
|
||||
---
|
||||
name: 🔧 Refactor
|
||||
name: "\U0001F527 Refactor"
|
||||
about: Help us improve our code by refactoring it.
|
||||
title: "\U0001F527 [Refactor] XXX"
|
||||
labels: refactor
|
||||
title: 🔧 [Refactor]
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Refactor
|
||||
<!-- Describe your issue in detail. Include screenshots if needed. Give us as much information as possible. Use a clear and concise description of what the problem is.-->
|
||||
|
||||
|
||||
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,13 +1,15 @@
|
||||
## 🍰 Pullrequest
|
||||
## 🍰 Pull Request
|
||||
<!-- Describe the Pullrequest. Use Screenshots if possible. -->
|
||||
|
||||
XXX
|
||||
|
||||
### Issues
|
||||
<!-- Which Issues does this fix, which are related?
|
||||
<!-- Which Issues does this fix, which are related? -->
|
||||
|
||||
- fixes #XXX
|
||||
- relates #XXX
|
||||
-->
|
||||
- None
|
||||
|
||||
### Todo
|
||||
<!-- In case some parts are still missing, list them here. -->
|
||||
- [X] None
|
||||
|
||||
- [ ] XXX list here …
|
||||
|
||||
43
CHANGELOG.md
@ -4,8 +4,51 @@ All notable changes to this project will be documented in this file. Dates are d
|
||||
|
||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
|
||||
#### [1.1.1](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/1.1.0...1.1.1)
|
||||
|
||||
- chore: 🍰 Refactor Rebranding [`#5390`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5390)
|
||||
- feat: 🍰 Tooltips For Topics [`#5350`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5350)
|
||||
- feat: 🍰 Save Categories In Frontend [`#5284`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5284)
|
||||
- feat: 🍰 Add New Yunite Icons [`#5319`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5319)
|
||||
- chore: 🍰 Update Neode From v^0.4.7 To v^0.4.8 In Backend [`#5334`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5334)
|
||||
- fix: Category Filter Menu Client Only [`#5301`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5301)
|
||||
- feat: Save Category Settings [`#5261`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5261)
|
||||
- feat: Topics Menu [`#5248`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5248)
|
||||
- docs: 🍰 Document GraqhQL Playground [`#5253`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5253)
|
||||
- feat: Categories Filter Menu [`#5198`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5198)
|
||||
- fix: 🍰 Fix Test Description From `enter-nonce.vue` To `change-password` [`#5217`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5217)
|
||||
- Bump cookie-universal-nuxt from 2.1.5 to 2.2.2 in /webapp [`#5218`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5218)
|
||||
- Bump prettier from 2.2.1 to 2.7.1 in /webapp [`#5170`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5170)
|
||||
- Bump eslint-plugin-prettier from 3.1.2 to 3.4.1 in /backend [`#5211`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5211)
|
||||
- Bump slug from 4.0.2 to 6.0.0 in /backend [`#5193`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5193)
|
||||
- chore: 🍰 Fix typo in PULL_REQUEST_TEMPLATE.md file [`#5208`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5208)
|
||||
- Bump slug from 5.1.0 to 6.0.0 [`#5191`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5191)
|
||||
- Bump vue-sweetalert-icons from 4.3.0 to 4.3.1 in /webapp [`#5174`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5174)
|
||||
- feat: 🍰 Change Error Message With `Authorised` To `Authorized` All Over The Place To Have American English [`#5206`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5206)
|
||||
- Bump cross-env from 7.0.2 to 7.0.3 in /webapp [`#5168`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5168)
|
||||
- chore: 🍰 Add `--logHeapUsage` To Jest Test Call [`#5182`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5182)
|
||||
- refactor: 🍰 Rename `UserGroup` To `UserRole` [`#5143`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5143)
|
||||
- add new yunite icons [`bb0d632`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/bb0d6329e7e36ea03671318ea8dd128a6d5a5a7a)
|
||||
- cleanup refactor rebranding [`5f5c0fa`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/5f5c0faa1f28cd4df7681eba335ae5998b2d9cca)
|
||||
- change color and scss in branding [`52070b8`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/52070b8c570970bf48df561134bf67cb4111b640)
|
||||
|
||||
#### [1.1.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/1.0.9...1.1.0)
|
||||
|
||||
> 4 August 2022
|
||||
|
||||
- chore: 🍰 Release v1.1.0 - Implement Categories Again [`#5145`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5145)
|
||||
- feat: Make Categories Optional [`#5102`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5102)
|
||||
- Update issue templates [`#5101`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5101)
|
||||
- chore: 🍰 Betters Automatic Deployment To `stage.ocelot.social` On Push To `master` Branch [`#5097`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5097)
|
||||
- add optional categories to teaser and post [`bc95500`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/bc955003f7c33aabe592bee782aca973b4f00cba)
|
||||
- env vatiable for CATEGORIES_ACTIVE and switch for categories in contribution form [`e31f250`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/e31f250ea5e1949f4f08e72fe82622d41ecd85f1)
|
||||
- fix some tests [`5393c2a`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/5393c2aeaaf070a637390c430d5f03057030ff52)
|
||||
|
||||
#### [1.0.9](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/1.0.8...1.0.9)
|
||||
|
||||
> 20 July 2022
|
||||
|
||||
- chore: 🍰 Release v1.0.9 [`#5095`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5095)
|
||||
- chore: 🍰 Automatic Deployment To `stage.ocelot.social` On Push To `master` Branch [`#5080`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5080)
|
||||
- change footer version-link [`#5091`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5091)
|
||||
- docs: 🍰 Add Neo4j Docu For Important Commands [`#5090`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5090)
|
||||
|
||||
@ -28,3 +28,5 @@ AWS_BUCKET=
|
||||
|
||||
EMAIL_DEFAULT_SENDER="devops@ocelot.social"
|
||||
EMAIL_SUPPORT="devops@ocelot.social"
|
||||
|
||||
CATEGORIES_ACTIVE=false
|
||||
@ -60,6 +60,7 @@ your `.env` configuration file.
|
||||
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.
|
||||
More details about our GraphQL playground and how to use it with ocelot.social can be found [here](./src/graphql/GraphQL-Playground.md).
|
||||
|
||||

|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ocelot-social-backend",
|
||||
"version": "1.0.9",
|
||||
"version": "1.1.1",
|
||||
"description": "GraphQL Backend for ocelot.social",
|
||||
"repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social",
|
||||
"author": "ocelot.social Community",
|
||||
@ -15,7 +15,7 @@
|
||||
"dev": "nodemon --exec babel-node src/ -e js,gql",
|
||||
"dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 src/ -e js,gql",
|
||||
"lint": "eslint src --config .eslintrc.js",
|
||||
"test": "cross-env NODE_ENV=test jest --forceExit --detectOpenHandles --runInBand --coverage",
|
||||
"test": "cross-env NODE_ENV=test NODE_OPTIONS=--max-old-space-size=8192 jest --forceExit --detectOpenHandles --runInBand --coverage --logHeapUsage",
|
||||
"db:clean": "babel-node src/db/clean.js",
|
||||
"db:reset": "yarn run db:clean",
|
||||
"db:seed": "babel-node src/db/seed.js",
|
||||
@ -103,14 +103,14 @@
|
||||
"mustache": "^4.2.0",
|
||||
"neo4j-driver": "^4.0.2",
|
||||
"neo4j-graphql-js": "^2.11.5",
|
||||
"neode": "^0.4.7",
|
||||
"neode": "^0.4.8",
|
||||
"node-fetch": "~2.6.1",
|
||||
"nodemailer": "^6.4.4",
|
||||
"nodemailer-html-to-text": "^3.2.0",
|
||||
"npm-run-all": "~4.1.5",
|
||||
"request": "~2.88.2",
|
||||
"sanitize-html": "~1.22.0",
|
||||
"slug": "~4.0.2",
|
||||
"slug": "~6.0.0",
|
||||
"subscriptions-transport-ws": "^0.9.19",
|
||||
"trunc-html": "~1.1.2",
|
||||
"uuid": "~8.3.2",
|
||||
@ -129,7 +129,7 @@
|
||||
"eslint-plugin-import": "~2.20.2",
|
||||
"eslint-plugin-jest": "~23.8.2",
|
||||
"eslint-plugin-node": "~11.1.0",
|
||||
"eslint-plugin-prettier": "~3.1.2",
|
||||
"eslint-plugin-prettier": "~3.4.1",
|
||||
"eslint-plugin-promise": "~4.3.1",
|
||||
"eslint-plugin-standard": "~4.0.1",
|
||||
"jest": "~25.3.0",
|
||||
|
||||
@ -86,6 +86,7 @@ const options = {
|
||||
ORGANIZATION_URL: emails.ORGANIZATION_LINK,
|
||||
PUBLIC_REGISTRATION: env.PUBLIC_REGISTRATION === 'true' || false,
|
||||
INVITE_REGISTRATION: env.INVITE_REGISTRATION !== 'false', // default = true
|
||||
CATEGORIES_ACTIVE: process.env.CATEGORIES_ACTIVE === 'true' || false,
|
||||
}
|
||||
|
||||
// Check if all required configs are present
|
||||
|
||||
98
backend/src/constants/categories.js
Normal file
@ -0,0 +1,98 @@
|
||||
export const categories = [
|
||||
{
|
||||
icon: 'networking',
|
||||
name: 'networking',
|
||||
description: 'Kooperation, Aktionsbündnisse, Solidarität, Hilfe',
|
||||
},
|
||||
{
|
||||
icon: 'home',
|
||||
name: 'home',
|
||||
description: 'Bauen, Lebensgemeinschaften, Tiny Houses, Gemüsegarten',
|
||||
},
|
||||
{
|
||||
icon: 'energy',
|
||||
name: 'energy',
|
||||
description: 'Öl, Gas, Kohle, Wind, Wasserkraft, Biogas, Atomenergie, ...',
|
||||
},
|
||||
{
|
||||
icon: 'psyche',
|
||||
name: 'psyche',
|
||||
description: 'Seele, Gefühle, Glück',
|
||||
},
|
||||
{
|
||||
icon: 'movement',
|
||||
name: 'body-and-excercise',
|
||||
description: 'Sport, Yoga, Massage, Tanzen, Entspannung',
|
||||
},
|
||||
{
|
||||
icon: 'balance-scale',
|
||||
name: 'law',
|
||||
description: 'Menschenrechte, Gesetze, Verordnungen',
|
||||
},
|
||||
{
|
||||
icon: 'finance',
|
||||
name: 'finance',
|
||||
description: 'Geld, Finanzsystem, Alternativwährungen, ...',
|
||||
},
|
||||
{
|
||||
icon: 'child',
|
||||
name: 'children',
|
||||
description: 'Familie, Pädagogik, Schule, Prägung',
|
||||
},
|
||||
{
|
||||
icon: 'mobility',
|
||||
name: 'mobility',
|
||||
description: 'Reise, Verkehr, Elektromobilität',
|
||||
},
|
||||
{
|
||||
icon: 'shopping-cart',
|
||||
name: 'economy',
|
||||
description: 'Handel, Konsum, Marketing, Lebensmittel, Lieferketten, ...',
|
||||
},
|
||||
{
|
||||
icon: 'peace',
|
||||
name: 'peace',
|
||||
description: 'Krieg, Militär, soziale Verteidigung, Waffen, Cyberattacken',
|
||||
},
|
||||
{
|
||||
icon: 'politics',
|
||||
name: 'politics',
|
||||
description: 'Demokratie, Mitbestimmung, Wahlen, Korruption, Parteien',
|
||||
},
|
||||
{
|
||||
icon: 'nature',
|
||||
name: 'nature',
|
||||
description: 'Tiere, Pflanzen, Landwirtschaft, Ökologie, Artenvielfalt',
|
||||
},
|
||||
{
|
||||
icon: 'science',
|
||||
name: 'science',
|
||||
description: 'Bildung, Hochschule, Publikationen, ...',
|
||||
},
|
||||
{
|
||||
icon: 'health',
|
||||
name: 'health',
|
||||
description: 'Medizin, Ernährung, WHO, Impfungen, Schadstoffe, ...',
|
||||
},
|
||||
{
|
||||
icon: 'media',
|
||||
name: 'it-and-media',
|
||||
description:
|
||||
'Nachrichten, Manipulation, Datenschutz, Überwachung, Datenkraken, AI, Software, Apps',
|
||||
},
|
||||
{
|
||||
icon: 'spirituality',
|
||||
name: 'spirituality',
|
||||
description: 'Religion, Werte, Ethik',
|
||||
},
|
||||
{
|
||||
icon: 'culture',
|
||||
name: 'culture',
|
||||
description: 'Kunst, Theater, Musik, Fotografie, Film',
|
||||
},
|
||||
{
|
||||
icon: 'miscellaneous',
|
||||
name: 'miscellaneous',
|
||||
description: '',
|
||||
},
|
||||
]
|
||||
@ -1,6 +1,8 @@
|
||||
import { getDriver, getNeode } from '../../db/neo4j'
|
||||
import { hashSync } from 'bcryptjs'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
import { categories } from '../../constants/categories'
|
||||
import CONFIG from '../../config'
|
||||
|
||||
const defaultAdmin = {
|
||||
email: 'admin@example.org',
|
||||
@ -10,6 +12,29 @@ const defaultAdmin = {
|
||||
slug: 'admin',
|
||||
}
|
||||
|
||||
const createCategories = async (session) => {
|
||||
const createCategoriesTxResultPromise = session.writeTransaction(async (txc) => {
|
||||
categories.forEach(({ icon, name }, index) => {
|
||||
const id = `cat${index + 1}`
|
||||
txc.run(
|
||||
`MERGE (c:Category {
|
||||
icon: "${icon}",
|
||||
slug: "${name}",
|
||||
name: "${name}",
|
||||
id: "${id}",
|
||||
createdAt: toString(datetime())
|
||||
})`,
|
||||
)
|
||||
})
|
||||
})
|
||||
try {
|
||||
await createCategoriesTxResultPromise
|
||||
console.log('Successfully created categories!') // eslint-disable-line no-console
|
||||
} catch (error) {
|
||||
console.log(`Error creating categories: ${error}`) // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
const createDefaultAdminUser = async (session) => {
|
||||
const readTxResultPromise = session.readTransaction(async (txc) => {
|
||||
const result = await txc.run('MATCH (user:User) RETURN count(user) AS userCount')
|
||||
@ -45,7 +70,7 @@ const createDefaultAdminUser = async (session) => {
|
||||
})
|
||||
try {
|
||||
await createAdminTxResultPromise
|
||||
console.log('Successfully created default admin user') // eslint-disable-line no-console
|
||||
console.log('Successfully created default admin user!') // eslint-disable-line no-console
|
||||
} catch (error) {
|
||||
console.log(error) // eslint-disable-line no-console
|
||||
}
|
||||
@ -58,6 +83,7 @@ class Store {
|
||||
const { driver } = neode
|
||||
const session = driver.session()
|
||||
await createDefaultAdminUser(session)
|
||||
if (CONFIG.CATEGORIES_ACTIVE) await createCategories(session)
|
||||
const writeTxResultPromise = session.writeTransaction(async (txc) => {
|
||||
await txc.run('CALL apoc.schema.assert({},{},true)') // drop all indices
|
||||
return Promise.all(
|
||||
|
||||
@ -6,6 +6,7 @@ import faker from '@faker-js/faker'
|
||||
import Factory from '../db/factories'
|
||||
import { getNeode, getDriver } from '../db/neo4j'
|
||||
import { gql } from '../helpers/jest'
|
||||
import { categories } from '../constants/categories'
|
||||
|
||||
if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) {
|
||||
throw new Error(`You cannot seed the database in a non-staging and real production environment!`)
|
||||
@ -267,104 +268,16 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
dagobert.relateTo(louie, 'blocked'),
|
||||
])
|
||||
|
||||
await Promise.all([
|
||||
Factory.build('category', {
|
||||
id: 'cat1',
|
||||
name: 'Just For Fun',
|
||||
slug: 'just-for-fun',
|
||||
icon: 'smile',
|
||||
await Promise.all(
|
||||
categories.map(({ icon, name }, index) => {
|
||||
Factory.build('category', {
|
||||
id: `cat${index + 1}`,
|
||||
slug: name,
|
||||
name,
|
||||
icon,
|
||||
})
|
||||
}),
|
||||
Factory.build('category', {
|
||||
id: 'cat2',
|
||||
name: 'Happiness & Values',
|
||||
slug: 'happiness-values',
|
||||
icon: 'heart-o',
|
||||
}),
|
||||
Factory.build('category', {
|
||||
id: 'cat3',
|
||||
name: 'Health & Wellbeing',
|
||||
slug: 'health-wellbeing',
|
||||
icon: 'medkit',
|
||||
}),
|
||||
Factory.build('category', {
|
||||
id: 'cat4',
|
||||
name: 'Environment & Nature',
|
||||
slug: 'environment-nature',
|
||||
icon: 'tree',
|
||||
}),
|
||||
Factory.build('category', {
|
||||
id: 'cat5',
|
||||
name: 'Animal Protection',
|
||||
slug: 'animal-protection',
|
||||
icon: 'paw',
|
||||
}),
|
||||
Factory.build('category', {
|
||||
id: 'cat6',
|
||||
name: 'Human Rights & Justice',
|
||||
slug: 'human-rights-justice',
|
||||
icon: 'balance-scale',
|
||||
}),
|
||||
Factory.build('category', {
|
||||
id: 'cat7',
|
||||
name: 'Education & Sciences',
|
||||
slug: 'education-sciences',
|
||||
icon: 'graduation-cap',
|
||||
}),
|
||||
Factory.build('category', {
|
||||
id: 'cat8',
|
||||
name: 'Cooperation & Development',
|
||||
slug: 'cooperation-development',
|
||||
icon: 'users',
|
||||
}),
|
||||
Factory.build('category', {
|
||||
id: 'cat9',
|
||||
name: 'Democracy & Politics',
|
||||
slug: 'democracy-politics',
|
||||
icon: 'university',
|
||||
}),
|
||||
Factory.build('category', {
|
||||
id: 'cat10',
|
||||
name: 'Economy & Finances',
|
||||
slug: 'economy-finances',
|
||||
icon: 'money',
|
||||
}),
|
||||
Factory.build('category', {
|
||||
id: 'cat11',
|
||||
name: 'Energy & Technology',
|
||||
slug: 'energy-technology',
|
||||
icon: 'flash',
|
||||
}),
|
||||
Factory.build('category', {
|
||||
id: 'cat12',
|
||||
name: 'IT, Internet & Data Privacy',
|
||||
slug: 'it-internet-data-privacy',
|
||||
icon: 'mouse-pointer',
|
||||
}),
|
||||
Factory.build('category', {
|
||||
id: 'cat13',
|
||||
name: 'Art, Culture & Sport',
|
||||
slug: 'art-culture-sport',
|
||||
icon: 'paint-brush',
|
||||
}),
|
||||
Factory.build('category', {
|
||||
id: 'cat14',
|
||||
name: 'Freedom of Speech',
|
||||
slug: 'freedom-of-speech',
|
||||
icon: 'bullhorn',
|
||||
}),
|
||||
Factory.build('category', {
|
||||
id: 'cat15',
|
||||
name: 'Consumption & Sustainability',
|
||||
slug: 'consumption-sustainability',
|
||||
icon: 'shopping-cart',
|
||||
}),
|
||||
Factory.build('category', {
|
||||
id: 'cat16',
|
||||
name: 'Global Peace & Nonviolence',
|
||||
slug: 'global-peace-nonviolence',
|
||||
icon: 'angellist',
|
||||
}),
|
||||
])
|
||||
)
|
||||
|
||||
const [environment, nature, democracy, freedom] = await Promise.all([
|
||||
Factory.build('tag', {
|
||||
|
||||
108
backend/src/graphql/GraphQL-Playground.md
Normal file
@ -0,0 +1,108 @@
|
||||
# GraphQL Playground
|
||||
|
||||
To use GraphQL Playground, we need to know some basics:
|
||||
|
||||
## How To Login?
|
||||
|
||||
First, we need to have a user from ocelot.social to log in as.
|
||||
The user can be created by seeding the Neo4j database from the backend or by multiple GraphQL mutations.
|
||||
|
||||
### Seed The Neo4j Database
|
||||
|
||||
In your browser you can reach the GraphQL Playground under `http://localhost:4000/`, if the database and the backend are running, see [backend](../../README.md).
|
||||
There you will also find instructions on how to seed the database.
|
||||
|
||||
### Use GraphQL Mutations To Create A User
|
||||
|
||||
TODO: Describe how to create a user using GraphQL mutations!
|
||||
|
||||
### Login Via GraphQL
|
||||
|
||||
You can register a user by sending the query:
|
||||
|
||||
```gql
|
||||
mutation {
|
||||
login(email: "user@example.org", password: "1234")
|
||||
}
|
||||
```
|
||||
|
||||
Or use `"moderator@example.org"` or `"admin@example.org"` for the roll you need.
|
||||
|
||||
If all goes well, you will receive a QGL response like:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"login": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InUzIiwibmFtZSI6Ikplbm55IFJvc3RvY2siLCJzbHVnIjoiamVubnktcm9zdG9jayIsImlhdCI6MTY2MjAyMzMwNSwiZXhwIjoxNzI1MTM4NTA1LCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjQwMDAiLCJzdWIiOiJ1MyJ9.atBS-SOeS784HPeFl_5s8sRWehEAU1BkgcOZFD8d4bU"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can use this response to set an HTTP header when you click `HTTP HEADERS` in the footer.
|
||||
Just set it with the login token you received in response:
|
||||
|
||||
```json
|
||||
{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InUzIiwibmFtZSI6Ikplbm55IFJvc3RvY2siLCJzbHVnIjoiamVubnktcm9zdG9jayIsImlhdCI6MTY2MjAyMzMwNSwiZXhwIjoxNzI1MTM4NTA1LCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjQwMDAiLCJzdWIiOiJ1MyJ9.atBS-SOeS784HPeFl_5s8sRWehEAU1BkgcOZFD8d4bU"
|
||||
}
|
||||
```
|
||||
|
||||
This token is used for all other queries and mutations you send to the backend.
|
||||
|
||||
## Query And Mutate
|
||||
|
||||
When you are logged in and open a new playground tab by clicking "+", you can create a new group by sending the following mutation:
|
||||
|
||||
```gql
|
||||
mutation {
|
||||
CreateGroup(
|
||||
# id: ""
|
||||
name: "My Group"
|
||||
# slug: ""
|
||||
about: "We will save the world"
|
||||
description: "<p class=\"\"><em>English:</em></p><p class=\"\">This group is hidden.</p><h3>What is our group for?</h3><p>This group was created to allow investigative journalists to share and collaborate.</p><h3>How does it work?</h3><p>Here you can internally share posts and comments about them.</p><p><br></p><p><em>Deutsch:</em></p><p class=\"\">Diese Gruppe ist verborgen.</p><h3>Wofür ist unsere Gruppe?</h3><p class=\"\">Diese Gruppe wurde geschaffen, um investigativen Journalisten den Austausch und die Zusammenarbeit zu ermöglichen.</p><h3>Wie funktioniert das?</h3><p class=\"\">Hier könnt ihr euch intern über Beiträge und Kommentare zu ihnen austauschen.</p>"
|
||||
groupType: hidden
|
||||
actionRadius: interplanetary
|
||||
categoryIds: ["cat12"]
|
||||
) {
|
||||
id
|
||||
name
|
||||
slug
|
||||
createdAt
|
||||
updatedAt
|
||||
disabled
|
||||
deleted
|
||||
about
|
||||
description
|
||||
groupType
|
||||
actionRadius
|
||||
myRole
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You will receive the answer:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"CreateGroup": {
|
||||
"id": "2e3bbadb-804b-4ebc-a673-2d7c7f05e827",
|
||||
"name": "My Group",
|
||||
"slug": "my-group",
|
||||
"createdAt": "2022-09-01T09:44:47.969Z",
|
||||
"updatedAt": "2022-09-01T09:44:47.969Z",
|
||||
"disabled": false,
|
||||
"deleted": false,
|
||||
"about": "We will save the world",
|
||||
"description": "<p class=\"\"><em>English:</em></p><p class=\"\">This group is hidden.</p><h3>What is our group for?</h3><p>This group was created to allow investigative journalists to share and collaborate.</p><h3>How does it work?</h3><p>Here you can internally share posts and comments about them.</p><p><br></p><p><em>Deutsch:</em></p><p class=\"\">Diese Gruppe ist verborgen.</p><h3>Wofür ist unsere Gruppe?</h3><p class=\"\">Diese Gruppe wurde geschaffen, um investigativen Journalisten den Austausch und die Zusammenarbeit zu ermöglichen.</p><h3>Wie funktioniert das?</h3><p class=\"\">Hier könnt ihr euch intern über Beiträge und Kommentare zu ihnen austauschen.</p>",
|
||||
"groupType": "hidden",
|
||||
"actionRadius": "interplanetary",
|
||||
"myRole": "owner"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you look into the Neo4j database with your browser and search the groups, you will now also find your new group.
|
||||
For more details about our Neo4j database read [here](../../../neo4j/README.md).
|
||||
@ -178,6 +178,7 @@ export default shield(
|
||||
GenerateInviteCode: isAuthenticated,
|
||||
switchUserRole: isAdmin,
|
||||
markTeaserAsViewed: allow,
|
||||
saveCategorySettings: isAuthenticated,
|
||||
},
|
||||
User: {
|
||||
email: or(isMyOwn, isAdmin),
|
||||
@ -188,5 +189,6 @@ export default shield(
|
||||
debug,
|
||||
allowExternalErrors,
|
||||
fallbackRule: allow,
|
||||
fallbackError: Error('Not Authorized!'),
|
||||
},
|
||||
)
|
||||
|
||||
@ -102,7 +102,7 @@ describe('authorization', () => {
|
||||
await expect(
|
||||
query({ query: userQuery, variables: { name: 'Owner' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: { User: [null] },
|
||||
})
|
||||
})
|
||||
@ -132,7 +132,7 @@ describe('authorization', () => {
|
||||
await expect(
|
||||
query({ query: userQuery, variables: { name: 'Owner' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: { User: [null] },
|
||||
})
|
||||
})
|
||||
@ -147,7 +147,7 @@ describe('authorization', () => {
|
||||
await expect(
|
||||
query({ query: userQuery, variables: { name: 'Owner' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: { User: [null] },
|
||||
})
|
||||
})
|
||||
@ -198,7 +198,7 @@ describe('authorization', () => {
|
||||
|
||||
it('denies permission', async () => {
|
||||
await expect(mutate({ mutation: signupMutation, variables })).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: { Signup: null },
|
||||
})
|
||||
})
|
||||
@ -288,7 +288,7 @@ describe('authorization', () => {
|
||||
|
||||
it('denies permission', async () => {
|
||||
await expect(mutate({ mutation: signupMutation, variables })).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: { Signup: null },
|
||||
})
|
||||
})
|
||||
|
||||
@ -9,8 +9,7 @@ export default {
|
||||
updatedAt: {
|
||||
type: 'string',
|
||||
isoDate: true,
|
||||
required: true,
|
||||
default: () => new Date().toISOString(),
|
||||
required: false,
|
||||
},
|
||||
post: {
|
||||
type: 'relationship',
|
||||
|
||||
@ -88,10 +88,10 @@ describe('CreateComment', () => {
|
||||
variables = {
|
||||
...variables,
|
||||
postId: 'p1',
|
||||
content: "I'm not authorised to comment",
|
||||
content: "I'm not authorized to comment",
|
||||
}
|
||||
const { errors } = await mutate({ mutation: createCommentMutation, variables })
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
@ -107,14 +107,14 @@ describe('CreateComment', () => {
|
||||
variables = {
|
||||
...variables,
|
||||
postId: 'p1',
|
||||
content: "I'm authorised to comment",
|
||||
content: "I'm authorized to comment",
|
||||
}
|
||||
})
|
||||
|
||||
it('creates a comment', async () => {
|
||||
await expect(mutate({ mutation: createCommentMutation, variables })).resolves.toMatchObject(
|
||||
{
|
||||
data: { CreateComment: { content: "I'm authorised to comment" } },
|
||||
data: { CreateComment: { content: "I'm authorized to comment" } },
|
||||
errors: undefined,
|
||||
},
|
||||
)
|
||||
@ -150,7 +150,7 @@ describe('UpdateComment', () => {
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
const { errors } = await mutate({ mutation: updateCommentMutation, variables })
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
@ -162,7 +162,7 @@ describe('UpdateComment', () => {
|
||||
|
||||
it('throws authorization error', async () => {
|
||||
const { errors } = await mutate({ mutation: updateCommentMutation, variables })
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
@ -217,7 +217,7 @@ describe('UpdateComment', () => {
|
||||
it('returns null', async () => {
|
||||
const { data, errors } = await mutate({ mutation: updateCommentMutation, variables })
|
||||
expect(data).toMatchObject({ UpdateComment: null })
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -242,7 +242,7 @@ describe('DeleteComment', () => {
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
const result = await mutate({ mutation: deleteCommentMutation, variables })
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
@ -254,7 +254,7 @@ describe('DeleteComment', () => {
|
||||
|
||||
it('throws authorization error', async () => {
|
||||
const { errors } = await mutate({ mutation: deleteCommentMutation, variables })
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -72,7 +72,7 @@ describe('donations', () => {
|
||||
it('throws authorization error', async () => {
|
||||
authenticatedUser = undefined
|
||||
await expect(query({ query: donationsQuery, variables })).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -106,7 +106,7 @@ describe('donations', () => {
|
||||
await expect(
|
||||
mutate({ mutation: updateDonationsMutation, variables }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -126,7 +126,7 @@ describe('donations', () => {
|
||||
mutate({ mutation: updateDonationsMutation, variables }),
|
||||
).resolves.toMatchObject({
|
||||
data: { UpdateDonations: null },
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -145,7 +145,7 @@ describe('donations', () => {
|
||||
mutate({ mutation: updateDonationsMutation, variables }),
|
||||
).resolves.toMatchObject({
|
||||
data: { UpdateDonations: null },
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -63,7 +63,7 @@ describe('AddEmailAddress', () => {
|
||||
it('throws AuthorizationError', async () => {
|
||||
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
|
||||
data: { AddEmailAddress: null },
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -169,7 +169,7 @@ describe('VerifyEmailAddress', () => {
|
||||
it('throws AuthorizationError', async () => {
|
||||
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
|
||||
data: { VerifyEmailAddress: null },
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -117,7 +117,7 @@ describe('follow', () => {
|
||||
variables,
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: { followUser: null },
|
||||
})
|
||||
})
|
||||
@ -191,7 +191,7 @@ describe('follow', () => {
|
||||
authenticatedUser = null
|
||||
await expect(mutate({ mutation: mutationUnfollowUser, variables })).resolves.toMatchObject({
|
||||
data: { unfollowUser: null },
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -120,7 +120,7 @@ describe('moderate resources', () => {
|
||||
await expect(
|
||||
mutate({ mutation: reviewMutation, variables: disableVariables }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -134,7 +134,7 @@ describe('moderate resources', () => {
|
||||
await expect(
|
||||
mutate({ mutation: reviewMutation, variables: disableVariables }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -218,7 +218,7 @@ describe('moderate resources', () => {
|
||||
await expect(
|
||||
mutate({ mutation: reviewMutation, variables: disableVariables }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -232,7 +232,7 @@ describe('moderate resources', () => {
|
||||
await expect(
|
||||
mutate({ mutation: reviewMutation, variables: disableVariables }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -488,7 +488,7 @@ describe('moderate resources', () => {
|
||||
await expect(
|
||||
mutate({ mutation: reviewMutation, variables: enableVariables }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -507,7 +507,7 @@ describe('moderate resources', () => {
|
||||
await expect(
|
||||
mutate({ mutation: reviewMutation, variables: enableVariables }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -165,7 +165,7 @@ describe('given some notifications', () => {
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
const { errors } = await query({ query: notificationQuery })
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
@ -313,7 +313,7 @@ describe('given some notifications', () => {
|
||||
mutation: markAsReadMutation,
|
||||
variables: { ...variables, id: 'p1' },
|
||||
})
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import { UserInputError } from 'apollo-server'
|
||||
import { mergeImage, deleteImage } from './images/images'
|
||||
import Resolver from './helpers/Resolver'
|
||||
import { filterForMutedUsers } from './helpers/filterForMutedUsers'
|
||||
import CONFIG from '../../config'
|
||||
|
||||
const maintainPinnedPosts = (params) => {
|
||||
const pinnedPostFilter = { pinned: true }
|
||||
@ -76,12 +77,20 @@ export default {
|
||||
},
|
||||
Mutation: {
|
||||
CreatePost: async (_parent, params, context, _resolveInfo) => {
|
||||
const { categoryIds } = params
|
||||
const { image: imageInput } = params
|
||||
delete params.categoryIds
|
||||
delete params.image
|
||||
params.id = params.id || uuid()
|
||||
const session = context.driver.session()
|
||||
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
||||
const categoriesCypher =
|
||||
CONFIG.CATEGORIES_ACTIVE && categoryIds
|
||||
? `WITH post
|
||||
UNWIND $categoryIds AS categoryId
|
||||
MATCH (category:Category {id: categoryId})
|
||||
MERGE (post)-[:CATEGORIZED]->(category)`
|
||||
: ''
|
||||
const createPostTransactionResponse = await transaction.run(
|
||||
`
|
||||
CREATE (post:Post)
|
||||
@ -93,9 +102,10 @@ export default {
|
||||
WITH post
|
||||
MATCH (author:User {id: $userId})
|
||||
MERGE (post)<-[:WROTE]-(author)
|
||||
${categoriesCypher}
|
||||
RETURN post {.*}
|
||||
`,
|
||||
{ userId: context.user.id, params },
|
||||
{ userId: context.user.id, params, categoryIds },
|
||||
)
|
||||
const [post] = createPostTransactionResponse.records.map((record) => record.get('post'))
|
||||
if (imageInput) {
|
||||
@ -127,7 +137,7 @@ export default {
|
||||
WITH post
|
||||
`
|
||||
|
||||
if (categoryIds && categoryIds.length) {
|
||||
if (CONFIG.CATEGORIES_ACTIVE && categoryIds && categoryIds.length) {
|
||||
const cypherDeletePreviousRelations = `
|
||||
MATCH (post:Post { id: $params.id })-[previousRelations:CATEGORIZED]->(category:Category)
|
||||
DELETE previousRelations
|
||||
@ -348,7 +358,7 @@ export default {
|
||||
undefinedToNull: ['activityId', 'objectId', 'language', 'pinnedAt', 'pinned'],
|
||||
hasMany: {
|
||||
tags: '-[:TAGGED]->(related:Tag)',
|
||||
// categories: '-[:CATEGORIZED]->(related:Category)',
|
||||
categories: '-[:CATEGORIZED]->(related:Category)',
|
||||
comments: '<-[:COMMENTS]-(related:Comment)',
|
||||
shoutedBy: '<-[:SHOUTED]-(related:User)',
|
||||
emotions: '<-[related:EMOTED]',
|
||||
|
||||
@ -281,7 +281,7 @@ describe('CreatePost', () => {
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
const { errors } = await mutate({ mutation: createPostMutation, variables })
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
@ -369,7 +369,7 @@ describe('UpdatePost', () => {
|
||||
it('throws authorization error', async () => {
|
||||
authenticatedUser = null
|
||||
expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: { UpdatePost: null },
|
||||
})
|
||||
})
|
||||
@ -382,7 +382,7 @@ describe('UpdatePost', () => {
|
||||
|
||||
it('throws authorization error', async () => {
|
||||
const { errors } = await mutate({ mutation: updatePostMutation, variables })
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
@ -547,7 +547,7 @@ describe('pin posts', () => {
|
||||
it('throws authorization error', async () => {
|
||||
authenticatedUser = null
|
||||
await expect(mutate({ mutation: pinPostMutation, variables })).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: { pinPost: null },
|
||||
})
|
||||
})
|
||||
@ -556,7 +556,7 @@ describe('pin posts', () => {
|
||||
describe('ordinary users', () => {
|
||||
it('throws authorization error', async () => {
|
||||
await expect(mutate({ mutation: pinPostMutation, variables })).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: { pinPost: null },
|
||||
})
|
||||
})
|
||||
@ -571,7 +571,7 @@ describe('pin posts', () => {
|
||||
|
||||
it('throws authorization error', async () => {
|
||||
await expect(mutate({ mutation: pinPostMutation, variables })).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: { pinPost: null },
|
||||
})
|
||||
})
|
||||
@ -854,7 +854,7 @@ describe('unpin posts', () => {
|
||||
it('throws authorization error', async () => {
|
||||
authenticatedUser = null
|
||||
await expect(mutate({ mutation: unpinPostMutation, variables })).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: { unpinPost: null },
|
||||
})
|
||||
})
|
||||
@ -863,7 +863,7 @@ describe('unpin posts', () => {
|
||||
describe('users cannot unpin posts', () => {
|
||||
it('throws authorization error', async () => {
|
||||
await expect(mutate({ mutation: unpinPostMutation, variables })).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: { unpinPost: null },
|
||||
})
|
||||
})
|
||||
@ -878,7 +878,7 @@ describe('unpin posts', () => {
|
||||
|
||||
it('throws authorization error', async () => {
|
||||
await expect(mutate({ mutation: unpinPostMutation, variables })).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: { unpinPost: null },
|
||||
})
|
||||
})
|
||||
@ -975,7 +975,7 @@ describe('DeletePost', () => {
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
const { errors } = await mutate({ mutation: deletePostMutation, variables })
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
@ -986,7 +986,7 @@ describe('DeletePost', () => {
|
||||
|
||||
it('throws authorization error', async () => {
|
||||
const { errors } = await mutate({ mutation: deletePostMutation, variables })
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
@ -1128,7 +1128,7 @@ describe('emotions', () => {
|
||||
variables,
|
||||
})
|
||||
|
||||
expect(addPostEmotions.errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(addPostEmotions.errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
@ -1249,7 +1249,7 @@ describe('emotions', () => {
|
||||
mutation: removePostEmotionsMutation,
|
||||
variables: removePostEmotionsVariables,
|
||||
})
|
||||
expect(removePostEmotions.errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(removePostEmotions.errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -61,7 +61,7 @@ describe('Signup', () => {
|
||||
CONFIG.INVITE_REGISTRATION = false
|
||||
CONFIG.PUBLIC_REGISTRATION = false
|
||||
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -130,7 +130,7 @@ describe('file a report on a resource', () => {
|
||||
authenticatedUser = null
|
||||
await expect(mutate({ mutation: fileReportMutation, variables })).resolves.toMatchObject({
|
||||
data: { fileReport: null },
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -729,7 +729,7 @@ describe('file a report on a resource', () => {
|
||||
authenticatedUser = null
|
||||
expect(query({ query: reportsQuery })).resolves.toMatchObject({
|
||||
data: { reports: null },
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -739,7 +739,7 @@ describe('file a report on a resource', () => {
|
||||
authenticatedUser = await currentUser.toJson()
|
||||
expect(query({ query: reportsQuery })).resolves.toMatchObject({
|
||||
data: { reports: null },
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -97,7 +97,7 @@ describe('rewards', () => {
|
||||
authenticatedUser = null
|
||||
await expect(mutate({ mutation: rewardMutation, variables })).resolves.toMatchObject({
|
||||
data: { reward: null },
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -255,7 +255,7 @@ describe('rewards', () => {
|
||||
it('throws authorization error', async () => {
|
||||
await expect(mutate({ mutation: rewardMutation, variables })).resolves.toMatchObject({
|
||||
data: { reward: null },
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -308,7 +308,7 @@ describe('rewards', () => {
|
||||
authenticatedUser = null
|
||||
await expect(mutate({ mutation: unrewardMutation, variables })).resolves.toMatchObject({
|
||||
data: { unreward: null },
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -341,7 +341,7 @@ describe('rewards', () => {
|
||||
it('throws authorization error', async () => {
|
||||
await expect(mutate({ mutation: unrewardMutation, variables })).resolves.toMatchObject({
|
||||
data: { unreward: null },
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -90,7 +90,7 @@ describe('shout and unshout posts', () => {
|
||||
variables = { id: 'post-to-shout-id' }
|
||||
authenticatedUser = undefined
|
||||
await expect(mutate({ mutation: mutationShoutPost, variables })).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -165,7 +165,7 @@ describe('shout and unshout posts', () => {
|
||||
authenticatedUser = undefined
|
||||
variables = { id: 'post-to-shout-id' }
|
||||
await expect(mutate({ mutation: mutationUnshoutPost, variables })).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -94,7 +94,7 @@ describe('SocialMedia', () => {
|
||||
const user = null
|
||||
const result = await socialMediaAction(user, mutation, variables)
|
||||
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
@ -186,7 +186,7 @@ describe('SocialMedia', () => {
|
||||
const user = null
|
||||
const result = await socialMediaAction(user, mutation, variables)
|
||||
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
@ -195,7 +195,7 @@ describe('SocialMedia', () => {
|
||||
const user = someUser
|
||||
const result = await socialMediaAction(user, mutation, variables)
|
||||
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
@ -222,7 +222,7 @@ describe('SocialMedia', () => {
|
||||
variables.id = 'some-id'
|
||||
const result = await socialMediaAction(user, mutation, variables)
|
||||
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -249,7 +249,7 @@ describe('SocialMedia', () => {
|
||||
const user = null
|
||||
const result = await socialMediaAction(user, mutation, variables)
|
||||
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
@ -258,7 +258,7 @@ describe('SocialMedia', () => {
|
||||
const user = someUser
|
||||
const result = await socialMediaAction(user, mutation, variables)
|
||||
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -20,16 +20,22 @@ export default {
|
||||
const result = await transaction.run(
|
||||
`
|
||||
MATCH (user:User {id: $id})
|
||||
WITH user, [(user)<-[:OWNED_BY]-(medium:SocialMedia) | properties(medium) ] as media
|
||||
RETURN user {.*, socialMedia: media } as user
|
||||
OPTIONAL MATCH (category:Category) WHERE NOT ((user)-[:NOT_INTERESTED_IN]->(category))
|
||||
OPTIONAL MATCH (cats:Category)
|
||||
WITH user, [(user)<-[:OWNED_BY]-(medium:SocialMedia) | properties(medium) ] AS media, category, toString(COUNT(cats)) AS categoryCount
|
||||
RETURN user {.*, socialMedia: media, activeCategories: collect(category.id) } AS user, categoryCount
|
||||
`,
|
||||
{ id: user.id },
|
||||
)
|
||||
log(result)
|
||||
return result.records.map((record) => record.get('user'))
|
||||
const [categoryCount] = result.records.map((record) => record.get('categoryCount'))
|
||||
const [currentUser] = result.records.map((record) => record.get('user'))
|
||||
// frontend expects empty array when all categories are selected
|
||||
if (currentUser.activeCategories.length === parseInt(categoryCount))
|
||||
currentUser.activeCategories = []
|
||||
return currentUser
|
||||
})
|
||||
try {
|
||||
const [currentUser] = await currentUserTransactionPromise
|
||||
const currentUser = await currentUserTransactionPromise
|
||||
return currentUser
|
||||
} finally {
|
||||
session.close()
|
||||
|
||||
@ -6,6 +6,7 @@ import { createTestClient } from 'apollo-server-testing'
|
||||
import createServer, { context } from '../../server'
|
||||
import encode from '../../jwt/encode'
|
||||
import { getNeode } from '../../db/neo4j'
|
||||
import { categories } from '../../constants/categories'
|
||||
|
||||
const neode = getNeode()
|
||||
let query, mutate, variables, req, user
|
||||
@ -118,6 +119,7 @@ describe('currentUser', () => {
|
||||
}
|
||||
email
|
||||
role
|
||||
activeCategories
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -172,6 +174,52 @@ describe('currentUser', () => {
|
||||
}
|
||||
await respondsWith(expected)
|
||||
})
|
||||
|
||||
describe('with categories in DB', () => {
|
||||
beforeEach(async () => {
|
||||
await Promise.all(
|
||||
categories.map(async ({ icon, name }, index) => {
|
||||
await Factory.build('category', {
|
||||
id: `cat${index + 1}`,
|
||||
slug: name,
|
||||
name,
|
||||
icon,
|
||||
})
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('returns empty array for all categories', async () => {
|
||||
await respondsWith({
|
||||
data: {
|
||||
currentUser: expect.objectContaining({ activeCategories: [] }),
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('with categories saved for current user', () => {
|
||||
const saveCategorySettings = gql`
|
||||
mutation ($activeCategories: [String]) {
|
||||
saveCategorySettings(activeCategories: $activeCategories)
|
||||
}
|
||||
`
|
||||
beforeEach(async () => {
|
||||
await mutate({
|
||||
mutation: saveCategorySettings,
|
||||
variables: { activeCategories: ['cat1', 'cat3', 'cat5', 'cat7'] },
|
||||
})
|
||||
})
|
||||
|
||||
it('returns only the saved active categories', async () => {
|
||||
const result = await query({ query: currentUserQuery, variables })
|
||||
expect(result.data.currentUser.activeCategories).toHaveLength(4)
|
||||
expect(result.data.currentUser.activeCategories).toContain('cat1')
|
||||
expect(result.data.currentUser.activeCategories).toContain('cat3')
|
||||
expect(result.data.currentUser.activeCategories).toContain('cat5')
|
||||
expect(result.data.currentUser.activeCategories).toContain('cat7')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -310,8 +358,8 @@ describe('change password', () => {
|
||||
})
|
||||
|
||||
describe('unauthenticated', () => {
|
||||
it('throws "Not Authorised!"', async () => {
|
||||
await respondsWith({ errors: [{ message: 'Not Authorised!' }] })
|
||||
it('throws "Not Authorized!"', async () => {
|
||||
await respondsWith({ errors: [{ message: 'Not Authorized!' }] })
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -269,6 +269,49 @@ export default {
|
||||
session.close()
|
||||
}
|
||||
},
|
||||
saveCategorySettings: async (object, args, context, resolveInfo) => {
|
||||
const { activeCategories } = args
|
||||
const {
|
||||
user: { id },
|
||||
} = context
|
||||
|
||||
const session = context.driver.session()
|
||||
await session.writeTransaction((transaction) => {
|
||||
return transaction.run(
|
||||
`
|
||||
MATCH (user:User { id: $id })-[previousCategories:NOT_INTERESTED_IN]->(category:Category)
|
||||
DELETE previousCategories
|
||||
RETURN user, category
|
||||
`,
|
||||
{ id },
|
||||
)
|
||||
})
|
||||
|
||||
// frontend gives [] when all categories are selected (default)
|
||||
if (activeCategories.length === 0) return true
|
||||
|
||||
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
||||
const saveCategorySettingsResponse = await transaction.run(
|
||||
`
|
||||
MATCH (category:Category) WHERE NOT category.id IN $activeCategories
|
||||
MATCH (user:User { id: $id })
|
||||
MERGE (user)-[r:NOT_INTERESTED_IN]->(category)
|
||||
RETURN user, r, category
|
||||
`,
|
||||
{ id, activeCategories },
|
||||
)
|
||||
const [user] = await saveCategorySettingsResponse.records.map((record) =>
|
||||
record.get('user'),
|
||||
)
|
||||
return user
|
||||
})
|
||||
try {
|
||||
await writeTxResultPromise
|
||||
return true
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
},
|
||||
},
|
||||
User: {
|
||||
email: async (parent, params, context, resolveInfo) => {
|
||||
|
||||
@ -3,6 +3,7 @@ import { gql } from '../../helpers/jest'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import createServer from '../../server'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import { categories } from '../../constants/categories'
|
||||
|
||||
const categoryIds = ['cat9']
|
||||
let user
|
||||
@ -45,7 +46,7 @@ const deleteUserMutation = gql`
|
||||
}
|
||||
`
|
||||
const switchUserRoleMutation = gql`
|
||||
mutation ($role: UserGroup!, $id: ID!) {
|
||||
mutation ($role: UserRole!, $id: ID!) {
|
||||
switchUserRole(role: $role, id: $id) {
|
||||
name
|
||||
role
|
||||
@ -56,6 +57,12 @@ const switchUserRoleMutation = gql`
|
||||
}
|
||||
`
|
||||
|
||||
const saveCategorySettings = gql`
|
||||
mutation ($activeCategories: [String]) {
|
||||
saveCategorySettings(activeCategories: $activeCategories)
|
||||
}
|
||||
`
|
||||
|
||||
beforeAll(async () => {
|
||||
await cleanDatabase()
|
||||
|
||||
@ -101,7 +108,7 @@ describe('User', () => {
|
||||
|
||||
it('is forbidden', async () => {
|
||||
await expect(query({ query: userQuery, variables })).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorised!' }],
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
|
||||
@ -207,7 +214,7 @@ describe('UpdateUser', () => {
|
||||
|
||||
it('is not allowed to change other user accounts', async () => {
|
||||
const { errors } = await mutate({ mutation: updateUserMutation, variables })
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
})
|
||||
|
||||
@ -500,7 +507,7 @@ describe('switch user role', () => {
|
||||
expect.objectContaining({
|
||||
errors: [
|
||||
expect.objectContaining({
|
||||
message: 'Not Authorised!',
|
||||
message: 'Not Authorized!',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
@ -544,3 +551,140 @@ describe('switch user role', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('save category settings', () => {
|
||||
beforeEach(async () => {
|
||||
await Promise.all(
|
||||
categories.map(({ icon, name }, index) => {
|
||||
Factory.build('category', {
|
||||
id: `cat${index + 1}`,
|
||||
slug: name,
|
||||
name,
|
||||
icon,
|
||||
})
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await Factory.build('user', {
|
||||
id: 'user',
|
||||
role: 'user',
|
||||
})
|
||||
variables = {
|
||||
activeCategories: ['cat1', 'cat3', 'cat5'],
|
||||
}
|
||||
})
|
||||
|
||||
describe('not authenticated', () => {
|
||||
beforeEach(async () => {
|
||||
authenticatedUser = undefined
|
||||
})
|
||||
|
||||
it('throws an error', async () => {
|
||||
await expect(mutate({ mutation: saveCategorySettings, variables })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [
|
||||
expect.objectContaining({
|
||||
message: 'Not Authorized!',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated', () => {
|
||||
beforeEach(async () => {
|
||||
authenticatedUser = await user.toJson()
|
||||
})
|
||||
|
||||
const userQuery = gql`
|
||||
query ($id: ID) {
|
||||
User(id: $id) {
|
||||
activeCategories
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
describe('no categories saved', () => {
|
||||
it('returns true for active categories mutation', async () => {
|
||||
await expect(mutate({ mutation: saveCategorySettings, variables })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: { saveCategorySettings: true },
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
describe('query for user', () => {
|
||||
beforeEach(async () => {
|
||||
await mutate({ mutation: saveCategorySettings, variables })
|
||||
})
|
||||
|
||||
it('returns the active categories when user is queried', async () => {
|
||||
await expect(
|
||||
query({ query: userQuery, variables: { id: authenticatedUser.id } }),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
User: [
|
||||
{
|
||||
activeCategories: expect.arrayContaining(['cat1', 'cat3', 'cat5']),
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('categories already saved', () => {
|
||||
beforeEach(async () => {
|
||||
variables = {
|
||||
activeCategories: ['cat1', 'cat3', 'cat5'],
|
||||
}
|
||||
await mutate({ mutation: saveCategorySettings, variables })
|
||||
variables = {
|
||||
activeCategories: ['cat10', 'cat11', 'cat12', 'cat8', 'cat9'],
|
||||
}
|
||||
})
|
||||
|
||||
it('returns true', async () => {
|
||||
await expect(mutate({ mutation: saveCategorySettings, variables })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: { saveCategorySettings: true },
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
describe('query for user', () => {
|
||||
beforeEach(async () => {
|
||||
await mutate({ mutation: saveCategorySettings, variables })
|
||||
})
|
||||
|
||||
it('returns the new active categories when user is queried', async () => {
|
||||
await expect(
|
||||
query({ query: userQuery, variables: { id: authenticatedUser.id } }),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
User: [
|
||||
{
|
||||
activeCategories: expect.arrayContaining([
|
||||
'cat10',
|
||||
'cat11',
|
||||
'cat12',
|
||||
'cat8',
|
||||
'cat9',
|
||||
]),
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -121,7 +121,7 @@ describe('Location Service', () => {
|
||||
const result = await query({ query: queryLocations, variables })
|
||||
expect(result.data.queryLocations).toEqual(
|
||||
expect.arrayContaining([
|
||||
{ id: 'place.14094307404564380', place_name: 'Berlin, Germany' },
|
||||
{ id: expect.stringMatching(/^place\.[0-9]+$/), place_name: 'Berlin, Germany' },
|
||||
{
|
||||
id: expect.stringMatching(/^place\.[0-9]+$/),
|
||||
place_name: 'Berlin, Maryland, United States',
|
||||
|
||||
@ -58,7 +58,7 @@ describe('mutedUsers', () => {
|
||||
it('throws permission error', async () => {
|
||||
const { query } = createTestClient(server)
|
||||
const result = await query({ query: mutedUserQuery })
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
|
||||
describe('authenticated and given a muted user', () => {
|
||||
@ -116,7 +116,7 @@ describe('muteUser', () => {
|
||||
|
||||
it('throws permission error', async () => {
|
||||
const result = await muteAction({ id: 'u2' })
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
|
||||
describe('authenticated', () => {
|
||||
@ -333,7 +333,7 @@ describe('unmuteUser', () => {
|
||||
|
||||
it('throws permission error', async () => {
|
||||
const result = await unmuteAction({ id: 'u2' })
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!')
|
||||
})
|
||||
|
||||
describe('authenticated', () => {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
enum UserGroup {
|
||||
enum UserRole {
|
||||
admin
|
||||
moderator
|
||||
user
|
||||
@ -28,7 +28,7 @@ type User {
|
||||
avatar: Image @relation(name: "AVATAR_IMAGE", direction: "OUT")
|
||||
deleted: Boolean
|
||||
disabled: Boolean
|
||||
role: UserGroup!
|
||||
role: UserRole!
|
||||
publicKey: String
|
||||
invitedBy: User @relation(name: "INVITED", direction: "IN")
|
||||
invited: [User] @relation(name: "INVITED", direction: "OUT")
|
||||
@ -114,6 +114,14 @@ type User {
|
||||
badgesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)")
|
||||
|
||||
emotions: [EMOTED]
|
||||
|
||||
activeCategories: [String] @cypher(
|
||||
statement: """
|
||||
MATCH (category:Category)
|
||||
WHERE NOT ((this)-[:NOT_INTERESTED_IN]->(category))
|
||||
RETURN collect(category.id)
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -151,7 +159,7 @@ input _UserFilter {
|
||||
followedBy_none: _UserFilter
|
||||
followedBy_single: _UserFilter
|
||||
followedBy_every: _UserFilter
|
||||
role_in: [UserGroup!]
|
||||
role_in: [UserRole!]
|
||||
}
|
||||
|
||||
type Query {
|
||||
@ -160,7 +168,7 @@ type Query {
|
||||
email: String # admins need to search for a user sometimes
|
||||
name: String
|
||||
slug: String
|
||||
role: UserGroup
|
||||
role: UserRole
|
||||
locationName: String
|
||||
about: String
|
||||
createdAt: String
|
||||
@ -171,7 +179,7 @@ type Query {
|
||||
filter: _UserFilter
|
||||
): [User]
|
||||
|
||||
availableRoles: [UserGroup]!
|
||||
availableRoles: [UserRole]!
|
||||
mutedUsers: [User]
|
||||
blockedUsers: [User]
|
||||
isLoggedIn: Boolean!
|
||||
@ -219,5 +227,7 @@ type Mutation {
|
||||
blockUser(id: ID!): User
|
||||
unblockUser(id: ID!): User
|
||||
|
||||
switchUserRole(role: UserGroup!, id: ID!): User
|
||||
switchUserRole(role: UserRole!, id: ID!): User
|
||||
|
||||
saveCategorySettings(activeCategories: [String]): Boolean
|
||||
}
|
||||
|
||||
@ -4253,10 +4253,10 @@ eslint-plugin-node@~11.1.0:
|
||||
resolve "^1.10.1"
|
||||
semver "^6.1.0"
|
||||
|
||||
eslint-plugin-prettier@~3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz#432e5a667666ab84ce72f945c72f77d996a5c9ba"
|
||||
integrity sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA==
|
||||
eslint-plugin-prettier@~3.4.1:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz#e9ddb200efb6f3d05ffe83b1665a716af4a387e5"
|
||||
integrity sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==
|
||||
dependencies:
|
||||
prettier-linter-helpers "^1.0.0"
|
||||
|
||||
@ -7574,10 +7574,10 @@ neo4j-graphql-js@^2.11.5:
|
||||
lodash "^4.17.15"
|
||||
neo4j-driver "^4.0.1"
|
||||
|
||||
neode@^0.4.7:
|
||||
version "0.4.7"
|
||||
resolved "https://registry.yarnpkg.com/neode/-/neode-0.4.7.tgz#033007b57a2ee167e9ee5537493086db08d005eb"
|
||||
integrity sha512-YXlc187JRpeKCBcUIkY6nimXXG+Tvlopfe71/FPno2THrwmYt5mm0RPHZ+mXF2O1Xg6zvjKvOpCpDz2vHBfroQ==
|
||||
neode@^0.4.8:
|
||||
version "0.4.8"
|
||||
resolved "https://registry.yarnpkg.com/neode/-/neode-0.4.8.tgz#0889b4fc7f1bf0b470b01fa5b8870373b5d47ad6"
|
||||
integrity sha512-pb91NfCOg4Fj5o+98H+S2XYC+ByQfbdhwcc1UVuzuUQ0Ezzj+jWz8NmKWU8ZfCH6l4plk71yDAPd2eTwpt+Xvg==
|
||||
dependencies:
|
||||
"@hapi/joi" "^15.1.1"
|
||||
dotenv "^4.0.0"
|
||||
@ -9262,10 +9262,10 @@ slug@^0.9.2:
|
||||
dependencies:
|
||||
unicode ">= 0.3.1"
|
||||
|
||||
slug@~4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/slug/-/slug-4.0.2.tgz#35a62b4e71582778ac08bb30a1bf439fd0a43ea7"
|
||||
integrity sha512-c5XbWkwxHU13gAdSvBHQgnGy2sxv/REMz0ugcM0SOSBCO/N4wfU0TDBC3pgdOwVGjZwGnLBTRljXzdVYE+KYNw==
|
||||
slug@~6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/slug/-/slug-6.0.0.tgz#39637b32e5a873bc692812a630842880499ed6c9"
|
||||
integrity sha512-0MpNLyCSUSf0G1nAZmp9gY1cvesPP35a1Live25vZ23gWQ5SAopF0N+0hk9KI4ytNuTebJrHGNrgTnxboofcSg==
|
||||
|
||||
smart-buffer@^4.1.0:
|
||||
version "4.1.0"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ocelot-social",
|
||||
"version": "1.0.9",
|
||||
"version": "1.1.1",
|
||||
"description": "Free and open source software program code available to run social networks.",
|
||||
"author": "ocelot.social Community",
|
||||
"license": "MIT",
|
||||
@ -42,10 +42,10 @@
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"mock-socket": "^9.0.3",
|
||||
"neo4j-driver": "^4.3.4",
|
||||
"neode": "^0.4.7",
|
||||
"neode": "^0.4.8",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"rosie": "^2.1.0",
|
||||
"slug": "^5.1.0"
|
||||
"slug": "^6.0.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"set-value": "^2.0.1"
|
||||
|
||||
@ -4,3 +4,4 @@ PUBLIC_REGISTRATION=false
|
||||
INVITE_REGISTRATION=true
|
||||
WEBSOCKETS_URI=ws://localhost:3000/api/graphql
|
||||
GRAPHQL_URI=http://localhost:4000/
|
||||
CATEGORIES_ACTIVE=false
|
||||
5
webapp/assets/_new/icons/svgs/child.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>child</title>
|
||||
<path d="M12 3c2.202 0 3.791 1.007 4.531 2.313 0.026-0.041 0.034-0.084 0.063-0.125 0.453-0.641 1.315-1.188 2.406-1.188v2c-0.453 0-0.588 0.111-0.719 0.281 3.845 0.921 6.812 4.105 7.563 8.063 1.193 0.397 2.156 1.337 2.156 2.656 0 1.365-1.024 2.33-2.281 2.688-0.816 4.701-4.82 8.313-9.719 8.313s-8.903-3.611-9.719-8.313c-1.257-0.357-2.281-1.323-2.281-2.688s1.024-2.33 2.281-2.688c0.741-4.271 4.122-7.637 8.406-8.219-0.39-0.574-1.192-1.094-2.688-1.094v-2zM16 8c-4.093 0-7.461 3.121-7.906 7.125l-0.094 0.875h-1c-0.555 0-1 0.445-1 1s0.445 1 1 1h1l0.094 0.875c0.445 4.004 3.813 7.125 7.906 7.125s7.461-3.121 7.906-7.125l0.094-0.875h1c0.555 0 1-0.445 1-1s-0.445-1-1-1h-0.875l-0.125-0.875c-0.536-4.019-3.907-7.125-8-7.125zM12.5 16c0.828 0 1.5 0.672 1.5 1.5s-0.672 1.5-1.5 1.5-1.5-0.672-1.5-1.5 0.672-1.5 1.5-1.5zM19.5 16c0.828 0 1.5 0.672 1.5 1.5s-0.672 1.5-1.5 1.5-1.5-0.672-1.5-1.5 0.672-1.5 1.5-1.5z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
20
webapp/assets/_new/icons/svgs/culture.svg
Normal file
@ -0,0 +1,20 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<path d="M5.9,25.1c-0.3,0.3-0.3,0.8,0,1.1s0.8,0.3,1.1,0c1.7-1.7,4.5-1.7,6.3,0c0.1,0.1,0.3,0.2,0.5,0.2s0.4-0.1,0.5-0.2
|
||||
c0.3-0.3,0.3-0.8,0-1.1C12,22.8,8.2,22.8,5.9,25.1z"/>
|
||||
<path d="M24.4,8.7c0.7-0.7,2-0.7,2.7,0C27.2,8.9,27.4,9,27.6,9c0.2,0,0.4-0.1,0.5-0.2c0.3-0.3,0.3-0.8,0-1.1
|
||||
c-1.3-1.3-3.5-1.3-4.8,0C23,8,23,8.5,23.3,8.7C23.6,9,24.1,9,24.4,8.7z"/>
|
||||
<path d="M16.4,7.7c-0.3,0.3-0.3,0.8,0,1.1c0.3,0.3,0.8,0.3,1.1,0c0.7-0.7,1.9-0.7,2.7,0C20.3,8.9,20.5,9,20.7,9s0.4-0.1,0.5-0.2
|
||||
c0.3-0.3,0.3-0.8,0-1.1C19.9,6.4,17.7,6.4,16.4,7.7z"/>
|
||||
<path d="M31.4,0.8c-0.2-0.1-0.5-0.2-0.7-0.1c-2,0.8-5.1,1.2-8.4,1.2s-6.4-0.4-8.4-1.2c-0.2-0.1-0.5-0.1-0.7,0.1
|
||||
c-0.2,0.1-0.3,0.4-0.3,0.6v9.9c-1,0.1-1.9,0.1-3,0.1c-3.3,0-6.4-0.4-8.4-1.2c-0.2-0.1-0.5-0.1-0.7,0.1c-0.2,0.1-0.3,0.4-0.3,0.6
|
||||
l0,11.5c0,5.2,4.2,9.4,9.4,9.4s9.4-4.2,9.4-9.4V22c0.7,0.3,1.6,0.4,3,0.4c5.2,0,9.4-4.2,9.4-9.4V1.4C31.7,1.2,31.6,0.9,31.4,0.8z
|
||||
M9.9,30.4c-4.4,0-7.9-3.6-7.9-7.9V12c2.1,0.6,4.9,1,7.9,1c2.7,0,5.2-0.3,7.3-0.8l0.5-0.1c0,1.6,0,2.9,0,4c0,1.3,0,2.4,0.1,3.2v3.2
|
||||
C17.8,26.8,14.2,30.4,9.9,30.4z M30.2,13c0,4.4-3.6,7.9-7.9,7.9c-2.1,0-2.8,0-3-1.7v-2.8c0.9,0.5,1.9,0.8,3,0.8
|
||||
c1.6,0,3.1-0.6,4.2-1.7c0.3-0.3,0.3-0.8,0-1.1s-0.8-0.3-1.1,0c-0.8,0.8-2,1.3-3.1,1.3c-1.1,0-2.1-0.4-3-1.2v-3.6
|
||||
c0-0.2-0.1-0.5-0.3-0.6c-0.2-0.1-0.5-0.2-0.7-0.1c-0.4,0.2-0.9,0.3-1.4,0.4l-2.5,0.4V2.5c2.1,0.6,4.9,1,7.9,1c3,0,5.8-0.3,7.9-1V13
|
||||
z"/>
|
||||
<path d="M10.9,17.7c-0.3,0.3-0.3,0.8,0,1.1s0.8,0.3,1.1,0c0.7-0.7,2-0.7,2.7,0c0.1,0.1,0.3,0.2,0.5,0.2s0.4-0.1,0.5-0.2
|
||||
c0.3-0.3,0.3-0.8,0-1.1C14.4,16.4,12.2,16.4,10.9,17.7z"/>
|
||||
<path d="M7.7,18.7C7.9,18.9,8.1,19,8.3,19s0.4-0.1,0.5-0.2c0.3-0.3,0.3-0.8,0-1.1c-1.3-1.3-3.5-1.3-4.8,0c-0.3,0.3-0.3,0.8,0,1.1
|
||||
s0.8,0.3,1.1,0C5.8,18,7,18,7.7,18.7z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
5
webapp/assets/_new/icons/svgs/desktop.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>desktop</title>
|
||||
<path d="M2 6h28v18h-13v2h5v2h-12v-2h5v-2h-13v-18zM4 8v14h24v-14h-24z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 240 B |
5
webapp/assets/_new/icons/svgs/ellipsis-h.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>ellipsis-h</title>
|
||||
<path d="M6 14c1.105 0 2 0.895 2 2s-0.895 2-2 2-2-0.895-2-2 0.895-2 2-2zM16 14c1.105 0 2 0.895 2 2s-0.895 2-2 2-2-0.895-2-2 0.895-2 2-2zM26 14c1.105 0 2 0.895 2 2s-0.895 2-2 2-2-0.895-2-2 0.895-2 2-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 374 B |
14
webapp/assets/_new/icons/svgs/energy.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<path d="M27.6,19.7L26.8,19c-3-2.8-4.8-4.4-5.2-4.6c-0.9-0.6-2.9-0.5-3.6-0.4c-0.2-0.5-0.5-0.9-0.9-1.2V2.2
|
||||
c0-1-0.7-1.6-1.3-1.6c-0.6-0.1-1.4,0.4-1.6,1.4c-0.1,0.5-0.3,1.4-0.5,2.4c-0.6,2.9-1,5-1,5.4c-0.1,1.1,1.1,2.8,1.5,3.3
|
||||
c-0.3,0.4-0.5,0.9-0.5,1.4c0,0,0,0,0,0.1c-0.9,0.6-7.3,5-8.7,6c-0.8,0.6-0.9,1.4-0.6,2c0.2,0.4,0.7,0.7,1.2,0.7
|
||||
c0.3,0,0.5-0.1,0.8-0.2c1.5-0.6,6.7-2.9,7.4-3.2c0.2-0.1,0.4-0.3,0.6-0.5l-1.3,11c0,1,2.3,1,2.8,1s2.8,0,2.8-1.1l-1.4-13.5
|
||||
c1.8,1.1,7.2,4.4,8.6,5.2c0.3,0.2,0.7,0.3,0.9,0.3c0.5,0,0.9-0.2,1.1-0.5C28.5,21.4,28.5,20.5,27.6,19.7z M14.8,14.6
|
||||
c0-0.8,0.6-1.2,1.2-1.2c0.6,0,1.2,0.6,1.2,1.2c0,0.8-0.6,1.2-1.2,1.2C15.2,15.8,14.8,15.2,14.8,14.6z M14.8,4.7c0.2-1,0.4-2,0.5-2.5
|
||||
c0.1-0.5,0.3-0.7,0.5-0.6c0.2,0,0.4,0.2,0.4,0.6v10.2c-0.1,0-0.1,0-0.2,0c-0.3,0-0.6,0.1-0.9,0.2c-0.6-0.8-1.4-2-1.3-2.7
|
||||
C13.8,9.5,14.4,6.5,14.8,4.7z M13.5,19.1c-0.6,0.3-5.8,2.6-7.3,3.2c-0.4,0.2-0.7,0.1-0.8-0.1c-0.1-0.2,0-0.4,0.3-0.7
|
||||
c1.3-0.9,6.9-4.8,8.4-5.8c0.2,0.3,0.5,0.6,0.8,0.8C14.6,17.4,13.9,18.8,13.5,19.1z M16,30.4c-0.6,0-1.4-0.1-1.8-0.2l1.6-13.4
|
||||
c0,0,0.1,0,0.1,0c0.1,0,0.3,0,0.4,0l1.4,13.4C17.4,30.3,16.6,30.4,16,30.4z M27.3,21.3c-0.1,0.1-0.4,0.2-0.7,0
|
||||
c-1.5-0.9-7.2-4.4-8.8-5.3c0.2-0.3,0.3-0.6,0.4-0.9c1-0.1,2.5-0.1,3,0.3c0.4,0.2,3.6,3.2,5,4.5l0.8,0.7
|
||||
C27.3,20.8,27.4,21.1,27.3,21.3z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
13
webapp/assets/_new/icons/svgs/finance.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<rect x="8.1" y="13.3" width="1.5" height="1.9"/>
|
||||
<path d="M31.9,17.9L31.9,17.9l0-0.6c-0.2-5.5-4.1-10.3-9.5-11.6c-0.2,0-0.8-0.2-0.8-0.2c-3.1-0.6-6.3-0.5-9.5,0.1
|
||||
c-0.3,0.1-0.6,0.1-0.9,0.2C10.6,5.7,10,5.4,9.7,5.4L9.5,5.3C8.7,5,7.6,4.6,6.8,5.1C6,5.7,6,6.7,6.2,7.3l0.3,1.2
|
||||
C5.1,9.7,4,11.2,3.3,12.8c-0.4,0.8-0.8,0.8-1.2,0.8c-0.1,0-0.2,0-0.3,0H0l0,5.5l0.6,0.1c0,0,1.1,0.2,1.6,1C2.5,20.7,2.7,21,3,22.3
|
||||
c1.1,3.4,3.6,5.3,4.6,5.9l0,3.9H12l2.6-3.4h6.1l1.7,3.4h4.9v-4.6l0.4-0.4c2.5-2.2,3.9-5.2,4.1-8.5l0-0.4
|
||||
C31.9,18.1,31.9,18,31.9,17.9z M26.7,25.9l-0.9,0.9v3.8h-2.5l-1.7-3.4h-7.7l-2.6,3.4H9.1l0-3.2l-0.4-0.2c0,0-3.1-1.7-4.2-5.3
|
||||
c-0.4-1.4-0.6-1.8-1-2.3c-0.5-0.9-1.4-1.3-2-1.5v-2.8l0.6,0c0.5,0,1.8,0,2.6-1.7c0.5-1.2,1.3-2.3,2.2-3.3c0.8-0.8,1.3-1,1.1-1.5
|
||||
l0,0L7.6,7c-0.1-0.2-0.1-0.5,0-0.6c0.2-0.1,1,0.1,1.4,0.3l0.3,0.1c0.3,0.1,0.9,0.4,1.7,0.6l0.2,0.1l0.3-0.1c0.3-0.1,0.7-0.2,1-0.2
|
||||
c3-0.6,5.9-0.6,8.9-0.1c0,0,0.6,0.1,0.8,0.1c4.7,1.1,8.2,5.3,8.4,10.1l0,0.8c-0.1,1.3-1.2,2.4-2.5,2.4c-1,0-1.9-0.9-1.9-2
|
||||
c0-0.8,0.6-1.4,1.4-1.4v-1.5c-1.6,0-2.9,1.3-2.9,2.9c0,1.9,1.5,3.5,3.4,3.5c0.7,0,1.3-0.2,1.8-0.5C29.1,23.2,28.1,24.7,26.7,25.9z"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
7
webapp/assets/_new/icons/svgs/health.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<path d="M26.1,20.7c-2.9,5.1-9.3,8.8-10.8,9.6c-2.1-1.1-13.7-7.8-13.7-16.1c0-3.6,2.8-6.5,6.5-6.5c2.6,0,4.9,1.5,5.9,3.9l0.7,1.7
|
||||
l0.7-1.7c1-2.4,3.3-3.9,5.9-3.9c3.6,0,6.5,2.8,6.5,6.5V15h1.5v-0.8c0-4.5-3.5-8-8-8c-2.7,0-5.2,1.3-6.6,3.5
|
||||
c-1.4-2.2-3.9-3.5-6.6-3.5c-4.5,0-8,3.5-8,8c0,10,14.3,17.3,14.9,17.6l0.3,0.2l0.3-0.2c0.3-0.2,8.3-4.2,11.8-10.4l0.4-0.7L26.5,20
|
||||
L26.1,20.7z"/>
|
||||
<polygon points="22.9,17 19.5,21.2 17,14.5 11.6,19.9 12.6,20.9 16.4,17.2 19,24.2 23.6,18.5 31.9,18.5 31.9,17 "/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 625 B |
5
webapp/assets/_new/icons/svgs/home.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>home</title>
|
||||
<path d="M16 2.594l0.719 0.688 13 13-1.438 1.438-1.281-1.281v11.563h-9v-10h-4v10h-9v-11.563l-1.281 1.281-1.438-1.438 13-13zM16 5.438l-9 9v11.563h5v-10h8v10h5v-11.563z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 334 B |
5
webapp/assets/_new/icons/svgs/lightbulb.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>lightbulb-o</title>
|
||||
<path d="M6.813 2.406l2.094 2.094-1.406 1.406-2.094-2.094zM25.188 2.406l1.406 1.406-2.094 2.094-1.406-1.406zM16 3.031c4.934-0.047 9 4.027 9 8.969 0 2.706-1.249 5.062-2.906 6.719-1.238 1.15-2 2.627-2 4.094v1.188h-0.094v4h-2.281c-0.347 0.597-0.982 1-1.719 1s-1.372-0.403-1.719-1h-2.281v-6c-0.203-1.117-0.794-2.212-1.75-3.031-2.233-1.898-3.573-4.845-3.125-8.094 0.561-4.039 3.789-7.316 7.844-7.781 0.011-0.001 0.020 0.001 0.031 0 0.336-0.041 0.671-0.059 1-0.063zM16 5.031c-0.258 0.004-0.518 0.030-0.781 0.063-3.131 0.348-5.687 2.881-6.125 6.031-0.352 2.552 0.702 4.811 2.469 6.313 1.388 1.19 2.124 2.848 2.344 4.563h4.375c0.236-1.792 1.094-3.434 2.438-4.688l-0.031-0.031c1.343-1.343 2.313-3.187 2.313-5.281 0-3.861-3.135-7.024-7-6.969zM2 12h3v2h-3v-2zM27 12h3v2h-3v-2zM7.5 20.094l1.406 1.406-2.094 2.094-1.406-1.406zM24.5 20.094l2.094 2.094-1.406 1.406-2.094-2.094zM14 24v2h4v-2h-4z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
7
webapp/assets/_new/icons/svgs/media.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<path d="M31.7,29.2L29.8,23V7.1c0-1.1-1-2.1-2.1-2.1H4.8c-1.1,0-2.1,1-2.1,2.1V23l-2,6.1c-0.2,0.6-0.1,1.3,0.2,1.9
|
||||
c0.4,0.6,1,0.9,1.7,0.9h27.1c0.7,0,1.3-0.3,1.7-0.9C31.8,30.4,31.9,29.7,31.7,29.2z M4.2,7.1c0-0.3,0.3-0.6,0.6-0.6h22.8
|
||||
c0.3,0,0.6,0.3,0.6,0.6v15.3h-24V7.1z M30.2,30.2c-0.1,0.1-0.2,0.2-0.5,0.2H2.6c-0.3,0-0.4-0.2-0.5-0.2C2.1,30,2,29.8,2.1,29.6
|
||||
L4,23.9h24.4l1.8,5.8C30.4,29.8,30.3,30,30.2,30.2z"/>
|
||||
<rect x="13.5" y="27.8" width="5.6" height="1.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 596 B |
13
webapp/assets/_new/icons/svgs/miscellaneous.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<path d="M7.5,4.9c0.1-0.1,0.2-0.2,0.3-0.2l0.7-0.6l-1-1.4L6.8,3.1C6.7,3.3,6.6,3.4,6.5,3.5L5.8,4.1l1,1.3L7.5,4.9z"/>
|
||||
<path d="M31.5,19.2c0.2-1,0.3-2.1,0.3-3.2c0-8.6-7-15.8-15.8-15.8c-1.7,0-3.3,0.2-5,0.8l-0.8,0.2l0.6,1.6l0.8-0.2
|
||||
c1.5-0.5,3-0.7,4.5-0.7c3.6,0,6.9,1.4,9.4,3.7C22.7,6,21.3,7.3,21,9.7c-0.2,1.3,0.6,2.4,1.4,3.3c0.3,0.5,1,1.3,0.9,1.5
|
||||
c0,0.2-0.2,0.3-0.6,0.6c-0.8,0.6-2,1.3-1.7,3.6c0.2,1.6,0.9,2.8,2.2,3.1c0.2,0.1,0.6,0.1,0.8,0.1c1,0,2.1-0.6,2.9-1.5
|
||||
c0.8-1,0.8-1,2-0.5c0.2,0.1,0.4,0.2,0.6,0.3c-1.8,5.7-7.1,9.8-13.4,9.8c-1.7,0-3.4-0.3-4.9-0.9l-0.1-0.1c-0.7-1.8,1-4.3,2.9-5.9
|
||||
c2.1-1.8,2.9-4.6,2.1-7.1c-0.5-1.2-1.4-2.1-2.7-2.3C12,13.5,10.6,14,9.7,15c-1,1.3-1.8,2-3,2c-1.3-0.1-3.3-2-4.3-3.1l-0.2-0.3
|
||||
c0.4-2.1,1.2-4,2.4-5.7L5,7.2l-1.4-1L3.1,6.8c-1.8,2.8-2.9,5.9-2.9,9.2c0,8.6,7.1,15.8,15.9,15.8c7.3,0,13.5-5.1,15.2-11.9
|
||||
L31.5,19.2L31.5,19.2z M2,16.1c0,0,0-0.1,0-0.1c1.1,1.1,2.9,2.6,4.5,2.6c2.1,0.1,3.3-1.3,4.5-2.5c0.5-0.6,1.3-0.8,2.1-0.7
|
||||
c0.3,0.1,1,0.3,1.3,1.2c0.6,1.8,0,4-1.5,5.3c-0.9,0.9-3.7,3.6-3.5,6.5C5,26,2,21.3,2,16.1z M29.6,18.4c-1.7-0.9-2.7-1-4.1,0.9
|
||||
c-0.6,0.7-1.3,1-1.8,0.9s-0.8-0.7-0.9-1.6c-0.1-1.3,0.3-1.5,0.9-2c0.5-0.3,1-0.7,1.3-1.5c0.3-1.2-0.5-2.2-1.2-3.1
|
||||
c-0.5-0.7-1.2-1.5-1-2.1c0.1-1,0.3-2.6,4.3-2.7c1.9,2.4,3.1,5.5,3.1,8.8c0,0.8-0.1,1.6-0.2,2.4C29.8,18.4,29.7,18.4,29.6,18.4z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
11
webapp/assets/_new/icons/svgs/mobility.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<path d="M31.8,20c0-6.8-5.6-9.1-8.2-9.1h-3.2c-0.3-1.6-1.7-2.9-3.5-2.9h-2.2v1.1h-3.4v1.5h3.4v2.1h-3.4v1.5h3.4v0.9h2.2
|
||||
c1.7,0,3.1-1.2,3.4-2.7h3.3c1.3,0,6.7,1.3,6.7,7.6c0,6.9-7.2,7.2-7.2,7.2h-0.8h-0.1V28c0,1.3-1.1,2.4-2.5,2.4
|
||||
c-1.4,0-2.5-1.1-2.5-2.4v-0.8H8.6V28c0,1.3-1.1,2.4-2.4,2.4c-1.5,0-2.4-1.2-2.4-2.4v-0.8H1.8v-2.6c0-0.1,0-0.8,0.5-1
|
||||
c0.5-0.1,1.3-0.4,2.2-0.7c0.5-0.2,1.1-0.4,1.2-0.4c0.4,0,0.8-0.3,1.4-1l0.1-0.1c0.1-0.1,0.5-0.7,0.9-1.1c0.8-0.9,1-1.2,1.1-1.4
|
||||
c0.1-0.1,0.5-0.4,1-0.4h8.6c0.6,0,0.8,0.2,1,0.4c0.4,0.5,1.9,2.5,1.9,2.5l1.2-0.9c0,0-1.5-2-1.9-2.5c-0.5-0.7-1.2-1-2.2-1h-8.6
|
||||
c-1,0-2,0.6-2.2,1.1c-0.1,0.1-0.6,0.8-1,1.2c-0.5,0.6-0.8,1-0.9,1.2c-0.2,0.2-0.5,0.4-0.6,0.6c-0.2,0-0.5,0.2-1.5,0.5
|
||||
c-0.7,0.3-1.6,0.6-2,0.7c-1,0.3-1.7,1.2-1.7,2.4v4.1h2.2c0.3,1.8,1.9,3.2,3.8,3.2c1.9,0,3.5-1.4,3.9-3.2h5.7
|
||||
c0.3,1.8,1.9,3.2,3.9,3.2c1.9,0,3.5-1.4,3.9-3.2C26.2,28.5,31.8,26.6,31.8,20z M16.9,13.6h-0.8V9.6h0.8c1.1,0,2.1,0.9,2.1,2
|
||||
S18,13.6,16.9,13.6z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
19
webapp/assets/_new/icons/svgs/movement.svg
Normal file
@ -0,0 +1,19 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<path d="M16,9.9c-2.7,0-4.8-2.2-4.8-4.8s2.2-4.8,4.8-4.8c2.7,0,4.8,2.2,4.8,4.8S18.6,9.9,16,9.9z M16,1.8c-1.8,0-3.3,1.5-3.3,3.3
|
||||
s1.5,3.3,3.3,3.3c1.8,0,3.3-1.5,3.3-3.3S17.8,1.8,16,1.8z"/>
|
||||
<path d="M19.6,32c-3.3,0-5.4-0.1-6-0.3c-2.6-1.1-2.6-2.5-2.4-3.1c0.5-2,4.1-3.1,8.7-2.6l0.3,0c0.4,0,0.8,0.3,0.8,0.8
|
||||
s-0.3,0.8-0.8,0.8h-0.3c-4.7-0.4-7,0.8-7.1,1.5c-0.1,0.3,0.4,0.9,1.5,1.4c0.8,0.3,7.5,0.2,10.1,0.2c0.7,0,1.3,0,1.7,0
|
||||
c0.6,0,1.3-0.5,1.3-1.3c0-1.6-2-3.1-3.6-4.2c-0.5-0.3-0.9-0.7-1.3-1c-0.2-0.2-0.4-0.4-0.7-0.6c-0.9-0.8-1.9-1.5-2-2.5
|
||||
c-0.1-0.5,0-1.1,0.2-1.8c0.2-0.6,0.3-1.2,0.3-2c0-0.4,0.3-0.7,0.6-0.7c0.4-0.1,0.7,0.1,0.8,0.5c0.9,2.3,1.4,2.7,2.7,3.9
|
||||
c0.6,0.5,1.3,1.1,2.4,2.1c0.5,0.5,1.2,0.5,1.6,0c0.2-0.2,0.3-0.5,0.3-0.8s-0.1-0.5-0.3-0.8l-2.9-2.8c-1.3-1.2-1.9-2.8-2.5-4
|
||||
c-0.7-1.7-1.1-2.5-2.1-2.5h-9c-0.9,0-1.3,0.7-2.1,2.5c-0.5,1.2-1.2,2.8-2.5,4l-2.9,2.8c-0.2,0.2-0.3,0.5-0.3,0.8
|
||||
c0,0.3,0.1,0.5,0.3,0.8c0.5,0.5,1.2,0.5,1.6,0c0.5-0.5,1-1,1.6-1.5c1.6-1.3,3.2-2.7,3.5-4.4c0.1-0.4,0.4-0.6,0.8-0.6
|
||||
c0.4,0,0.7,0.4,0.7,0.7c0,0.8,0.2,1.4,0.3,2c0.2,0.6,0.3,1.3,0.2,1.9c-0.2,0.9-1.2,1.7-2.1,2.5c-0.2,0.2-0.4,0.3-0.5,0.5
|
||||
c-0.4,0.3-0.8,0.7-1.3,1c-1.6,1.2-3.6,2.7-3.6,4.2c0,0.6,0.5,1.3,1.3,1.3h4c0.4,0,0.8,0.3,0.8,0.8S11.3,32,10.9,32h-4
|
||||
c-1.5,0-2.8-1.3-2.8-2.8c0-2.4,2.3-4.1,4.2-5.5c0.4-0.3,0.9-0.6,1.2-0.9c0.2-0.1,0.3-0.3,0.5-0.5c0.7-0.6,1.5-1.3,1.6-1.7
|
||||
c0.1-0.2,0-0.6-0.1-1c-0.8,1.1-1.9,2-2.9,2.9c-0.6,0.5-1.1,0.9-1.5,1.4c-1.1,1.1-2.7,1.1-3.7,0c-0.5-0.5-0.8-1.2-0.8-1.8
|
||||
s0.3-1.3,0.8-1.8l2.9-2.8c1.1-1,1.6-2.3,2.1-3.5c0.7-1.7,1.4-3.4,3.5-3.4h9c2,0,2.7,1.7,3.5,3.4c0.5,1.2,1.1,2.5,2.1,3.5l2.9,2.8
|
||||
c0.5,0.5,0.8,1.2,0.8,1.8s-0.3,1.3-0.8,1.8c-1.1,1.1-2.7,1-3.7,0c-1-0.9-1.7-1.5-2.3-2c-0.9-0.8-1.5-1.3-2.1-2.3
|
||||
c-0.1,0.4-0.2,0.8-0.2,1c0.1,0.5,0.9,1.1,1.5,1.6c0.2,0.2,0.5,0.4,0.7,0.6c0.3,0.3,0.8,0.6,1.2,0.9c1.9,1.4,4.2,3.1,4.2,5.5
|
||||
c0,1.5-1.3,2.8-2.8,2.8c-0.3,0-0.9,0-1.7,0C22.5,32,20.9,32,19.6,32z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
5
webapp/assets/_new/icons/svgs/music.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>music</title>
|
||||
<path d="M27 3.781v17.219c0 2.197-1.803 4-4 4s-4-1.803-4-4 1.803-4 4-4c0.732 0 1.407 0.214 2 0.563v-7.375l-14 2.625v11.188c0 2.197-1.803 4-4 4s-4-1.803-4-4 1.803-4 4-4c0.732 0 1.407 0.214 2 0.563v-13.406l0.813-0.125 16-3zM25 6.188l-14 2.625v2l14-2.625v-2zM23 19c-1.116 0-2 0.884-2 2s0.884 2 2 2 2-0.884 2-2-0.884-2-2-2zM7 22c-1.116 0-2 0.884-2 2s0.884 2 2 2 2-0.884 2-2-0.884-2-2-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 551 B |
18
webapp/assets/_new/icons/svgs/nature.svg
Normal file
@ -0,0 +1,18 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<path d="M31,20.4c-0.4-0.3-1-0.5-1.6-0.5l-0.2-0.4c-0.5-1.4-2.1-1.6-3.6-1.1c-0.2,0.1-0.6,0.3-1,0.6c-0.7-0.8-2-0.9-3.2-0.5
|
||||
c-0.5,0.2-1.8,0.9-2.5,1.3c-2.6-0.5-5.8-1.1-6.5-1.2c-2-0.2-3.3-0.2-4.9,0.3c-0.6,0.2-5.2,2.3-6.1,2.7L0.9,22l0.6,1.4l0.7-0.3
|
||||
c2.5-1.2,5.5-2.5,5.9-2.6c1.4-0.4,2.4-0.4,4.3-0.2c0.9,0.1,8.1,1.5,9.4,1.8c0.9,0.2,0.9,0.7,0.9,0.8c0,0.3-0.1,0.4-0.1,0.4
|
||||
s0,0-0.1,0h-8.6v1.5h2l0.5,0.3c0.1,0,1.4,0.9,2.4,1.3c1,0.4,2,0.4,2.7-0.2c2.6-1.7,6.7-4.4,7-4.6c0.6-0.4,1.4-0.4,1.6-0.2
|
||||
c0.1,0.1,0.2,0.2,0.2,0.3s-0.1,0.2-0.2,0.3c-2.8,2-10.3,7.5-11.2,8c-1.2,0.7-2.8,0.3-3.6,0.1c-1.2-0.4-7.1-2.5-7.6-2.7
|
||||
c-0.7-0.2-2.4-0.6-3.9,0C2.7,28,1,29,0.9,29l-0.7,0.3l0.7,1.3l0.7-0.3c0,0,1.7-0.9,2.8-1.4c0.9-0.4,2.2-0.2,2.6,0
|
||||
c0.5,0.2,6.4,2.3,7.7,2.7c0.5,0.1,1.3,0.3,2.2,0.3c0.8,0,1.7-0.2,2.5-0.3c1.1-0.6,10.9-7.8,11.3-8.1c0.5-0.4,0.8-0.9,0.8-1.5
|
||||
S31.5,20.8,31,20.4z M22,20c0,0,0.7-0.2,1.2-0.1c-0.4,0.3-0.8,0.5-1.1,0.7c0,0,0,0,0,0c-0.2-0.1-0.5-0.1-0.9-0.2
|
||||
C21.6,20.2,21.9,20.1,22,20z M19.3,25.2c-0.2-0.1-0.4-0.2-0.6-0.3h2.1c-0.1,0.1-0.2,0.2-0.3,0.2C20.3,25.3,19.9,25.5,19.3,25.2z
|
||||
M24.2,22.7c0-0.5-0.2-0.9-0.5-1.2c1.2-0.7,2.4-1.5,2.7-1.6c0,0,1.4-0.4,1.7,0.3l0,0.1c-0.2,0.1-0.3,0.1-0.5,0.2
|
||||
C27.5,20.5,25.8,21.6,24.2,22.7z"/>
|
||||
<path d="M15.9,16c3,0,4.5-1.3,5.3-2.3c1.3,1.4,1.5,3,1.5,3.1l0.1,0.7l1.6-0.1l-0.1-0.7c-0.1-0.1-0.3-2.5-2.5-4.4
|
||||
c0.4-1.6,0.1-3.5-1-4.9c-2.2-2.7-5.4-2.9-8.6-2.8c-2.9,0-4.6,0-6.4-0.9L4.9,3.2L4.7,4.2C4.3,6.3,5.2,9,7.1,11.5
|
||||
C9.4,14.4,12.7,16,15.9,16z M12.3,6.1c3-0.1,5.7,0,7.4,2.2c0.7,0.8,1,2,0.8,3c-2-1.1-4-1.6-5.6-2.1c-1.2-0.3-2.3-0.6-2.7-1
|
||||
l-0.6-0.5l-1,1.1l0.6,0.5c0.7,0.6,1.9,0.9,3.4,1.3c1.6,0.4,3.6,1,5.5,2.1c-0.7,1.2-2.1,1.8-4.1,1.8c-2.8,0-5.6-1.4-7.6-4
|
||||
C7,8.9,6.2,7,6.2,5.5C8,6.1,9.7,6.1,12.3,6.1z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
22
webapp/assets/_new/icons/svgs/networking.svg
Normal file
@ -0,0 +1,22 @@
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<path d="M31.6,16c-0.6-6.4-1.9-6.4-2.4-6.4c-0.4,0-0.8,0.1-1.1,0.4c-0.8,0.8-0.8,2.5-0.8,4.2c0,0.5,0,1.2,0,1.7h-0.1
|
||||
C26,15.5,25.1,17,24.5,18c-0.1,0.2-0.2,0.4-0.3,0.5c-0.8,0.9-1.2,1.2-2.2,2c-0.4,0.4-1,0.6-1.5,0.9c-0.6,0.3-1.2,0.6-1.8,1.1
|
||||
c-2.3,1.9-1.8,4.1-1.4,6.1c0.2,0.8,0.3,1.5,0.3,2.3v0.8h1.5v-0.8c0-0.9-0.2-1.7-0.4-2.6c-0.4-2.1-0.6-3.4,0.9-4.6
|
||||
c0.4-0.4,1-0.6,1.5-0.9c0.6-0.3,1.2-0.6,1.8-1.1c1-0.8,1.5-1.2,2.4-2.2c0.1-0.1,0.3-0.4,0.4-0.7c0.2-0.3,0.7-1.2,1-1.4
|
||||
c0.3,0.2,0.4,1.3,0.3,1.7c-0.3,1-1.1,1.9-1.9,2.8c-0.7,0.8-1.4,1.6-1.8,2.5L23,25l1.3,0.7l0.3-0.7c0.4-0.7,1-1.4,1.6-2.2
|
||||
c0.9-1,1.8-2.1,2.2-3.3c0.2-0.5,0.2-1.4-0.1-2.2c0.4-0.5,0.4-1.4,0.4-3c0-0.8,0-2.5,0.3-3c0.3,0.5,0.7,1.8,1,4.9
|
||||
c0.2,2,0.2,4.8-1.2,7.1c-0.5,0.9-1.3,1.7-2,2.4c-1.3,1.4-2.7,2.9-3,5.2l-0.1,0.7l1.5,0.2l0.1-0.7c0.2-1.7,1.4-3,2.6-4.3
|
||||
c0.8-0.8,1.5-1.7,2.2-2.7C31.9,21.4,31.9,18.2,31.6,16z"/>
|
||||
<path d="M26.3,7.2c0-2.9-2.4-5.2-5.2-5.2c-2.4,0-3.9,1.4-4.5,2c-0.6-0.6-2.1-2-4.5-2C9.3,2,6.9,4.3,6.9,7.1c0,1,0.5,1.9,1,2.4
|
||||
c0.4,0.6,0.8,1,0.8,1l7.4,7.5l0.6,0.6l7.9-7.9C24.6,10.7,26.3,9.2,26.3,7.2z M16.6,16.7l-7-7c0,0-0.4-0.3-0.7-0.8
|
||||
C8.6,8.4,8.3,7.7,8.3,7.2c0-2.1,1.7-3.8,3.8-3.8c2,0,3.9,2,3.9,2L16.6,6l0.5-0.6c0,0,1.9-2,4-2s3.8,1.7,3.8,3.8
|
||||
c0,1.1-1.3,2.5-1.3,2.5L16.6,16.7z"/>
|
||||
<path d="M13.4,22.6c-0.6-0.4-1.2-0.8-1.8-1.1c-0.6-0.3-1.1-0.6-1.5-0.9c-1-0.8-1.4-1.2-2.2-2c-0.1-0.1-0.2-0.3-0.3-0.5
|
||||
c-0.6-1-1.5-2.5-2.7-2.1c-0.1,0-0.1,0.1-0.2,0.1c0-0.5,0-1.2,0-1.7c0-1.8,0-3.5-0.8-4.2c-0.3-0.3-0.7-0.4-1-0.4
|
||||
c-0.5,0-1.8,0-2.4,6.4c-0.3,2.2-0.2,5.4,1.4,8.1c0.6,1,1.4,1.9,2.2,2.7c1.3,1.3,2.4,2.6,2.7,4.3L6.9,32l1.5-0.2L8.1,31
|
||||
c-0.3-2.2-1.7-3.7-3.1-5.1c-0.7-0.8-1.5-1.6-2-2.5c-1.4-2.3-1.4-5.2-1.2-7.1c0.3-3.1,0.8-4.5,1-4.9c0.3,0.5,0.3,2.2,0.3,3
|
||||
c0,1.7,0,2.6,0.5,3.1c-0.2,0.8-0.2,1.7-0.1,2.1C4,20.9,4.9,22,5.8,23c0.6,0.7,1.3,1.4,1.6,2.2l0.3,0.7L9,25.2l-0.3-0.7
|
||||
c-0.5-0.9-1.2-1.7-1.8-2.5c-0.8-0.9-1.6-1.8-1.9-2.8c-0.1-0.4,0-1.5,0.3-1.7c0.3,0.2,0.8,1.1,1,1.4s0.3,0.6,0.5,0.7
|
||||
c0.8,0.9,1.4,1.4,2.4,2.2c0.6,0.4,1.2,0.8,1.8,1.1c0.6,0.3,1.1,0.6,1.5,0.9c1.5,1.3,1.3,2.6,0.9,4.6C13.1,29.3,13,30.1,13,31v0.8
|
||||
h1.5V31c0-0.7,0.2-1.5,0.3-2.3C15.2,26.8,15.7,24.5,13.4,22.6z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
8
webapp/assets/_new/icons/svgs/peace.svg
Normal file
@ -0,0 +1,8 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="32px" height="32px">
|
||||
<path d="M27.3,5c-1.5-1.5-3.1-2.6-5-3.4c-2-0.8-4-1.2-6.2-1.2c-2.1,0-4.2,0.4-6.2,1.2C8,2.4,6.3,3.5,4.9,5c-1.5,1.5-2.6,3.1-3.4,5
|
||||
c-0.8,2-1.2,4-1.2,6.2c0,2.1,0.4,4.2,1.2,6.2c0.8,1.9,1.9,3.6,3.4,5c1.5,1.5,3.1,2.6,5,3.4c2,0.8,4,1.2,6.2,1.2
|
||||
c2.1,0,4.2-0.4,6.2-1.2c1.9-0.8,3.6-1.9,5-3.4c1.5-1.5,2.6-3.1,3.4-5c0.8-2,1.2-4,1.2-6.2c0-2.1-0.4-4.2-1.2-6.2
|
||||
C29.8,8.1,28.7,6.4,27.3,5z M15.1,2.4v13.4L5.4,25c-2-2.4-3.2-5.5-3.2-8.8C2.2,8.9,7.9,2.9,15.1,2.4z M6.8,26.4l8.3-7.9V30
|
||||
C11.9,29.7,9,28.4,6.8,26.4z M17.1,30V18.5l8.3,7.9C23.1,28.4,20.2,29.7,17.1,30z M26.7,25l-9.7-9.2V2.4c7.2,0.5,12.8,6.5,12.8,13.8
|
||||
C29.9,19.5,28.7,22.6,26.7,25z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 742 B |
12
webapp/assets/_new/icons/svgs/politics.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<path d="M19.3,15.4c0.3,0.2,0.9,0.5,1.1,0.8c0.5,0.6,0.6,1.2,0.6,1.3l0.8,3.1l1.4-0.4l-0.8-3c0-0.1-0.2-0.9-0.9-1.8
|
||||
c-0.5-0.6-1.5-1.1-1.6-1.2l-8.5-4.7l-0.7,1.3L19.3,15.4z"/>
|
||||
<path d="M7.4,19.1c0.4,2.2,2.5,4.1,3.4,4.8l0.9-1.2c-1.7-1.4-2.8-2.9-2.9-4.2l0-0.4l-2.3-1.3L11,7.8c0.1-0.2,0.1-0.4,0-0.6
|
||||
c-0.1-0.2-0.2-0.3-0.4-0.4L6.5,4.9L5.9,6.3l3.4,1.6L4.4,17.9l-3.4-1.6l-0.6,1.4l4.1,1.9c0.1,0,0.2,0.1,0.3,0.1
|
||||
c0.3,0,0.5-0.2,0.7-0.4l0.5-1L7.4,19.1z"/>
|
||||
<path d="M28.9,30.5l-2.8-10.1l-5.6,1.5l-1.1-4c-0.1-0.4-0.5-0.6-0.9-0.5c-0.4,0.1-0.6,0.5-0.5,0.9l2.1,7.6c0.2,0.7-0.2,1.4-1,1.6
|
||||
c-0.3,0.1-0.7,0-0.9-0.1c-0.3-0.2-0.5-0.4-0.6-0.8l-1.4-5c-0.1-0.3-0.4-0.5-0.7-0.6c-0.1,0-2.4,0-3.8-3.5c-0.2-0.4-0.6-0.6-1-0.4
|
||||
c-0.4,0.2-0.6,0.6-0.4,1c1.4,3.4,3.6,4.2,4.6,4.4l0.2,0.9l-6.8,1.9l1.2,4.4l1.4-0.4l-0.8-2.9l5.3-1.5l0.6,2.1
|
||||
c0.2,0.7,0.7,1.3,1.3,1.7c0.4,0.2,0.9,0.4,1.3,0.4c0.3,0,0.5,0,0.8-0.1c1.5-0.4,2.3-1.9,1.9-3.4L21,23.4l4.1-1.1l2.2,8.2h-20V32
|
||||
h24.5v-1.5H28.9z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
8
webapp/assets/_new/icons/svgs/psyche.svg
Normal file
@ -0,0 +1,8 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<path class="st0" d="M30.8,12C30.5,7.4,27.9,3.5,24,1.8c-2-0.9-4.3-1.4-6.4-1.4v0.1c0,0,0,0,0,0V0.4c-2,0-3.9,0.4-5.7,1.2
|
||||
c-1.8,0.8-3.4,2-4.7,3.6c0,0,0,0,0,0c-0.1,0.1-2.6,3.3-2.5,8.5l-3.4,6.3C1,20.7,1,21.4,1.4,22c0.3,0.6,1,0.9,1.6,0.9h1.9v2.8
|
||||
c0,1.7,1.4,3.1,3.1,3.1h2.8v1.7c0,0.7,0,1.3,0,1.3l1.4,0v-4.3h-4c-1,0-1.9-0.8-1.9-1.9v-4H3.2c-0.5,0-0.8-0.5-0.6-1L6.2,14
|
||||
c-0.2-5,2.2-8,2.2-8c1.2-1.4,2.6-2.5,4.2-3.2c1.5-0.6,3.2-1,4.8-1c2,0,4,0.4,5.9,1.3c3.7,1.6,5.7,5.3,6,9.2c0.1,2.1-0.3,4.3-1.1,6.2
|
||||
c-0.4,1-0.9,1.9-1.5,2.8c-0.3,0.5-1.8,1.9-1.8,2.4c0,0,0,6,0,6.8l0,1.3c0.2,0,1.5,0,1.5,0s0-0.8,0-1.3c0,0,0-5.4,0-6.6
|
||||
c0.2-0.3,0.6-0.7,0.9-1c0.4-0.4,0.6-0.7,0.8-0.9c0.7-1,1.2-2,1.6-3.1C30.5,16.6,30.9,14.3,30.8,12z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 823 B |
5
webapp/assets/_new/icons/svgs/save.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>save</title>
|
||||
<path d="M5 5h17.406l0.313 0.281 4 4 0.281 0.313v17.406h-22v-22zM7 7v18h2v-9h14v9h2v-14.563l-3-3v5.563h-12v-6h-3zM12 7v4h8v-4h-2v2h-2v-2h-4zM11 18v7h10v-7h-10z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 327 B |
25
webapp/assets/_new/icons/svgs/science.svg
Normal file
@ -0,0 +1,25 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<path d="M28.4,16.1c1-1.7,1.9-3.3,2.5-4.9c1.5-4,1.3-7.3-0.6-9.2C27.5-0.8,22,0.1,16.1,3.7C10.3,0.1,4.7-0.8,2,1.9
|
||||
c-2.8,2.8-1.9,8.3,1.8,14.1c-3.3,5.3-4.4,10.4-2.5,13.4C1.5,29.7,1.7,30,2,30.2c1.1,1.1,2.6,1.6,4.4,1.6c1.4,0,3-0.3,4.8-1
|
||||
c1.6-0.6,3.2-1.4,4.9-2.5c2.6,1.6,5.1,2.7,7.4,3.2c0,0,0.1,0,0.1,0c0.3,0.1,0.7,0.1,1,0.2c0.1,0,0.1,0,0.2,0c0.4,0,0.7,0.1,1,0.1
|
||||
c0,0,0,0,0,0c0,0,0,0,0,0c0.3,0,0.5,0,0.8-0.1c0.2,0,0.4,0,0.5,0c0.3,0,0.6-0.1,0.9-0.2c0.1,0,0.2,0,0.3-0.1
|
||||
c0.3-0.1,0.5-0.2,0.8-0.4c0.1,0,0.2-0.1,0.3-0.1c0.3-0.2,0.6-0.4,0.9-0.7c1.9-1.9,2.1-5.2,0.6-9.2C30.3,19.4,29.5,17.7,28.4,16.1z
|
||||
M29.5,21.5c1.3,3.4,1.1,6.2-0.3,7.6c-0.2,0.2-0.4,0.3-0.6,0.5c-0.2,0.1-0.4,0.2-0.6,0.3c0,0-0.1,0-0.1,0.1
|
||||
c-0.3,0.1-0.5,0.2-0.8,0.2c0,0,0,0,0,0c-0.3,0.1-0.6,0.1-0.9,0.1c0,0,0,0,0,0c-0.3,0-0.6,0-0.8,0c-0.1,0-0.1,0-0.2,0
|
||||
c-0.2,0-0.5-0.1-0.7-0.1c-0.2,0-0.3,0-0.5-0.1c-0.1,0-0.3-0.1-0.4-0.1c-0.3-0.1-0.6-0.1-0.9-0.2c0,0-0.1,0-0.1,0
|
||||
c-1.5-0.5-3.2-1.3-4.9-2.3c1.9-1.3,3.7-2.8,5.4-4.6c1.7-1.7,3.3-3.6,4.6-5.5C28.3,18.8,29,20.2,29.5,21.5z M5.6,16.1
|
||||
c1.3-1.9,2.9-3.9,4.7-5.8c1.9-1.9,3.8-3.5,5.8-4.7c0,0,0,0,0.1,0c0.1,0.1,0.2,0.2,0.4,0.2c0.4,0.3,0.8,0.6,1.2,0.8c0,0,0,0,0.1,0.1
|
||||
c0.4,0.3,0.8,0.6,1.2,1c0.1,0.1,0.2,0.2,0.4,0.3c0.3,0.3,0.6,0.5,0.9,0.8c0.1,0.1,0.2,0.2,0.4,0.3c0.4,0.4,0.8,0.8,1.2,1.2
|
||||
c1.8,1.8,3.5,3.8,4.8,5.8c-1.3,2-2.9,3.9-4.8,5.8c-1.8,1.8-3.8,3.4-5.8,4.8c-1.9-1.3-3.9-2.9-5.8-4.8C8.5,20,6.9,18,5.6,16.1z
|
||||
M25.8,1.8c1.4,0,2.6,0.4,3.4,1.2c1.4,1.4,1.5,4.2,0.3,7.6c-0.5,1.3-1.2,2.7-2,4c-1.3-1.9-2.8-3.7-4.6-5.5
|
||||
c-1.8-1.8-3.6-3.3-5.4-4.6C20.6,2.8,23.5,1.8,25.8,1.8z M3,3c0.2-0.2,0.4-0.4,0.7-0.5C3.8,2.4,3.9,2.4,4,2.3
|
||||
c0.1-0.1,0.3-0.1,0.4-0.2C4.6,2.1,4.8,2,4.9,2c0.1,0,0.3-0.1,0.4-0.1c0.2,0,0.4,0,0.5,0c0.1,0,0.3,0,0.4,0c0.3,0,0.6,0,1,0.1
|
||||
c0.1,0,0.1,0,0.2,0C7.8,1.9,8.1,2,8.4,2c0.1,0,0.2,0,0.3,0.1c0.3,0.1,0.7,0.2,1,0.3c0,0,0.1,0,0.1,0c0.4,0.1,0.8,0.3,1.2,0.4
|
||||
c0.1,0,0.2,0.1,0.3,0.1c0.3,0.1,0.6,0.3,0.9,0.4c0.1,0,0.2,0.1,0.3,0.1c0.4,0.2,0.8,0.4,1.1,0.6c0,0,0.1,0,0.1,0
|
||||
c0.4,0.2,0.7,0.4,1.1,0.6c-1.8,1.3-3.7,2.8-5.4,4.6C7.5,11,6,12.8,4.7,14.7C1.8,9.7,0.9,5.1,3,3z M10.7,29.4
|
||||
c-3.4,1.3-6.2,1.1-7.6-0.3C0.9,27,1.8,22.4,4.7,17.5c1.3,1.8,2.8,3.7,4.6,5.4c0.4,0.4,0.9,0.8,1.3,1.2c0.1,0.1,0.2,0.2,0.3,0.3
|
||||
c0.4,0.3,0.8,0.7,1.1,1c0.1,0.1,0.1,0.1,0.2,0.2c0.8,0.7,1.7,1.3,2.5,1.9C13.4,28.3,12,28.9,10.7,29.4z"/>
|
||||
<path d="M19.4,19.3c1.8-1.8,1.8-4.8,0-6.6c-1.8-1.8-4.8-1.8-6.6,0c-1.8,1.8-1.8,4.8,0,6.6c0.9,0.9,2.1,1.4,3.3,1.4
|
||||
C17.3,20.7,18.5,20.3,19.4,19.3z M13.9,13.8c0.6-0.6,1.4-0.9,2.2-0.9s1.6,0.3,2.2,0.9c1.2,1.2,1.2,3.2,0,4.4s-3.2,1.2-4.4,0
|
||||
S12.7,15.1,13.9,13.8z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
13
webapp/assets/_new/icons/svgs/spirituality.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<path d="M15.9,18.6c-1.4,0-2.7-0.5-3.7-1.5c-1.4-1.4-1.9-3.3-1.4-5.2c0.5-1.8,1.9-3.2,3.7-3.7c1.9-0.5,3.8,0,5.2,1.4
|
||||
c1.3,1.3,1.9,3.3,1.4,5.2c-0.5,1.8-1.9,3.2-3.7,3.7c0,0,0,0,0,0C16.9,18.5,16.4,18.6,15.9,18.6z M17.2,17.7L17.2,17.7
|
||||
L17.2,17.7z M15.9,9.5c-0.3,0-0.7,0-1,0.1c-1.2,0.3-2.3,1.4-2.6,2.6c-0.4,1.4,0,2.7,1,3.7s2.4,1.3,3.7,1
|
||||
c1.2-0.3,2.3-1.4,2.6-2.6c0.4-1.4,0-2.7-1-3.7C17.9,9.9,16.9,9.5,15.9,9.5z"/>
|
||||
<path d="M27.8,32H4v-2.2c0-5.5,4.4-9.9,9.9-9.9h4c5.5,0,9.9,4.4,9.9,9.9V32z M5.5,30.5h20.8v-0.7c0-4.6-3.8-8.4-8.4-8.4h-4
|
||||
c-4.6,0-8.4,3.8-8.4,8.4V30.5z"/>
|
||||
<rect x="15.6" y="0.5" width="1.5" height="4.3"/>
|
||||
<rect x="23.6" y="7.2" transform="matrix(0.8679 -0.4968 0.4968 0.8679 -0.5339 13.83)" width="4.3" height="1.5"/>
|
||||
<rect x="5.9" y="5.9" transform="matrix(0.4969 -0.8678 0.8678 0.4969 -3.6304 9.7958)" width="1.5" height="4.3"/>
|
||||
<rect x="9.4" y="1.8" transform="matrix(0.8207 -0.5714 0.5714 0.8207 -0.4488 6.4807)" width="1.5" height="4.3"/>
|
||||
<rect x="20.1" y="3.2" transform="matrix(0.5712 -0.8208 0.8208 0.5712 6.3227 19.9037)" width="4.3" height="1.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
5
webapp/assets/_new/icons/svgs/suitcase.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>suitcase</title>
|
||||
<path d="M14 3h4c1.093 0 2 0.907 2 2v1h3v-1h2v1h2c1.093 0 2 0.907 2 2v16c0 1.093-0.907 2-2 2h-22c-1.093 0-2-0.907-2-2v-16c0-1.093 0.907-2 2-2h2v-1h2v1h3v-1c0-1.093 0.907-2 2-2zM14 5v1h4v-1h-4zM5 8v16h2v-15h2v15h14v-15h2v15h2v-16h-22z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 405 B |
@ -356,3 +356,22 @@ $media-query-x-large: (min-width: 1200px);
|
||||
/**
|
||||
* @tokens Background Images
|
||||
*/
|
||||
|
||||
/**
|
||||
* @tokens Header Color
|
||||
*/
|
||||
|
||||
$color-header-background: $color-neutral-100;
|
||||
|
||||
/**
|
||||
* @tokens Footer Color
|
||||
*/
|
||||
|
||||
$color-footer-background: $color-neutral-100;
|
||||
$color-footer-link: $color-primary;
|
||||
|
||||
/**
|
||||
* @tokens Locale Menu Color
|
||||
*/
|
||||
|
||||
$color-locale-menu: $text-color-soft;
|
||||
5
webapp/assets/styles/imports/_branding.scss
Normal file
@ -0,0 +1,5 @@
|
||||
/*
|
||||
*
|
||||
* Here, all SCSS variables and classes can be adapted to your custom design.
|
||||
*
|
||||
*/
|
||||
@ -179,4 +179,4 @@ hr {
|
||||
|
||||
.dropdown-arrow {
|
||||
font-size: $font-size-xx-small;
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,11 @@
|
||||
:disabled="isDisabled(category.id)"
|
||||
:icon="category.icon"
|
||||
size="small"
|
||||
v-tooltip="{
|
||||
content: $t(`contribution.category.description.${category.slug}`),
|
||||
placement: 'bottom-start',
|
||||
delay: { show: 1500 },
|
||||
}"
|
||||
>
|
||||
{{ $t(`contribution.category.name.${category.slug}`) }}
|
||||
</base-button>
|
||||
|
||||
@ -18,8 +18,7 @@ const comment = {
|
||||
author: {
|
||||
id: '1',
|
||||
avatar: {
|
||||
url:
|
||||
'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg',
|
||||
url: 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg',
|
||||
},
|
||||
slug: 'jenny-rostock',
|
||||
name: 'Rainer Unsinn',
|
||||
|
||||
@ -58,6 +58,9 @@ describe('ContributionForm.vue', () => {
|
||||
back: jest.fn(),
|
||||
push: jest.fn(),
|
||||
},
|
||||
$env: {
|
||||
CATEGORIES_ACTIVE: false,
|
||||
},
|
||||
}
|
||||
propsData = {}
|
||||
})
|
||||
@ -132,6 +135,7 @@ describe('ContributionForm.vue', () => {
|
||||
variables: {
|
||||
title: postTitle,
|
||||
content: postContent,
|
||||
categoryIds: [],
|
||||
id: null,
|
||||
image: null,
|
||||
},
|
||||
@ -198,7 +202,7 @@ describe('ContributionForm.vue', () => {
|
||||
beforeEach(async () => {
|
||||
jest.useFakeTimers()
|
||||
mocks.$apollo.mutate = jest.fn().mockRejectedValueOnce({
|
||||
message: 'Not Authorised!',
|
||||
message: 'Not Authorized!',
|
||||
})
|
||||
wrapper = Wrapper()
|
||||
postTitleInput = wrapper.find('.ds-input')
|
||||
@ -209,7 +213,7 @@ describe('ContributionForm.vue', () => {
|
||||
it('shows an error toaster when apollo mutation rejects', async () => {
|
||||
await wrapper.find('form').trigger('submit')
|
||||
await mocks.$apollo.mutate
|
||||
await expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorised!')
|
||||
await expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorized!')
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -254,6 +258,7 @@ describe('ContributionForm.vue', () => {
|
||||
variables: {
|
||||
title: propsData.contribution.title,
|
||||
content: propsData.contribution.content,
|
||||
categoryIds: [],
|
||||
id: propsData.contribution.id,
|
||||
image: {
|
||||
sensitive: false,
|
||||
|
||||
@ -51,6 +51,19 @@
|
||||
{{ contentLength }}
|
||||
<base-icon v-if="errors && errors.content" name="warning" />
|
||||
</ds-chip>
|
||||
<categories-select
|
||||
v-if="categoriesActive"
|
||||
model="categoryIds"
|
||||
:existingCategoryIds="formData.categoryIds"
|
||||
/>
|
||||
<ds-chip
|
||||
v-if="categoriesActive"
|
||||
size="base"
|
||||
:color="errors && errors.categoryIds && 'danger'"
|
||||
>
|
||||
{{ formData.categoryIds.length }} / 3
|
||||
<base-icon v-if="errors && errors.categoryIds" name="warning" />
|
||||
</ds-chip>
|
||||
<div class="buttons">
|
||||
<base-button data-test="cancel-button" :disabled="loading" @click="$router.back()" danger>
|
||||
{{ $t('actions.cancel') }}
|
||||
@ -69,6 +82,7 @@ import gql from 'graphql-tag'
|
||||
import { mapGetters } from 'vuex'
|
||||
import HcEditor from '~/components/Editor/Editor'
|
||||
import PostMutations from '~/graphql/PostMutations.js'
|
||||
import CategoriesSelect from '~/components/CategoriesSelect/CategoriesSelect'
|
||||
import ImageUploader from '~/components/ImageUploader/ImageUploader'
|
||||
import links from '~/constants/links.js'
|
||||
import PageParamsLink from '~/components/_new/features/PageParamsLink/PageParamsLink.vue'
|
||||
@ -78,6 +92,7 @@ export default {
|
||||
HcEditor,
|
||||
ImageUploader,
|
||||
PageParamsLink,
|
||||
CategoriesSelect,
|
||||
},
|
||||
props: {
|
||||
contribution: {
|
||||
@ -86,7 +101,7 @@ export default {
|
||||
},
|
||||
},
|
||||
data() {
|
||||
const { title, content, image } = this.contribution
|
||||
const { title, content, image, categories } = this.contribution
|
||||
const {
|
||||
sensitive: imageBlurred = false,
|
||||
aspectRatio: imageAspectRatio = null,
|
||||
@ -94,6 +109,7 @@ export default {
|
||||
} = image || {}
|
||||
|
||||
return {
|
||||
categoriesActive: this.$env.CATEGORIES_ACTIVE,
|
||||
links,
|
||||
formData: {
|
||||
title: title || '',
|
||||
@ -102,11 +118,22 @@ export default {
|
||||
imageAspectRatio,
|
||||
imageType,
|
||||
imageBlurred,
|
||||
categoryIds: categories ? categories.map((category) => category.id) : [],
|
||||
},
|
||||
formSchema: {
|
||||
title: { required: true, min: 3, max: 100 },
|
||||
content: { required: true },
|
||||
imageBlurred: { required: false },
|
||||
categoryIds: {
|
||||
type: 'array',
|
||||
required: this.categoriesActive,
|
||||
validator: (_, value = []) => {
|
||||
if (this.categoriesActive && (value.length === 0 || value.length > 3)) {
|
||||
return [new Error(this.$t('common.validations.categories'))]
|
||||
}
|
||||
return []
|
||||
},
|
||||
},
|
||||
},
|
||||
loading: false,
|
||||
users: [],
|
||||
@ -125,7 +152,7 @@ export default {
|
||||
methods: {
|
||||
submit() {
|
||||
let image = null
|
||||
const { title, content } = this.formData
|
||||
const { title, content, categoryIds } = this.formData
|
||||
if (this.formData.image) {
|
||||
image = {
|
||||
sensitive: this.formData.imageBlurred,
|
||||
@ -143,6 +170,7 @@ export default {
|
||||
variables: {
|
||||
title,
|
||||
content,
|
||||
categoryIds,
|
||||
id: this.contribution.id || null,
|
||||
image,
|
||||
},
|
||||
|
||||
@ -29,7 +29,7 @@ describe('DeleteData.vue', () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
.mockRejectedValue({ message: 'Not authorised!' }),
|
||||
.mockRejectedValue({ message: 'Not Authorized!' }),
|
||||
},
|
||||
$toast: {
|
||||
error: jest.fn(),
|
||||
@ -180,7 +180,7 @@ describe('DeleteData.vue', () => {
|
||||
// second submission causes mutation to reject
|
||||
await deleteAccountBtn.trigger('click')
|
||||
await mocks.$apollo.mutate
|
||||
expect(mocks.$toast.error).toHaveBeenCalledWith('Not authorised!')
|
||||
expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorized!')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -103,7 +103,7 @@ export default {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: gql`
|
||||
mutation($id: ID!, $resource: [Deletable]) {
|
||||
mutation ($id: ID!, $resource: [Deletable]) {
|
||||
DeleteUser(id: $id, resource: $resource) {
|
||||
id
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
:offset="offset"
|
||||
>
|
||||
<slot :toggleMenu="toggleMenu" :openMenu="openMenu" :closeMenu="closeMenu" :isOpen="isOpen" />
|
||||
<div slot="popover" @mouseover="popoverMouseEnter" @mouseleave="popoveMouseLeave">
|
||||
<div slot="popover" @mouseover="popoverMouseEnter" @mouseleave="popoverMouseLeave">
|
||||
<slot
|
||||
name="popover"
|
||||
:toggleMenu="toggleMenu"
|
||||
@ -97,7 +97,7 @@ export default {
|
||||
}
|
||||
this.clearTimeouts()
|
||||
if (useTimeout === true) {
|
||||
this.popoveMouseLeave()
|
||||
this.popoverMouseLeave()
|
||||
} else {
|
||||
this.isPopoverOpen = false
|
||||
}
|
||||
@ -113,7 +113,7 @@ export default {
|
||||
}, 500)
|
||||
}
|
||||
},
|
||||
popoveMouseLeave() {
|
||||
popoverMouseLeave() {
|
||||
if (this.developerNoAutoClosing) return
|
||||
if (this.disabled) {
|
||||
return
|
||||
|
||||
@ -9,8 +9,7 @@ const embed = {
|
||||
title: 'Video Titel',
|
||||
// html: null,
|
||||
description: 'Video Description',
|
||||
html:
|
||||
'<iframe width="auto" height="250" src="https://www.youtube.com/embed/qkdXAtO40Fo?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
|
||||
html: '<iframe width="auto" height="250" src="https://www.youtube.com/embed/qkdXAtO40Fo?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
|
||||
}
|
||||
|
||||
const plugins = [
|
||||
|
||||
@ -33,8 +33,7 @@ describe('Embed.vue', () => {
|
||||
video: null,
|
||||
lang: 'de',
|
||||
sources: ['resource', 'oembed'],
|
||||
html:
|
||||
'<iframe width="480" height="270" src="https://www.youtube.com/embed/qkdXAtO40Fo?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
|
||||
html: '<iframe width="480" height="270" src="https://www.youtube.com/embed/qkdXAtO40Fo?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
@ -114,8 +114,7 @@ describe('EmbedComponent.vue', () => {
|
||||
video: null,
|
||||
lang: 'de',
|
||||
sources: ['resource', 'oembed'],
|
||||
html:
|
||||
'<iframe width="480" height="270" src="https://www.youtube.com/embed/qkdXAtO40Fo?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>',
|
||||
html: '<iframe width="480" height="270" src="https://www.youtube.com/embed/qkdXAtO40Fo?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>',
|
||||
}
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
@ -83,7 +83,7 @@ export default {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: gql`
|
||||
query($postId: ID!, $data: _EMOTEDInput!) {
|
||||
query ($postId: ID!, $data: _EMOTEDInput!) {
|
||||
PostsEmotionsCountByEmotion(postId: $postId, data: $data)
|
||||
}
|
||||
`,
|
||||
|
||||
@ -15,8 +15,19 @@ describe('CategoriesFilter.vue', () => {
|
||||
'posts/filteredCategoryIds': jest.fn(() => []),
|
||||
}
|
||||
|
||||
const apolloMutationMock = jest.fn().mockResolvedValue({
|
||||
data: { saveCategorySettings: true },
|
||||
})
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((string) => string),
|
||||
$apollo: {
|
||||
mutate: apolloMutationMock,
|
||||
},
|
||||
$toast: {
|
||||
success: jest.fn(),
|
||||
error: jest.fn(),
|
||||
},
|
||||
}
|
||||
|
||||
const Wrapper = () => {
|
||||
@ -76,5 +87,14 @@ describe('CategoriesFilter.vue', () => {
|
||||
expect(mutations['posts/RESET_CATEGORIES']).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('save categories', () => {
|
||||
it('calls the API', async () => {
|
||||
wrapper = await Wrapper()
|
||||
const saveButton = wrapper.findAll('.categories-filter .sidebar .base-button').at(1)
|
||||
saveButton.trigger('click')
|
||||
expect(apolloMutationMock).toBeCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
icon="check"
|
||||
@click="resetCategories"
|
||||
/>
|
||||
<hr />
|
||||
<labeled-button filled :label="$t('actions.save')" icon="save" @click="saveCategories" />
|
||||
</template>
|
||||
<template #filter-list>
|
||||
<li v-for="category in categories" :key="category.id" class="item">
|
||||
@ -15,6 +17,11 @@
|
||||
:filled="filteredCategoryIds.includes(category.id)"
|
||||
:label="$t(`contribution.category.name.${category.slug}`)"
|
||||
@click="toggleCategory(category.id)"
|
||||
v-tooltip="{
|
||||
content: $t(`contribution.category.description.${category.slug}`),
|
||||
placement: 'bottom-start',
|
||||
delay: { show: 1500 },
|
||||
}"
|
||||
/>
|
||||
</li>
|
||||
</template>
|
||||
@ -24,6 +31,7 @@
|
||||
<script>
|
||||
import { mapGetters, mapMutations } from 'vuex'
|
||||
import CategoryQuery from '~/graphql/CategoryQuery.js'
|
||||
import SaveCategories from '~/graphql/SaveCategories.js'
|
||||
import FilterMenuSection from '~/components/FilterMenu/FilterMenuSection'
|
||||
import LabeledButton from '~/components/_new/generic/LabeledButton/LabeledButton'
|
||||
|
||||
@ -47,6 +55,19 @@ export default {
|
||||
resetCategories: 'posts/RESET_CATEGORIES',
|
||||
toggleCategory: 'posts/TOGGLE_CATEGORY',
|
||||
}),
|
||||
saveCategories() {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: SaveCategories(),
|
||||
variables: { activeCategories: this.filteredCategoryIds },
|
||||
})
|
||||
.then(() => {
|
||||
this.$toast.success(this.$t('filter-menu.save.success'))
|
||||
})
|
||||
.catch(() => {
|
||||
this.$toast.error(this.$t('filter-menu.save.error'))
|
||||
})
|
||||
},
|
||||
},
|
||||
apollo: {
|
||||
Category: {
|
||||
|
||||
53
webapp/components/FilterMenu/CategoriesMenu.vue
Normal file
@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<dropdown ref="category-menu" placement="top-start" :offset="8" class="category-menu">
|
||||
<a href="#" slot="default" slot-scope="{ toggleMenu }" @click.prevent="toggleMenu()">
|
||||
<ds-text bold size="large">{{ $t('admin.categories.name') }}</ds-text>
|
||||
</a>
|
||||
<template slot="popover">
|
||||
<div class="category-menu-options">
|
||||
<h2 class="title">{{ $t('filter-menu.filter-by') }}</h2>
|
||||
<categories-filter v-if="categoriesActive" />
|
||||
</div>
|
||||
</template>
|
||||
</dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dropdown from '~/components/Dropdown'
|
||||
import { mapGetters } from 'vuex'
|
||||
import CategoriesFilter from './CategoriesFilter'
|
||||
|
||||
export default {
|
||||
name: 'CategoriesMenu',
|
||||
components: {
|
||||
Dropdown,
|
||||
CategoriesFilter,
|
||||
},
|
||||
props: {
|
||||
placement: { type: String },
|
||||
offset: { type: [String, Number] },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
categoriesActive: this.$env.CATEGORIES_ACTIVE,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
// TODO: implement visibility of active filter later on
|
||||
filterActive: 'posts/isActive',
|
||||
}),
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.category-menu-options {
|
||||
max-width: $size-max-width-filter-menu;
|
||||
padding: $space-small $space-x-small;
|
||||
|
||||
> .title {
|
||||
font-size: $font-size-large;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -8,6 +8,9 @@ let wrapper
|
||||
describe('FilterMenu.vue', () => {
|
||||
const mocks = {
|
||||
$t: jest.fn((string) => string),
|
||||
$env: {
|
||||
CATEGORIES_ACTIVE: true,
|
||||
},
|
||||
}
|
||||
|
||||
const getters = {
|
||||
|
||||
@ -82,7 +82,7 @@ export default {
|
||||
try {
|
||||
await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation($id: ID!, $locale: String) {
|
||||
mutation ($id: ID!, $locale: String) {
|
||||
UpdateUser(id: $id, locale: $locale) {
|
||||
id
|
||||
locale
|
||||
@ -117,7 +117,7 @@ export default {
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding: $space-xx-small;
|
||||
color: $text-color-soft;
|
||||
color: $color-locale-menu;
|
||||
|
||||
> .label {
|
||||
margin: 0 $space-xx-small;
|
||||
|
||||
@ -12,6 +12,8 @@ config.stubs['nuxt-link'] = '<span><slot /></span>'
|
||||
config.stubs['locale-switch'] = '<span><slot /></span>'
|
||||
config.stubs['client-only'] = '<span><slot /></span>'
|
||||
|
||||
const authUserMock = jest.fn().mockReturnValue({ activeCategories: [] })
|
||||
|
||||
describe('LoginForm', () => {
|
||||
let mocks
|
||||
let propsData
|
||||
@ -26,10 +28,15 @@ describe('LoginForm', () => {
|
||||
storeMocks = {
|
||||
getters: {
|
||||
'auth/pending': () => false,
|
||||
'auth/user': authUserMock,
|
||||
},
|
||||
actions: {
|
||||
'auth/login': jest.fn(),
|
||||
},
|
||||
mutations: {
|
||||
'posts/TOGGLE_CATEGORY': jest.fn(),
|
||||
'posts/RESET_CATEGORIES': jest.fn(),
|
||||
},
|
||||
}
|
||||
const store = new Vuex.Store(storeMocks)
|
||||
mocks = {
|
||||
@ -43,20 +50,46 @@ describe('LoginForm', () => {
|
||||
}
|
||||
|
||||
describe('fill in email and password and submit', () => {
|
||||
const fillIn = (wrapper, opts = {}) => {
|
||||
const fillIn = async (wrapper, opts = {}) => {
|
||||
const { email = 'email@example.org', password = '1234' } = opts
|
||||
wrapper.find('input[name="email"]').setValue(email)
|
||||
wrapper.find('input[name="password"]').setValue(password)
|
||||
wrapper.find('form').trigger('submit')
|
||||
await wrapper.find('form').trigger('submit')
|
||||
}
|
||||
|
||||
it('dispatches login with form data', () => {
|
||||
fillIn(Wrapper())
|
||||
it('dispatches login with form data', async () => {
|
||||
await fillIn(Wrapper())
|
||||
expect(storeMocks.actions['auth/login']).toHaveBeenCalledWith(expect.any(Object), {
|
||||
email: 'email@example.org',
|
||||
password: '1234',
|
||||
})
|
||||
})
|
||||
|
||||
describe('setting saved categories', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('no categories saved', () => {
|
||||
it('resets the categories', async () => {
|
||||
await fillIn(Wrapper())
|
||||
expect(storeMocks.mutations['posts/RESET_CATEGORIES']).toBeCalled()
|
||||
expect(storeMocks.mutations['posts/TOGGLE_CATEGORY']).not.toBeCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('categories saved', () => {
|
||||
it('sets the categories', async () => {
|
||||
authUserMock.mockReturnValue({ activeCategories: ['cat1', 'cat9', 'cat12'] })
|
||||
await fillIn(Wrapper())
|
||||
expect(storeMocks.mutations['posts/RESET_CATEGORIES']).toBeCalled()
|
||||
expect(storeMocks.mutations['posts/TOGGLE_CATEGORY']).toBeCalledTimes(3)
|
||||
expect(storeMocks.mutations['posts/TOGGLE_CATEGORY']).toBeCalledWith({}, 'cat1')
|
||||
expect(storeMocks.mutations['posts/TOGGLE_CATEGORY']).toBeCalledWith({}, 'cat9')
|
||||
expect(storeMocks.mutations['posts/TOGGLE_CATEGORY']).toBeCalledWith({}, 'cat12')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Visibility of password', () => {
|
||||
|
||||
@ -58,6 +58,7 @@ import PageParamsLink from '~/components/_new/features/PageParamsLink/PageParams
|
||||
import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch'
|
||||
import Logo from '~/components/Logo/Logo'
|
||||
import ShowPassword from '../ShowPassword/ShowPassword.vue'
|
||||
import { mapGetters, mapMutations } from 'vuex'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -84,12 +85,27 @@ export default {
|
||||
iconName() {
|
||||
return this.showPassword ? 'eye-slash' : 'eye'
|
||||
},
|
||||
...mapGetters({
|
||||
currentUser: 'auth/user',
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
...mapMutations({
|
||||
toggleCategory: 'posts/TOGGLE_CATEGORY',
|
||||
resetCategories: 'posts/RESET_CATEGORIES',
|
||||
}),
|
||||
async onSubmit() {
|
||||
const { email, password } = this.form
|
||||
try {
|
||||
await this.$store.dispatch('auth/login', { email, password })
|
||||
if (this.currentUser && this.currentUser.activeCategories) {
|
||||
this.resetCategories()
|
||||
if (this.currentUser.activeCategories.length > 0) {
|
||||
this.currentUser.activeCategories.forEach((categoryId) => {
|
||||
this.toggleCategory(categoryId)
|
||||
})
|
||||
}
|
||||
}
|
||||
this.$toast.success(this.$t('login.success'))
|
||||
this.$emit('success')
|
||||
} catch (err) {
|
||||
|
||||
@ -59,7 +59,11 @@ export default {
|
||||
},
|
||||
data() {
|
||||
const logosObject = {
|
||||
header: { path: logos.LOGO_HEADER_PATH, alt: 'Header', widthDefault: '130px' },
|
||||
header: {
|
||||
path: logos.LOGO_HEADER_PATH,
|
||||
alt: 'Header',
|
||||
widthDefault: logos.LOGO_HEADER_WIDTH,
|
||||
},
|
||||
welcome: { path: logos.LOGO_WELCOME_PATH, alt: 'Welcome', widthDefault: '200px' },
|
||||
signup: { path: logos.LOGO_SIGNUP_PATH, alt: 'Sign Up', widthDefault: '200px' },
|
||||
logout: { path: logos.LOGO_LOGOUT_PATH, alt: 'Logging Out', widthDefault: '200px' },
|
||||
@ -107,7 +111,6 @@ export default {
|
||||
}
|
||||
|
||||
.ds-logo-svg {
|
||||
width: 130px;
|
||||
height: auto;
|
||||
fill: #000000;
|
||||
}
|
||||
|
||||
@ -8,10 +8,4 @@
|
||||
|
||||
.ds-logo-inverse {
|
||||
color: $text-color-primary-inverse;
|
||||
}
|
||||
|
||||
.ds-logo-svg {
|
||||
width: 130px;
|
||||
height: auto;
|
||||
fill: currentColor;
|
||||
}
|
||||
@ -127,7 +127,7 @@ export default {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: gql`
|
||||
mutation($id: ID!, $resource: [Deletable]) {
|
||||
mutation ($id: ID!, $resource: [Deletable]) {
|
||||
DeleteUser(id: $id, resource: $resource) {
|
||||
id
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ describe('DisableModal.vue', () => {
|
||||
$t: jest.fn(),
|
||||
$apollo: {
|
||||
mutate: jest.fn().mockResolvedValueOnce().mockRejectedValue({
|
||||
message: 'Not Authorised!',
|
||||
message: 'Not Authorized!',
|
||||
}),
|
||||
},
|
||||
location: {
|
||||
@ -184,7 +184,7 @@ describe('DisableModal.vue', () => {
|
||||
})
|
||||
|
||||
it('shows an error toaster when mutation rejects', async () => {
|
||||
await expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorised!')
|
||||
await expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorized!')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -53,7 +53,7 @@ export default {
|
||||
// await this.modalData.buttons.confirm.callback()
|
||||
await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation($resourceId: ID!, $disable: Boolean, $closed: Boolean) {
|
||||
mutation ($resourceId: ID!, $disable: Boolean, $closed: Boolean) {
|
||||
review(resourceId: $resourceId, disable: $disable, closed: $closed) {
|
||||
disable
|
||||
}
|
||||
|
||||
@ -38,11 +38,14 @@ export default {
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
z-index: 10;
|
||||
background-color: white;
|
||||
background-color: $color-footer-background;
|
||||
width: 100%;
|
||||
padding: 10px 10px;
|
||||
box-shadow: 0px -6px 12px -4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.ds-footer a {
|
||||
color: $color-footer-link;
|
||||
}
|
||||
.division-line {
|
||||
margin-left: 0.2rem;
|
||||
margin-right: 0.2rem;
|
||||
|
||||
@ -65,7 +65,7 @@ export default {
|
||||
async handleSubmit(data) {
|
||||
this.loading = true
|
||||
const mutation = gql`
|
||||
mutation($oldPassword: String!, $password: String!) {
|
||||
mutation ($oldPassword: String!, $password: String!) {
|
||||
changePassword(oldPassword: $oldPassword, newPassword: $password)
|
||||
}
|
||||
`
|
||||
|
||||
@ -93,7 +93,7 @@ export default {
|
||||
methods: {
|
||||
async handleSubmitPassword() {
|
||||
const mutation = gql`
|
||||
mutation($nonce: String!, $email: String!, $password: String!) {
|
||||
mutation ($nonce: String!, $email: String!, $password: String!) {
|
||||
resetPassword(nonce: $nonce, email: $email, newPassword: $password)
|
||||
}
|
||||
`
|
||||
|
||||
@ -85,7 +85,7 @@ export default {
|
||||
},
|
||||
async handleSubmit() {
|
||||
const mutation = gql`
|
||||
mutation($email: String!) {
|
||||
mutation ($email: String!) {
|
||||
requestPasswordReset(email: $email)
|
||||
}
|
||||
`
|
||||
|
||||
@ -47,6 +47,9 @@ describe('PostTeaser', () => {
|
||||
data: { DeletePost: { id: 'deleted-post-id' } },
|
||||
}),
|
||||
},
|
||||
$env: {
|
||||
CATEGORIES_ACTIVE: false,
|
||||
},
|
||||
}
|
||||
getters = {
|
||||
'auth/isModerator': () => false,
|
||||
|
||||
@ -17,8 +17,7 @@ export const post = {
|
||||
author: {
|
||||
id: 'u3',
|
||||
avatar: {
|
||||
url:
|
||||
'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg',
|
||||
url: 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg',
|
||||
},
|
||||
slug: 'jenny-rostock',
|
||||
name: 'Rainer Unsinn',
|
||||
|
||||
@ -26,7 +26,19 @@
|
||||
class="footer"
|
||||
v-observe-visibility="(isVisible, entry) => visibilityChanged(isVisible, entry, post.id)"
|
||||
>
|
||||
<div class="categories-placeholder"></div>
|
||||
<div class="categories" v-if="categoriesActive">
|
||||
<hc-category
|
||||
v-for="category in post.categories"
|
||||
:key="category.id"
|
||||
v-tooltip="{
|
||||
content: $t(`contribution.category.name.${category.slug}`),
|
||||
placement: 'bottom-start',
|
||||
delay: { show: 1500 },
|
||||
}"
|
||||
:icon="category.icon"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="categories-placeholder"></div>
|
||||
<counter-icon
|
||||
icon="bullhorn"
|
||||
:count="post.shoutedCount"
|
||||
@ -70,6 +82,7 @@
|
||||
import UserTeaser from '~/components/UserTeaser/UserTeaser'
|
||||
import ContentMenu from '~/components/ContentMenu/ContentMenu'
|
||||
import HcRibbon from '~/components/Ribbon'
|
||||
import HcCategory from '~/components/Category'
|
||||
import CounterIcon from '~/components/_new/generic/CounterIcon/CounterIcon'
|
||||
import { mapGetters } from 'vuex'
|
||||
import PostMutations from '~/graphql/PostMutations'
|
||||
@ -79,6 +92,7 @@ export default {
|
||||
name: 'PostTeaser',
|
||||
components: {
|
||||
UserTeaser,
|
||||
HcCategory,
|
||||
HcRibbon,
|
||||
ContentMenu,
|
||||
CounterIcon,
|
||||
@ -93,6 +107,11 @@ export default {
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
categoriesActive: this.$env.CATEGORIES_ACTIVE,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const { image } = this.post
|
||||
if (!image) return
|
||||
|
||||
@ -35,7 +35,7 @@ import normalizeEmail from '~/components/utils/NormalizeEmail'
|
||||
import translateErrorMessage from '~/components/utils/TranslateErrorMessage'
|
||||
|
||||
export const SignupMutation = gql`
|
||||
mutation($email: String!, $inviteCode: String) {
|
||||
mutation ($email: String!, $inviteCode: String) {
|
||||
Signup(email: $email, inviteCode: $inviteCode) {
|
||||
email
|
||||
}
|
||||
@ -165,9 +165,8 @@ export default {
|
||||
})
|
||||
this.setButtonValues()
|
||||
|
||||
const { email: responseEmail } = this.sliderData.sliders[
|
||||
this.sliderIndex
|
||||
].data.response.Signup
|
||||
const { email: responseEmail } =
|
||||
this.sliderData.sliders[this.sliderIndex].data.response.Signup
|
||||
this.$toast.success(
|
||||
this.$t('components.registration.email.form.success', { email: responseEmail }),
|
||||
)
|
||||
|
||||