diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index ac333604e..d4ecca7f5 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -3,8 +3,8 @@ name: ocelot.social publish CI
on:
push:
branches:
- - master
- # - 5093-fix-automatic-deployment # for testing while developing
+ # - master
+ - 5059-epic-groups # for testing while developing
jobs:
##############################################################################
@@ -94,16 +94,16 @@ jobs:
##########################################################################
- name: Backend | Build `production` image
run: |
- docker build --target base -t "ocelotsocialnetwork/backend:latest-base" -t "ocelotsocialnetwork/backend:${VERSION}-base" -t "ocelotsocialnetwork/backend:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
- docker build --target code -t "ocelotsocialnetwork/backend:latest-code" -t "ocelotsocialnetwork/backend:${VERSION}-code" -t "ocelotsocialnetwork/backend:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
- docker build --target production -t "ocelotsocialnetwork/backend:latest" -t "ocelotsocialnetwork/backend:${VERSION}" -t "ocelotsocialnetwork/backend:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
+ docker build --target base -t "ocelotsocialnetwork/backend-groups:latest-base" -t "ocelotsocialnetwork/backend-groups:${VERSION}-base" -t "ocelotsocialnetwork/backend-groups:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
+ docker build --target code -t "ocelotsocialnetwork/backend-groups:latest-code" -t "ocelotsocialnetwork/backend-groups:${VERSION}-code" -t "ocelotsocialnetwork/backend-groups:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
+ docker build --target production -t "ocelotsocialnetwork/backend-groups:latest" -t "ocelotsocialnetwork/backend-groups:${VERSION}" -t "ocelotsocialnetwork/backend-groups:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
- name: Backend | Save docker image
- run: docker save "ocelotsocialnetwork/backend" > /tmp/backend.tar
+ run: docker save "ocelotsocialnetwork/backend-groups" > /tmp/backend-groups.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: docker-backend-production
- path: /tmp/backend.tar
+ path: /tmp/backend-groups.tar
##############################################################################
# JOB: DOCKER BUILD PRODUCTION WEBAPP ########################################
@@ -134,16 +134,16 @@ jobs:
##########################################################################
- name: Webapp | Build `production` image
run: |
- docker build --target base -t "ocelotsocialnetwork/webapp:latest-base" -t "ocelotsocialnetwork/webapp:${VERSION}-base" -t "ocelotsocialnetwork/webapp:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
- docker build --target code -t "ocelotsocialnetwork/webapp:latest-code" -t "ocelotsocialnetwork/webapp:${VERSION}-code" -t "ocelotsocialnetwork/webapp:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
- docker build --target production -t "ocelotsocialnetwork/webapp:latest" -t "ocelotsocialnetwork/webapp:${VERSION}" -t "ocelotsocialnetwork/webapp:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
+ docker build --target base -t "ocelotsocialnetwork/webapp-groups:latest-base" -t "ocelotsocialnetwork/webapp-groups:${VERSION}-base" -t "ocelotsocialnetwork/webapp-groups:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
+ docker build --target code -t "ocelotsocialnetwork/webapp-groups:latest-code" -t "ocelotsocialnetwork/webapp-groups:${VERSION}-code" -t "ocelotsocialnetwork/webapp-groups:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
+ docker build --target production -t "ocelotsocialnetwork/webapp-groups:latest" -t "ocelotsocialnetwork/webapp-groups:${VERSION}" -t "ocelotsocialnetwork/webapp-groups:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
- name: Webapp | Save docker image
- run: docker save "ocelotsocialnetwork/webapp" > /tmp/webapp.tar
+ run: docker save "ocelotsocialnetwork/webapp-groups" > /tmp/webapp-groups.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: docker-webapp-production
- path: /tmp/webapp.tar
+ path: /tmp/webapp-groups.tar
##############################################################################
# JOB: DOCKER BUILD PRODUCTION MAINTENANCE ###################################
@@ -174,16 +174,16 @@ jobs:
##########################################################################
- name: Maintenance | Build `production` image
run: |
- docker build --target base -t "ocelotsocialnetwork/maintenance:latest-base" -t "ocelotsocialnetwork/maintenance:${VERSION}-base" -t "ocelotsocialnetwork/maintenance:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
- docker build --target code -t "ocelotsocialnetwork/maintenance:latest-code" -t "ocelotsocialnetwork/maintenance:${VERSION}-code" -t "ocelotsocialnetwork/maintenance:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
- docker build --target production -t "ocelotsocialnetwork/maintenance:latest" -t "ocelotsocialnetwork/maintenance:${VERSION}" -t "ocelotsocialnetwork/maintenance:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
+ docker build --target base -t "ocelotsocialnetwork/maintenance-groups:latest-base" -t "ocelotsocialnetwork/maintenance-groups:${VERSION}-base" -t "ocelotsocialnetwork/maintenance-groups:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
+ docker build --target code -t "ocelotsocialnetwork/maintenance-groups:latest-code" -t "ocelotsocialnetwork/maintenance-groups:${VERSION}-code" -t "ocelotsocialnetwork/maintenance-groups:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
+ docker build --target production -t "ocelotsocialnetwork/maintenance-groups:latest" -t "ocelotsocialnetwork/maintenance-groups:${VERSION}" -t "ocelotsocialnetwork/maintenance-groups:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
- name: Maintenance | Save docker image
- run: docker save "ocelotsocialnetwork/maintenance" > /tmp/maintenance.tar
+ run: docker save "ocelotsocialnetwork/maintenance-groups" > /tmp/maintenance-groups.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: docker-maintenance-production
- path: /tmp/maintenance.tar
+ path: /tmp/maintenance-groups.tar
##############################################################################
# JOB: UPLOAD TO DOCKERHUB ###################################################
@@ -217,21 +217,21 @@ jobs:
name: docker-backend-production
path: /tmp
- name: Load Docker Image
- run: docker load < /tmp/backend.tar
+ run: docker load < /tmp/backend-groups.tar
- name: Download Docker Image (WebApp)
uses: actions/download-artifact@v2
with:
name: docker-webapp-production
path: /tmp
- name: Load Docker Image
- run: docker load < /tmp/webapp.tar
+ run: docker load < /tmp/webapp-groups.tar
- name: Download Docker Image (Maintenance)
uses: actions/download-artifact@v2
with:
name: docker-maintenance-production
path: /tmp
- name: Load Docker Image
- run: docker load < /tmp/maintenance.tar
+ run: docker load < /tmp/maintenance-groups.tar
##########################################################################
# Upload #################################################################
##########################################################################
@@ -240,11 +240,11 @@ jobs:
- name: Push neo4j
run: docker push --all-tags ocelotsocialnetwork/neo4j-community
- name: Push backend
- run: docker push --all-tags ocelotsocialnetwork/backend
+ run: docker push --all-tags ocelotsocialnetwork/backend-groups
- name: Push webapp
- run: docker push --all-tags ocelotsocialnetwork/webapp
+ run: docker push --all-tags ocelotsocialnetwork/webapp-groups
- name: Push maintenance
- run: docker push --all-tags ocelotsocialnetwork/maintenance
+ run: docker push --all-tags ocelotsocialnetwork/maintenance-groups
##############################################################################
# JOB: KUBERNETES DEPLOY ACTUAL/LATEST VERSION ######################################
@@ -292,11 +292,11 @@ jobs:
# kubectl -n default rollout restart deployment/ocelot-neo4j
- name: Deploy actual version '$BUILD_VERSION' to DigitalOcean Kubernetes
run: |
- kubectl -n default set image deployment/ocelot-webapp container-ocelot-webapp=ocelotsocialnetwork/webapp:$BUILD_VERSION
+ kubectl -n default set image deployment/ocelot-webapp container-ocelot-webapp=ocelotsocialnetwork/webapp-groups:$BUILD_VERSION
kubectl -n default rollout restart deployment/ocelot-webapp
- kubectl -n default set image deployment/ocelot-backend container-ocelot-backend=ocelotsocialnetwork/backend:$BUILD_VERSION
+ kubectl -n default set image deployment/ocelot-backend container-ocelot-backend=ocelotsocialnetwork/backend-groups:$BUILD_VERSION
kubectl -n default rollout restart deployment/ocelot-backend
- kubectl -n default set image deployment/ocelot-maintenance container-ocelot-maintenance=ocelotsocialnetwork/maintenance:$BUILD_VERSION
+ kubectl -n default set image deployment/ocelot-maintenance container-ocelot-maintenance=ocelotsocialnetwork/maintenance-groups:$BUILD_VERSION
kubectl -n default rollout restart deployment/ocelot-maintenance
kubectl -n default set image deployment/ocelot-neo4j container-ocelot-neo4j=ocelotsocialnetwork/neo4j-community:$BUILD_VERSION
kubectl -n default rollout restart deployment/ocelot-neo4j
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 74f2c1dcc..940cc77a8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,11 +4,50 @@ 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.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/1.0.8...1.1.0)
+#### [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)
diff --git a/backend/package.json b/backend/package.json
index f6299ee48..bcc91a25a 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -1,6 +1,6 @@
{
"name": "ocelot-social-backend",
- "version": "1.1.0",
+ "version": "1.1.1",
"description": "GraphQL Backend for ocelot.social",
"repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social",
"author": "ocelot.social Community",
diff --git a/backend/src/constants/groups.js b/backend/src/constants/groups.js
index 64ffeaa59..e9c941a89 100644
--- a/backend/src/constants/groups.js
+++ b/backend/src/constants/groups.js
@@ -1,3 +1,3 @@
// this file is duplicated in `backend/src/constants/group.js` and `webapp/constants/group.js`
export const DESCRIPTION_WITHOUT_HTML_LENGTH_MIN = 100 // with removed HTML tags
-export const DESCRIPTION_EXCERPT_HTML_LENGTH = 120 // with removed HTML tags
+export const DESCRIPTION_EXCERPT_HTML_LENGTH = 250 // with removed HTML tags
diff --git a/backend/src/db/graphql/groups.js b/backend/src/db/graphql/groups.js
index 4350e19c9..e388b2cd9 100644
--- a/backend/src/db/graphql/groups.js
+++ b/backend/src/db/graphql/groups.js
@@ -2,170 +2,202 @@ import gql from 'graphql-tag'
// ------ mutations
-export const createGroupMutation = gql`
- mutation (
- $id: ID
- $name: String!
- $slug: String
- $about: String
- $description: String!
- $groupType: GroupType!
- $actionRadius: GroupActionRadius!
- $categoryIds: [ID]
- $locationName: String
- ) {
- CreateGroup(
- id: $id
- name: $name
- slug: $slug
- about: $about
- description: $description
- groupType: $groupType
- actionRadius: $actionRadius
- categoryIds: $categoryIds
- locationName: $locationName
+export const createGroupMutation = () => {
+ return gql`
+ mutation (
+ $id: ID
+ $name: String!
+ $slug: String
+ $about: String
+ $description: String!
+ $groupType: GroupType!
+ $actionRadius: GroupActionRadius!
+ $categoryIds: [ID]
+ $locationName: String # empty string '' sets it to null
) {
- id
- name
- slug
- createdAt
- updatedAt
- disabled
- deleted
- about
- description
- groupType
- actionRadius
- categories {
+ CreateGroup(
+ id: $id
+ name: $name
+ slug: $slug
+ about: $about
+ description: $description
+ groupType: $groupType
+ actionRadius: $actionRadius
+ categoryIds: $categoryIds
+ locationName: $locationName
+ ) {
id
- slug
name
- icon
+ slug
+ createdAt
+ updatedAt
+ disabled
+ deleted
+ about
+ description
+ descriptionExcerpt
+ groupType
+ actionRadius
+ categories {
+ id
+ slug
+ name
+ icon
+ }
+ locationName
+ location {
+ name
+ nameDE
+ nameEN
+ }
+ myRole
}
- # locationName # test this as result
- myRole
}
- }
-`
+ `
+}
-export const updateGroupMutation = gql`
- mutation (
- $id: ID!
- $name: String
- $slug: String
- $about: String
- $description: String
- $actionRadius: GroupActionRadius
- $categoryIds: [ID]
- $avatar: ImageInput
- $locationName: String
- ) {
- UpdateGroup(
- id: $id
- name: $name
- slug: $slug
- about: $about
- description: $description
- actionRadius: $actionRadius
- categoryIds: $categoryIds
- avatar: $avatar
- locationName: $locationName
+export const updateGroupMutation = () => {
+ return gql`
+ mutation (
+ $id: ID!
+ $name: String
+ $slug: String
+ $about: String
+ $description: String
+ $actionRadius: GroupActionRadius
+ $categoryIds: [ID]
+ $avatar: ImageInput
+ $locationName: String # empty string '' sets it to null
) {
- id
- name
- slug
- createdAt
- updatedAt
- disabled
- deleted
- about
- description
- groupType
- actionRadius
- categories {
+ UpdateGroup(
+ id: $id
+ name: $name
+ slug: $slug
+ about: $about
+ description: $description
+ actionRadius: $actionRadius
+ categoryIds: $categoryIds
+ avatar: $avatar
+ locationName: $locationName
+ ) {
id
- slug
name
- icon
+ slug
+ createdAt
+ updatedAt
+ disabled
+ deleted
+ about
+ description
+ descriptionExcerpt
+ groupType
+ actionRadius
+ categories {
+ id
+ slug
+ name
+ icon
+ }
+ # avatar # test this as result
+ locationName
+ location {
+ name
+ nameDE
+ nameEN
+ }
+ myRole
}
- # avatar # test this as result
- # locationName # test this as result
- myRole
}
- }
-`
+ `
+}
-export const joinGroupMutation = gql`
- mutation ($groupId: ID!, $userId: ID!) {
- JoinGroup(groupId: $groupId, userId: $userId) {
- id
- name
- slug
- myRoleInGroup
+export const joinGroupMutation = () => {
+ return gql`
+ mutation ($groupId: ID!, $userId: ID!) {
+ JoinGroup(groupId: $groupId, userId: $userId) {
+ id
+ name
+ slug
+ myRoleInGroup
+ }
}
- }
-`
+ `
+}
-export const leaveGroupMutation = gql`
- mutation ($groupId: ID!, $userId: ID!) {
- LeaveGroup(groupId: $groupId, userId: $userId) {
- id
- name
- slug
- myRoleInGroup
+export const leaveGroupMutation = () => {
+ return gql`
+ mutation ($groupId: ID!, $userId: ID!) {
+ LeaveGroup(groupId: $groupId, userId: $userId) {
+ id
+ name
+ slug
+ myRoleInGroup
+ }
}
- }
-`
+ `
+}
-export const changeGroupMemberRoleMutation = gql`
- mutation ($groupId: ID!, $userId: ID!, $roleInGroup: GroupMemberRole!) {
- ChangeGroupMemberRole(groupId: $groupId, userId: $userId, roleInGroup: $roleInGroup) {
- id
- name
- slug
- myRoleInGroup
+export const changeGroupMemberRoleMutation = () => {
+ return gql`
+ mutation ($groupId: ID!, $userId: ID!, $roleInGroup: GroupMemberRole!) {
+ ChangeGroupMemberRole(groupId: $groupId, userId: $userId, roleInGroup: $roleInGroup) {
+ id
+ name
+ slug
+ myRoleInGroup
+ }
}
- }
-`
+ `
+}
// ------ queries
-export const groupQuery = gql`
- query ($isMember: Boolean, $id: ID, $slug: String) {
- Group(isMember: $isMember, id: $id, slug: $slug) {
- id
- name
- slug
- createdAt
- updatedAt
- disabled
- deleted
- about
- description
- groupType
- actionRadius
- categories {
+export const groupQuery = () => {
+ return gql`
+ query ($isMember: Boolean, $id: ID, $slug: String) {
+ Group(isMember: $isMember, id: $id, slug: $slug) {
id
- slug
name
- icon
+ slug
+ createdAt
+ updatedAt
+ disabled
+ deleted
+ about
+ description
+ descriptionExcerpt
+ groupType
+ actionRadius
+ categories {
+ id
+ slug
+ name
+ icon
+ }
+ avatar {
+ url
+ }
+ locationName
+ location {
+ name
+ nameDE
+ nameEN
+ }
+ myRole
}
- avatar {
- url
- }
- # locationName # test this as result
- myRole
}
- }
-`
+ `
+}
-export const groupMembersQuery = gql`
- query ($id: ID!) {
- GroupMembers(id: $id) {
- id
- name
- slug
- myRoleInGroup
+export const groupMembersQuery = () => {
+ return gql`
+ query ($id: ID!) {
+ GroupMembers(id: $id) {
+ id
+ name
+ slug
+ myRoleInGroup
+ }
}
- }
-`
+ `
+}
diff --git a/backend/src/db/seed.js b/backend/src/db/seed.js
index 632aa8f4d..d693f9905 100644
--- a/backend/src/db/seed.js
+++ b/backend/src/db/seed.js
@@ -305,7 +305,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
authenticatedUser = await peterLustig.toJson()
await Promise.all([
mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'g0',
name: 'Investigative Journalism',
@@ -313,27 +313,28 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
description: `
English:
This group is hidden.
What is our group for? This group was created to allow investigative journalists to share and collaborate.
How does it work? Here you can internally share posts and comments about them.
Deutsch:
Diese Gruppe ist verborgen.
Wofür ist unsere Gruppe? Diese Gruppe wurde geschaffen, um investigativen Journalisten den Austausch und die Zusammenarbeit zu ermöglichen.
Wie funktioniert das? Hier könnt ihr euch intern über Beiträge und Kommentare zu ihnen austauschen.
`,
groupType: 'hidden',
actionRadius: 'global',
- categoryIds: ['cat6', 'cat9', 'cat14'],
+ categoryIds: ['cat6', 'cat12', 'cat16'],
+ locationName: 'Hamburg, Germany',
},
}),
])
await Promise.all([
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'g0',
userId: 'u2',
},
}),
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'g0',
userId: 'u4',
},
}),
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'g0',
userId: 'u6',
@@ -342,7 +343,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
])
await Promise.all([
mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'g0',
userId: 'u2',
@@ -350,7 +351,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
},
}),
mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'g0',
userId: 'u4',
@@ -362,7 +363,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
authenticatedUser = await jennyRostock.toJson()
await Promise.all([
mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'g1',
name: 'School For Citizens',
@@ -370,41 +371,42 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
description: `English
Our goal Only those who enjoy learning and do not lose their curiosity can obtain a good education for life and continue to learn with joy throughout their lives.
Curiosity For this we need a school that takes up the curiosity of the children, the people, and satisfies it through a lot of experience.
Deutsch
Unser Ziel Nur wer Spaß am Lernen hat und seine Neugier nicht verliert, kann gute Bildung für's Leben erlangen und sein ganzes Leben mit Freude weiter lernen.
Neugier Dazu benötigen wir eine Schule, die die Neugier der Kinder, der Menschen, aufnimmt und durch viel Erfahrung befriedigt.
`,
groupType: 'closed',
actionRadius: 'national',
- categoryIds: ['cat7', 'cat9', 'cat16'],
+ categoryIds: ['cat8', 'cat14'],
+ locationName: 'France',
},
}),
])
await Promise.all([
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'g1',
userId: 'u1',
},
}),
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'g1',
userId: 'u2',
},
}),
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'g1',
userId: 'u5',
},
}),
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'g1',
userId: 'u6',
},
}),
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'g1',
userId: 'u7',
@@ -413,7 +415,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
])
await Promise.all([
mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'g1',
userId: 'u1',
@@ -421,7 +423,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
},
}),
mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'g1',
userId: 'u5',
@@ -429,7 +431,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
},
}),
mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'g1',
userId: 'u6',
@@ -441,7 +443,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
authenticatedUser = await bobDerBaumeister.toJson()
await Promise.all([
mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'g2',
name: 'Yoga Practice',
@@ -449,41 +451,41 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
description: `What Is yoga? Yoga is not just about practicing asanas. It's about how we do it.
And practicing asanas doesn't have to be yoga, it can be more athletic than yogic.
What makes practicing asanas yogic? The important thing is:
`,
groupType: 'public',
actionRadius: 'interplanetary',
- categoryIds: ['cat3', 'cat13', 'cat16'],
+ categoryIds: ['cat4', 'cat5', 'cat17'],
},
}),
])
await Promise.all([
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'g2',
userId: 'u3',
},
}),
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'g2',
userId: 'u4',
},
}),
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'g2',
userId: 'u5',
},
}),
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'g2',
userId: 'u6',
},
}),
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'g2',
userId: 'u7',
@@ -492,31 +494,31 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
])
await Promise.all([
mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'g2',
userId: 'u3',
+ roleInGroup: 'usual',
+ },
+ }),
+ mutate({
+ mutation: changeGroupMemberRoleMutation(),
+ variables: {
+ groupId: 'g2',
+ userId: 'u4',
roleInGroup: 'pending',
},
}),
mutate({
- mutation: changeGroupMemberRoleMutation,
- variables: {
- groupId: 'g2',
- userId: 'u4',
- roleInGroup: 'usual',
- },
- }),
- mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'g2',
userId: 'u5',
- roleInGroup: 'usual',
+ roleInGroup: 'admin',
},
}),
mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'g2',
userId: 'u6',
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index df2326db5..b73ba166f 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -82,7 +82,7 @@ const isAllowedToChangeGroupSettings = rule({
}
})
-const isAllowedSeeingMembersOfGroup = rule({
+const isAllowedSeeingGroupMembers = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
if (!(user && user.id)) return false
@@ -312,7 +312,7 @@ export default shield(
statistics: allow,
currentUser: allow,
Group: isAuthenticated,
- GroupMembers: isAllowedSeeingMembersOfGroup,
+ GroupMembers: isAllowedSeeingGroupMembers,
Post: allow,
profilePagePosts: allow,
Comment: allow,
diff --git a/backend/src/middleware/slugifyMiddleware.spec.js b/backend/src/middleware/slugifyMiddleware.spec.js
index edb6b64eb..c600d0e52 100644
--- a/backend/src/middleware/slugifyMiddleware.spec.js
+++ b/backend/src/middleware/slugifyMiddleware.spec.js
@@ -51,6 +51,7 @@ beforeEach(async () => {
await Factory.build('category', {
id: 'cat9',
name: 'Democracy & Politics',
+ slug: 'democracy-politics',
icon: 'university',
})
authenticatedUser = await admin.toJson()
@@ -79,7 +80,7 @@ describe('slugifyMiddleware', () => {
it('generates a slug based on name', async () => {
await expect(
mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables,
}),
).resolves.toMatchObject({
@@ -93,13 +94,14 @@ describe('slugifyMiddleware', () => {
actionRadius: 'national',
},
},
+ errors: undefined,
})
})
it('generates a slug based on given slug', async () => {
await expect(
mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
...variables,
slug: 'the-group',
@@ -111,6 +113,7 @@ describe('slugifyMiddleware', () => {
slug: 'the-group',
},
},
+ errors: undefined,
})
})
})
@@ -118,7 +121,7 @@ describe('slugifyMiddleware', () => {
describe('if slug exists', () => {
beforeEach(async () => {
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
...variables,
name: 'Pre-Existing Group',
@@ -131,7 +134,7 @@ describe('slugifyMiddleware', () => {
it('chooses another slug', async () => {
await expect(
mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
...variables,
name: 'Pre-Existing Group',
@@ -144,6 +147,7 @@ describe('slugifyMiddleware', () => {
slug: 'pre-existing-group-1',
},
},
+ errors: undefined,
})
})
@@ -152,7 +156,7 @@ describe('slugifyMiddleware', () => {
try {
await expect(
mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
...variables,
name: 'Pre-Existing Group',
@@ -194,7 +198,7 @@ describe('slugifyMiddleware', () => {
beforeEach(async () => {
createGroupResult = await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
name: 'The Best Group',
slug: 'the-best-group',
@@ -213,7 +217,7 @@ describe('slugifyMiddleware', () => {
it('has the new slug', async () => {
await expect(
mutate({
- mutation: updateGroupMutation,
+ mutation: updateGroupMutation(),
variables: {
id: createGroupResult.data.CreateGroup.id,
name: 'My Best Group',
@@ -231,6 +235,7 @@ describe('slugifyMiddleware', () => {
myRole: 'owner',
},
},
+ errors: undefined,
})
})
})
@@ -239,7 +244,7 @@ describe('slugifyMiddleware', () => {
it('has the new slug', async () => {
await expect(
mutate({
- mutation: updateGroupMutation,
+ mutation: updateGroupMutation(),
variables: {
id: createGroupResult.data.CreateGroup.id,
slug: 'my-best-group',
@@ -257,6 +262,7 @@ describe('slugifyMiddleware', () => {
myRole: 'owner',
},
},
+ errors: undefined,
})
})
})
@@ -265,7 +271,7 @@ describe('slugifyMiddleware', () => {
describe('if new slug exists in another group', () => {
beforeEach(async () => {
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
name: 'Pre-Existing Group',
slug: 'pre-existing-group',
@@ -282,7 +288,7 @@ describe('slugifyMiddleware', () => {
it('has unique slug "*-1"', async () => {
await expect(
mutate({
- mutation: updateGroupMutation,
+ mutation: updateGroupMutation(),
variables: {
id: createGroupResult.data.CreateGroup.id,
name: 'Pre-Existing Group',
@@ -300,6 +306,7 @@ describe('slugifyMiddleware', () => {
myRole: 'owner',
},
},
+ errors: undefined,
})
})
})
@@ -309,7 +316,7 @@ describe('slugifyMiddleware', () => {
try {
await expect(
mutate({
- mutation: updateGroupMutation,
+ mutation: updateGroupMutation(),
variables: {
id: createGroupResult.data.CreateGroup.id,
slug: 'pre-existing-group',
@@ -368,6 +375,7 @@ describe('slugifyMiddleware', () => {
slug: 'i-am-a-brand-new-post',
},
},
+ errors: undefined,
})
})
@@ -386,6 +394,7 @@ describe('slugifyMiddleware', () => {
slug: 'the-post',
},
},
+ errors: undefined,
})
})
})
@@ -422,6 +431,7 @@ describe('slugifyMiddleware', () => {
slug: 'pre-existing-post-1',
},
},
+ errors: undefined,
})
})
@@ -504,6 +514,7 @@ describe('slugifyMiddleware', () => {
slug: 'i-am-a-user',
},
},
+ errors: undefined,
})
})
@@ -522,6 +533,7 @@ describe('slugifyMiddleware', () => {
slug: 'the-user',
},
},
+ errors: undefined,
})
})
})
@@ -546,6 +558,7 @@ describe('slugifyMiddleware', () => {
slug: 'i-am-a-user-1',
},
},
+ errors: undefined,
})
})
diff --git a/backend/src/schema/index.js b/backend/src/schema/index.js
index 612487147..06e150c86 100644
--- a/backend/src/schema/index.js
+++ b/backend/src/schema/index.js
@@ -20,6 +20,7 @@ export default makeAugmentedSchema({
'FILED',
'REVIEWED',
'Report',
+ 'Group',
],
},
mutation: false,
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index 8d750284d..7a6298047 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -9,6 +9,7 @@ import Resolver, {
convertObjectToCypherMapLiteral,
} from './helpers/Resolver'
import { mergeImage } from './images/images'
+import { createOrUpdateLocations } from './users/location'
export default {
Query: {
@@ -85,6 +86,7 @@ export default {
CreateGroup: async (_parent, params, context, _resolveInfo) => {
const { categoryIds } = params
delete params.categoryIds
+ params.locationName = params.locationName === '' ? null : params.locationName
if (CONFIG.CATEGORIES_ACTIVE && (!categoryIds || categoryIds.length < CATEGORIES_MIN)) {
throw new UserInputError('Too view categories!')
}
@@ -135,7 +137,10 @@ export default {
return group
})
try {
- return await writeTxResultPromise
+ const group = await writeTxResultPromise
+ // TODO: put in a middleware, see "UpdateGroup", "UpdateUser"
+ await createOrUpdateLocations('Group', params.id, params.locationName, session)
+ return group
} catch (error) {
if (error.code === 'Neo.ClientError.Schema.ConstraintValidationFailed')
throw new UserInputError('Group with this slug already exists!')
@@ -146,9 +151,11 @@ export default {
},
UpdateGroup: async (_parent, params, context, _resolveInfo) => {
const { categoryIds } = params
- const { id: groupId, avatar: avatarInput } = params
delete params.categoryIds
+ const { id: groupId, avatar: avatarInput } = params
delete params.avatar
+ params.locationName = params.locationName === '' ? null : params.locationName
+
if (CONFIG.CATEGORIES_ACTIVE && categoryIds) {
if (categoryIds.length < CATEGORIES_MIN) {
throw new UserInputError('Too view categories!')
@@ -206,7 +213,10 @@ export default {
return group
})
try {
- return await writeTxResultPromise
+ const group = await writeTxResultPromise
+ // TODO: put in a middleware, see "CreateGroup", "UpdateUser"
+ await createOrUpdateLocations('Group', params.id, params.locationName, session)
+ return group
} catch (error) {
if (error.code === 'Neo.ClientError.Schema.ConstraintValidationFailed')
throw new UserInputError('Group with this slug already exists!')
@@ -300,12 +310,14 @@ export default {
},
Group: {
...Resolver('Group', {
+ undefinedToNull: ['deleted', 'disabled', 'locationName', 'about'],
hasMany: {
categories: '-[:CATEGORIZED]->(related:Category)',
posts: '<-[:IN]-(related:Post)',
},
hasOne: {
avatar: '-[:AVATAR_IMAGE]->(related:Image)',
+ location: '-[:IS_IN]->(related:Location)',
},
}),
},
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index b4653edaf..d707440a4 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -55,18 +55,18 @@ const seedBasicsAndClearAuthentication = async () => {
},
)
await Promise.all([
- neode.create('Category', {
- id: 'cat9',
- name: 'Democracy & Politics',
- slug: 'democracy-politics',
- icon: 'university',
- }),
neode.create('Category', {
id: 'cat4',
name: 'Environment & Nature',
slug: 'environment-nature',
icon: 'tree',
}),
+ neode.create('Category', {
+ id: 'cat9',
+ name: 'Democracy & Politics',
+ slug: 'democracy-politics',
+ icon: 'university',
+ }),
neode.create('Category', {
id: 'cat15',
name: 'Consumption & Sustainability',
@@ -156,7 +156,7 @@ const seedComplexScenarioAndClearAuthentication = async () => {
// public-group
authenticatedUser = await usualMemberUser.toJson()
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'public-group',
name: 'The Best Group',
@@ -168,14 +168,14 @@ const seedComplexScenarioAndClearAuthentication = async () => {
},
})
await mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'public-group',
userId: 'owner-of-closed-group',
},
})
await mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'public-group',
userId: 'owner-of-hidden-group',
@@ -184,7 +184,7 @@ const seedComplexScenarioAndClearAuthentication = async () => {
// closed-group
authenticatedUser = await ownerMemberUser.toJson()
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'closed-group',
name: 'Uninteresting Group',
@@ -198,7 +198,7 @@ const seedComplexScenarioAndClearAuthentication = async () => {
// hidden-group
authenticatedUser = await adminMemberUser.toJson()
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'hidden-group',
name: 'Investigative Journalism Group',
@@ -211,7 +211,7 @@ const seedComplexScenarioAndClearAuthentication = async () => {
})
// 'JoinGroup' mutation does not work in hidden groups so we join them by 'ChangeGroupMemberRole' through the owner
await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'hidden-group',
userId: 'admin-member-user',
@@ -219,7 +219,7 @@ const seedComplexScenarioAndClearAuthentication = async () => {
},
})
await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'hidden-group',
userId: 'second-owner-member-user',
@@ -227,7 +227,7 @@ const seedComplexScenarioAndClearAuthentication = async () => {
},
})
await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'hidden-group',
userId: 'admin-member-user',
@@ -235,7 +235,7 @@ const seedComplexScenarioAndClearAuthentication = async () => {
},
})
await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'hidden-group',
userId: 'second-owner-member-user',
@@ -277,13 +277,13 @@ describe('in mode', () => {
groupType: 'public',
actionRadius: 'regional',
categoryIds,
- // locationName, // test this as result
+ locationName: 'Hamburg, Germany',
}
})
describe('unauthenticated', () => {
it('throws authorization error', async () => {
- const { errors } = await mutate({ mutation: createGroupMutation, variables })
+ const { errors } = await mutate({ mutation: createGroupMutation(), variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -294,43 +294,50 @@ describe('in mode', () => {
})
it('creates a group', async () => {
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
- {
- data: {
- CreateGroup: {
- name: 'The Best Group',
- slug: 'the-group',
- about: 'We will change the world!',
- description: 'Some description' + descriptionAdditional100,
- groupType: 'public',
- actionRadius: 'regional',
- },
+ await expect(
+ mutate({ mutation: createGroupMutation(), variables }),
+ ).resolves.toMatchObject({
+ data: {
+ CreateGroup: {
+ name: 'The Best Group',
+ slug: 'the-group',
+ about: 'We will change the world!',
+ description: 'Some description' + descriptionAdditional100,
+ descriptionExcerpt: 'Some description' + descriptionAdditional100,
+ groupType: 'public',
+ actionRadius: 'regional',
+ locationName: 'Hamburg, Germany',
+ location: expect.objectContaining({
+ name: 'Hamburg',
+ nameDE: 'Hamburg',
+ nameEN: 'Hamburg',
+ }),
},
- errors: undefined,
},
- )
+ errors: undefined,
+ })
})
it('assigns the authenticated user as owner', async () => {
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
- {
- data: {
- CreateGroup: {
- name: 'The Best Group',
- myRole: 'owner',
- },
+ await expect(
+ mutate({ mutation: createGroupMutation(), variables }),
+ ).resolves.toMatchObject({
+ data: {
+ CreateGroup: {
+ name: 'The Best Group',
+ myRole: 'owner',
},
- errors: undefined,
},
- )
+ errors: undefined,
+ })
})
it('has "disabled" and "deleted" default to "false"', async () => {
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
- {
- data: { CreateGroup: { disabled: false, deleted: false } },
- },
- )
+ await expect(
+ mutate({ mutation: createGroupMutation(), variables }),
+ ).resolves.toMatchObject({
+ data: { CreateGroup: { disabled: false, deleted: false } },
+ })
})
describe('description', () => {
@@ -338,7 +345,7 @@ describe('in mode', () => {
describe('less then 100 chars', () => {
it('throws error: "Description too short!"', async () => {
const { errors } = await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
...variables,
description:
@@ -361,7 +368,7 @@ describe('in mode', () => {
it('has new categories', async () => {
await expect(
mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
...variables,
categoryIds: ['cat4', 'cat27'],
@@ -386,7 +393,7 @@ describe('in mode', () => {
describe('by "categoryIds: null"', () => {
it('throws error: "Too view categories!"', async () => {
const { errors } = await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: { ...variables, categoryIds: null },
})
expect(errors[0]).toHaveProperty('message', 'Too view categories!')
@@ -396,7 +403,7 @@ describe('in mode', () => {
describe('by "categoryIds: []"', () => {
it('throws error: "Too view categories!"', async () => {
const { errors } = await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: { ...variables, categoryIds: [] },
})
expect(errors[0]).toHaveProperty('message', 'Too view categories!')
@@ -407,7 +414,7 @@ describe('in mode', () => {
describe('four', () => {
it('throws error: "Too many categories!"', async () => {
const { errors } = await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: { ...variables, categoryIds: ['cat9', 'cat4', 'cat15', 'cat27'] },
})
expect(errors[0]).toHaveProperty('message', 'Too many categories!')
@@ -430,7 +437,7 @@ describe('in mode', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
- const { errors } = await query({ query: groupQuery, variables: {} })
+ const { errors } = await query({ query: groupQuery(), variables: {} })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -464,7 +471,7 @@ describe('in mode', () => {
)
authenticatedUser = await otherUser.toJson()
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'others-group',
name: 'Uninteresting Group',
@@ -477,7 +484,7 @@ describe('in mode', () => {
})
authenticatedUser = await ownerOfHiddenGroupUser.toJson()
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'hidden-group',
name: 'Investigative Journalism Group',
@@ -489,7 +496,7 @@ describe('in mode', () => {
},
})
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'second-hidden-group',
name: 'Second Investigative Journalism Group',
@@ -501,7 +508,7 @@ describe('in mode', () => {
},
})
await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'second-hidden-group',
userId: 'current-user',
@@ -509,7 +516,7 @@ describe('in mode', () => {
},
})
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'third-hidden-group',
name: 'Third Investigative Journalism Group',
@@ -521,7 +528,7 @@ describe('in mode', () => {
},
})
await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'third-hidden-group',
userId: 'current-user',
@@ -530,7 +537,7 @@ describe('in mode', () => {
})
authenticatedUser = await user.toJson()
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'my-group',
name: 'The Best Group',
@@ -539,6 +546,7 @@ describe('in mode', () => {
groupType: 'public',
actionRadius: 'regional',
categoryIds,
+ locationName: 'Hamburg, Germany',
},
})
})
@@ -546,25 +554,38 @@ describe('in mode', () => {
describe('query groups', () => {
describe('in general finds only listed groups – no hidden groups where user is none or pending member', () => {
describe('without any filters', () => {
- it('finds all listed groups', async () => {
- const result = await query({ query: groupQuery, variables: {} })
+ it('finds all listed groups – including the set descriptionExcerpts and locations', async () => {
+ const result = await query({ query: groupQuery(), variables: {} })
expect(result).toMatchObject({
data: {
Group: expect.arrayContaining([
expect.objectContaining({
id: 'my-group',
slug: 'the-best-group',
+ descriptionExcerpt: 'Some description' + descriptionAdditional100,
+ locationName: 'Hamburg, Germany',
+ location: expect.objectContaining({
+ name: 'Hamburg',
+ nameDE: 'Hamburg',
+ nameEN: 'Hamburg',
+ }),
myRole: 'owner',
}),
expect.objectContaining({
id: 'others-group',
slug: 'uninteresting-group',
+ descriptionExcerpt: 'We love it like it is!?' + descriptionAdditional100,
+ locationName: null,
+ location: null,
myRole: null,
}),
expect.objectContaining({
id: 'third-hidden-group',
slug: 'third-investigative-journalism-group',
+ descriptionExcerpt: 'We research …' + descriptionAdditional100,
myRole: 'usual',
+ locationName: null,
+ location: null,
}),
]),
},
@@ -579,7 +600,9 @@ describe('in mode', () => {
})
it('has set categories', async () => {
- await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject({
+ await expect(
+ query({ query: groupQuery(), variables: {} }),
+ ).resolves.toMatchObject({
data: {
Group: expect.arrayContaining([
expect.objectContaining({
@@ -613,7 +636,7 @@ describe('in mode', () => {
describe('with given id', () => {
describe("id = 'my-group'", () => {
it('finds only the listed group with this id', async () => {
- const result = await query({ query: groupQuery, variables: { id: 'my-group' } })
+ const result = await query({ query: groupQuery(), variables: { id: 'my-group' } })
expect(result).toMatchObject({
data: {
Group: [
@@ -633,7 +656,7 @@ describe('in mode', () => {
describe("id = 'third-hidden-group'", () => {
it("finds only the hidden group where I'm 'usual' member", async () => {
const result = await query({
- query: groupQuery,
+ query: groupQuery(),
variables: { id: 'third-hidden-group' },
})
expect(result).toMatchObject({
@@ -655,7 +678,7 @@ describe('in mode', () => {
describe("id = 'second-hidden-group'", () => {
it("finds no hidden group where I'm 'pending' member", async () => {
const result = await query({
- query: groupQuery,
+ query: groupQuery(),
variables: { id: 'second-hidden-group' },
})
expect(result.data.Group.length).toBe(0)
@@ -665,7 +688,7 @@ describe('in mode', () => {
describe("id = 'hidden-group'", () => {
it("finds no hidden group where I'm not(!) a member at all", async () => {
const result = await query({
- query: groupQuery,
+ query: groupQuery(),
variables: { id: 'hidden-group' },
})
expect(result.data.Group.length).toBe(0)
@@ -677,7 +700,7 @@ describe('in mode', () => {
describe("slug = 'the-best-group'", () => {
it('finds only the listed group with this slug', async () => {
const result = await query({
- query: groupQuery,
+ query: groupQuery(),
variables: { slug: 'the-best-group' },
})
expect(result).toMatchObject({
@@ -699,7 +722,7 @@ describe('in mode', () => {
describe("slug = 'third-investigative-journalism-group'", () => {
it("finds only the hidden group where I'm 'usual' member", async () => {
const result = await query({
- query: groupQuery,
+ query: groupQuery(),
variables: { slug: 'third-investigative-journalism-group' },
})
expect(result).toMatchObject({
@@ -721,7 +744,7 @@ describe('in mode', () => {
describe("slug = 'second-investigative-journalism-group'", () => {
it("finds no hidden group where I'm 'pending' member", async () => {
const result = await query({
- query: groupQuery,
+ query: groupQuery(),
variables: { slug: 'second-investigative-journalism-group' },
})
expect(result.data.Group.length).toBe(0)
@@ -731,7 +754,7 @@ describe('in mode', () => {
describe("slug = 'investigative-journalism-group'", () => {
it("finds no hidden group where I'm not(!) a member at all", async () => {
const result = await query({
- query: groupQuery,
+ query: groupQuery(),
variables: { slug: 'investigative-journalism-group' },
})
expect(result.data.Group.length).toBe(0)
@@ -741,7 +764,7 @@ describe('in mode', () => {
describe('isMember = true', () => {
it('finds only listed groups where user is member', async () => {
- const result = await query({ query: groupQuery, variables: { isMember: true } })
+ const result = await query({ query: groupQuery(), variables: { isMember: true } })
expect(result).toMatchObject({
data: {
Group: expect.arrayContaining([
@@ -765,7 +788,7 @@ describe('in mode', () => {
describe('isMember = false', () => {
it('finds only listed groups where user is not(!) member', async () => {
- const result = await query({ query: groupQuery, variables: { isMember: false } })
+ const result = await query({ query: groupQuery(), variables: { isMember: false } })
expect(result).toMatchObject({
data: {
Group: expect.arrayContaining([
@@ -798,7 +821,7 @@ describe('in mode', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'not-existing-group',
userId: 'current-user',
@@ -840,7 +863,7 @@ describe('in mode', () => {
// public-group
authenticatedUser = await ownerOfClosedGroupUser.toJson()
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'closed-group',
name: 'Uninteresting Group',
@@ -853,7 +876,7 @@ describe('in mode', () => {
})
authenticatedUser = await ownerOfHiddenGroupUser.toJson()
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'hidden-group',
name: 'Investigative Journalism Group',
@@ -866,7 +889,7 @@ describe('in mode', () => {
})
authenticatedUser = await user.toJson()
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'public-group',
name: 'The Best Group',
@@ -884,7 +907,7 @@ describe('in mode', () => {
it('has "usual" as membership role', async () => {
await expect(
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'public-group',
userId: 'owner-of-closed-group',
@@ -907,7 +930,7 @@ describe('in mode', () => {
it('has still "owner" as membership role', async () => {
await expect(
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'public-group',
userId: 'current-user',
@@ -932,7 +955,7 @@ describe('in mode', () => {
it('has "pending" as membership role', async () => {
await expect(
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'closed-group',
userId: 'current-user',
@@ -955,7 +978,7 @@ describe('in mode', () => {
it('has still "owner" as membership role', async () => {
await expect(
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'closed-group',
userId: 'owner-of-closed-group',
@@ -979,7 +1002,7 @@ describe('in mode', () => {
describe('joined by "owner-of-closed-group"', () => {
it('throws authorization error', async () => {
const { errors } = await query({
- query: joinGroupMutation,
+ query: joinGroupMutation(),
variables: {
groupId: 'hidden-group',
userId: 'owner-of-closed-group',
@@ -994,7 +1017,7 @@ describe('in mode', () => {
it('has still "owner" as membership role', async () => {
await expect(
mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'hidden-group',
userId: 'owner-of-hidden-group',
@@ -1030,7 +1053,7 @@ describe('in mode', () => {
variables = {
id: 'not-existing-group',
}
- const { errors } = await query({ query: groupMembersQuery, variables })
+ const { errors } = await query({ query: groupMembersQuery(), variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -1091,7 +1114,7 @@ describe('in mode', () => {
// public-group
authenticatedUser = await user.toJson()
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'public-group',
name: 'The Best Group',
@@ -1103,14 +1126,14 @@ describe('in mode', () => {
},
})
await mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'public-group',
userId: 'owner-of-closed-group',
},
})
await mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'public-group',
userId: 'owner-of-hidden-group',
@@ -1119,7 +1142,7 @@ describe('in mode', () => {
// closed-group
authenticatedUser = await ownerOfClosedGroupUser.toJson()
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'closed-group',
name: 'Uninteresting Group',
@@ -1131,14 +1154,14 @@ describe('in mode', () => {
},
})
await mutate({
- mutation: joinGroupMutation,
+ mutation: joinGroupMutation(),
variables: {
groupId: 'closed-group',
userId: 'current-user',
},
})
await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'closed-group',
userId: 'owner-of-hidden-group',
@@ -1148,7 +1171,7 @@ describe('in mode', () => {
// hidden-group
authenticatedUser = await ownerOfHiddenGroupUser.toJson()
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'hidden-group',
name: 'Investigative Journalism Group',
@@ -1161,7 +1184,7 @@ describe('in mode', () => {
})
// 'JoinGroup' mutation does not work in hidden groups so we join them by 'ChangeGroupMemberRole' through the owner
await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'hidden-group',
userId: 'pending-user',
@@ -1169,7 +1192,7 @@ describe('in mode', () => {
},
})
await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'hidden-group',
userId: 'current-user',
@@ -1177,7 +1200,7 @@ describe('in mode', () => {
},
})
await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'hidden-group',
userId: 'owner-of-closed-group',
@@ -1203,7 +1226,7 @@ describe('in mode', () => {
it('finds all members', async () => {
const result = await query({
- query: groupMembersQuery,
+ query: groupMembersQuery(),
variables,
})
expect(result).toMatchObject({
@@ -1236,7 +1259,7 @@ describe('in mode', () => {
it('finds all members', async () => {
const result = await query({
- query: groupMembersQuery,
+ query: groupMembersQuery(),
variables,
})
expect(result).toMatchObject({
@@ -1269,7 +1292,7 @@ describe('in mode', () => {
it('finds all members', async () => {
const result = await query({
- query: groupMembersQuery,
+ query: groupMembersQuery(),
variables,
})
expect(result).toMatchObject({
@@ -1312,7 +1335,7 @@ describe('in mode', () => {
it('finds all members', async () => {
const result = await query({
- query: groupMembersQuery,
+ query: groupMembersQuery(),
variables,
})
expect(result).toMatchObject({
@@ -1345,7 +1368,7 @@ describe('in mode', () => {
it('finds all members', async () => {
const result = await query({
- query: groupMembersQuery,
+ query: groupMembersQuery(),
variables,
})
expect(result).toMatchObject({
@@ -1377,7 +1400,7 @@ describe('in mode', () => {
})
it('throws authorization error', async () => {
- const { errors } = await query({ query: groupMembersQuery, variables })
+ const { errors } = await query({ query: groupMembersQuery(), variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -1388,7 +1411,7 @@ describe('in mode', () => {
})
it('throws authorization error', async () => {
- const { errors } = await query({ query: groupMembersQuery, variables })
+ const { errors } = await query({ query: groupMembersQuery(), variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -1410,7 +1433,7 @@ describe('in mode', () => {
it('finds all members', async () => {
const result = await query({
- query: groupMembersQuery,
+ query: groupMembersQuery(),
variables,
})
expect(result).toMatchObject({
@@ -1447,7 +1470,7 @@ describe('in mode', () => {
it('finds all members', async () => {
const result = await query({
- query: groupMembersQuery,
+ query: groupMembersQuery(),
variables,
})
expect(result).toMatchObject({
@@ -1484,7 +1507,7 @@ describe('in mode', () => {
it('finds all members', async () => {
const result = await query({
- query: groupMembersQuery,
+ query: groupMembersQuery(),
variables,
})
expect(result).toMatchObject({
@@ -1520,7 +1543,7 @@ describe('in mode', () => {
})
it('throws authorization error', async () => {
- const { errors } = await query({ query: groupMembersQuery, variables })
+ const { errors } = await query({ query: groupMembersQuery(), variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -1531,7 +1554,7 @@ describe('in mode', () => {
})
it('throws authorization error', async () => {
- const { errors } = await query({ query: groupMembersQuery, variables })
+ const { errors } = await query({ query: groupMembersQuery(), variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -1552,7 +1575,7 @@ describe('in mode', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'not-existing-group',
userId: 'current-user',
@@ -1596,7 +1619,7 @@ describe('in mode', () => {
it('has role usual', async () => {
await expect(
mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
}),
).resolves.toMatchObject({
@@ -1634,7 +1657,7 @@ describe('in mode', () => {
it('has role admin', async () => {
await expect(
mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
}),
).resolves.toMatchObject({
@@ -1669,7 +1692,7 @@ describe('in mode', () => {
it('has role owner', async () => {
await expect(
mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
}),
).resolves.toMatchObject({
@@ -1711,7 +1734,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -1737,7 +1760,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -1755,7 +1778,7 @@ describe('in mode', () => {
it('has role owner still', async () => {
await expect(
mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
}),
).resolves.toMatchObject({
@@ -1786,7 +1809,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -1809,7 +1832,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -1832,7 +1855,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -1865,7 +1888,7 @@ describe('in mode', () => {
it('has role owner', async () => {
await expect(
mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
}),
).resolves.toMatchObject({
@@ -1890,7 +1913,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -1913,7 +1936,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -1930,7 +1953,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -1953,7 +1976,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -1970,7 +1993,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -1993,7 +2016,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -2010,7 +2033,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -2043,7 +2066,7 @@ describe('in mode', () => {
it('has role admin', async () => {
await expect(
mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
}),
).resolves.toMatchObject({
@@ -2069,7 +2092,7 @@ describe('in mode', () => {
it('has role usual again', async () => {
await expect(
mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
}),
).resolves.toMatchObject({
@@ -2100,7 +2123,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -2117,7 +2140,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -2140,7 +2163,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -2157,7 +2180,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -2180,7 +2203,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -2197,7 +2220,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -2230,7 +2253,7 @@ describe('in mode', () => {
it('has role usual', async () => {
await expect(
mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
}),
).resolves.toMatchObject({
@@ -2256,7 +2279,7 @@ describe('in mode', () => {
it('has role usual again', async () => {
await expect(
mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
}),
).resolves.toMatchObject({
@@ -2287,7 +2310,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -2310,7 +2333,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -2333,7 +2356,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
@@ -2352,7 +2375,7 @@ describe('in mode', () => {
// closed-group
authenticatedUser = await ownerMemberUser.toJson()
await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'closed-group',
userId: 'pending-member-user',
@@ -2360,7 +2383,7 @@ describe('in mode', () => {
},
})
await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'closed-group',
userId: 'usual-member-user',
@@ -2368,7 +2391,7 @@ describe('in mode', () => {
},
})
await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'closed-group',
userId: 'admin-member-user',
@@ -2376,7 +2399,7 @@ describe('in mode', () => {
},
})
await mutate({
- mutation: changeGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'closed-group',
userId: 'second-owner-member-user',
@@ -2394,7 +2417,7 @@ describe('in mode', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: leaveGroupMutation,
+ mutation: leaveGroupMutation(),
variables: {
groupId: 'not-existing-group',
userId: 'current-user',
@@ -2409,7 +2432,7 @@ describe('in mode', () => {
describe('here "closed-group" for example', () => {
const memberInGroup = async (userId, groupId) => {
const result = await query({
- query: groupMembersQuery,
+ query: groupMembersQuery(),
variables: {
id: groupId,
},
@@ -2433,7 +2456,7 @@ describe('in mode', () => {
authenticatedUser = await pendingMemberUser.toJson()
await expect(
mutate({
- mutation: leaveGroupMutation,
+ mutation: leaveGroupMutation(),
variables: {
...variables,
userId: 'pending-member-user',
@@ -2460,7 +2483,7 @@ describe('in mode', () => {
authenticatedUser = await usualMemberUser.toJson()
await expect(
mutate({
- mutation: leaveGroupMutation,
+ mutation: leaveGroupMutation(),
variables: {
...variables,
userId: 'usual-member-user',
@@ -2487,7 +2510,7 @@ describe('in mode', () => {
authenticatedUser = await adminMemberUser.toJson()
await expect(
mutate({
- mutation: leaveGroupMutation,
+ mutation: leaveGroupMutation(),
variables: {
...variables,
userId: 'admin-member-user',
@@ -2511,7 +2534,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
authenticatedUser = await ownerMemberUser.toJson()
const { errors } = await mutate({
- mutation: leaveGroupMutation,
+ mutation: leaveGroupMutation(),
variables: {
...variables,
userId: 'owner-member-user',
@@ -2525,7 +2548,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
authenticatedUser = await secondOwnerMemberUser.toJson()
const { errors } = await mutate({
- mutation: leaveGroupMutation,
+ mutation: leaveGroupMutation(),
variables: {
...variables,
userId: 'second-owner-member-user',
@@ -2539,7 +2562,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
authenticatedUser = await noMemberUser.toJson()
const { errors } = await mutate({
- mutation: leaveGroupMutation,
+ mutation: leaveGroupMutation(),
variables: {
...variables,
userId: 'none-member-user',
@@ -2553,7 +2576,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
authenticatedUser = await ownerMemberUser.toJson()
const { errors } = await mutate({
- mutation: leaveGroupMutation,
+ mutation: leaveGroupMutation(),
variables: {
...variables,
userId: 'usual-member-user',
@@ -2567,7 +2590,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
authenticatedUser = await usualMemberUser.toJson()
const { errors } = await mutate({
- mutation: leaveGroupMutation,
+ mutation: leaveGroupMutation(),
variables: {
...variables,
userId: 'admin-member-user',
@@ -2593,7 +2616,7 @@ describe('in mode', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- query: updateGroupMutation,
+ mutation: updateGroupMutation(),
variables: {
id: 'my-group',
slug: 'my-best-group',
@@ -2604,23 +2627,34 @@ describe('in mode', () => {
})
describe('authenticated', () => {
- let otherUser
+ let noMemberUser
beforeAll(async () => {
- otherUser = await Factory.build(
+ noMemberUser = await Factory.build(
'user',
{
- id: 'other-user',
- name: 'Other TestUser',
+ id: 'none-member-user',
+ name: 'None Member TestUser',
},
{
- email: 'test2@example.org',
+ email: 'none-member-user@example.org',
password: '1234',
},
)
- authenticatedUser = await otherUser.toJson()
+ usualMemberUser = await Factory.build(
+ 'user',
+ {
+ id: 'usual-member-user',
+ name: 'Usual Member TestUser',
+ },
+ {
+ email: 'usual-member-user@example.org',
+ password: '1234',
+ },
+ )
+ authenticatedUser = await noMemberUser.toJson()
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'others-group',
name: 'Uninteresting Group',
@@ -2633,7 +2667,7 @@ describe('in mode', () => {
})
authenticatedUser = await user.toJson()
await mutate({
- mutation: createGroupMutation,
+ mutation: createGroupMutation(),
variables: {
id: 'my-group',
name: 'The Best Group',
@@ -2642,6 +2676,15 @@ describe('in mode', () => {
groupType: 'public',
actionRadius: 'regional',
categoryIds,
+ locationName: 'Berlin, Germany',
+ },
+ })
+ await mutate({
+ mutation: changeGroupMemberRoleMutation(),
+ variables: {
+ groupId: 'my-group',
+ userId: 'usual-member-user',
+ roleInGroup: 'usual',
},
})
})
@@ -2652,35 +2695,170 @@ describe('in mode', () => {
authenticatedUser = await user.toJson()
})
- it('has set the settings', async () => {
- await expect(
- mutate({
- mutation: updateGroupMutation,
- variables: {
- id: 'my-group',
- name: 'The New Group For Our Country',
- about: 'We will change the land!',
- description: 'Some country relevant description' + descriptionAdditional100,
- actionRadius: 'national',
- // avatar, // test this as result
- // locationName, // test this as result
+ describe('all standard settings – excluding location', () => {
+ it('has updated the settings', async () => {
+ await expect(
+ mutate({
+ mutation: updateGroupMutation(),
+ variables: {
+ id: 'my-group',
+ name: 'The New Group For Our Country',
+ about: 'We will change the land!',
+ description: 'Some country relevant description' + descriptionAdditional100,
+ actionRadius: 'national',
+ // avatar, // test this as result
+ },
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ UpdateGroup: {
+ id: 'my-group',
+ name: 'The New Group For Our Country',
+ slug: 'the-new-group-for-our-country', // changing the slug is tested in the slugifyMiddleware
+ about: 'We will change the land!',
+ description: 'Some country relevant description' + descriptionAdditional100,
+ descriptionExcerpt:
+ 'Some country relevant description' + descriptionAdditional100,
+ actionRadius: 'national',
+ // avatar, // test this as result
+ myRole: 'owner',
+ },
},
- }),
- ).resolves.toMatchObject({
- data: {
- UpdateGroup: {
- id: 'my-group',
- name: 'The New Group For Our Country',
- slug: 'the-new-group-for-our-country', // changing the slug is tested in the slugifyMiddleware
- about: 'We will change the land!',
- description: 'Some country relevant description' + descriptionAdditional100,
- actionRadius: 'national',
- // avatar, // test this as result
- // locationName, // test this as result
- myRole: 'owner',
- },
- },
- errors: undefined,
+ errors: undefined,
+ })
+ })
+ })
+
+ describe('location', () => {
+ describe('"locationName" is undefined – shall not change location', () => {
+ it('has left locaton unchanged as "Berlin"', async () => {
+ await expect(
+ mutate({
+ mutation: updateGroupMutation(),
+ variables: {
+ id: 'my-group',
+ },
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ UpdateGroup: {
+ id: 'my-group',
+ locationName: 'Berlin, Germany',
+ location: expect.objectContaining({
+ name: 'Berlin',
+ nameDE: 'Berlin',
+ nameEN: 'Berlin',
+ }),
+ myRole: 'owner',
+ },
+ },
+ errors: undefined,
+ })
+ })
+ })
+
+ describe('"locationName" is null – shall change location "Berlin" to unset location', () => {
+ it('has updated the location to unset location', async () => {
+ await expect(
+ mutate({
+ mutation: updateGroupMutation(),
+ variables: {
+ id: 'my-group',
+ locationName: null,
+ },
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ UpdateGroup: {
+ id: 'my-group',
+ locationName: null,
+ location: null,
+ myRole: 'owner',
+ },
+ },
+ errors: undefined,
+ })
+ })
+ })
+
+ describe('change unset location to "Paris"', () => {
+ it('has updated the location to "Paris"', async () => {
+ await expect(
+ mutate({
+ mutation: updateGroupMutation(),
+ variables: {
+ id: 'my-group',
+ locationName: 'Paris, France',
+ },
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ UpdateGroup: {
+ id: 'my-group',
+ locationName: 'Paris, France',
+ location: expect.objectContaining({
+ name: 'Paris',
+ nameDE: 'Paris',
+ nameEN: 'Paris',
+ }),
+ myRole: 'owner',
+ },
+ },
+ errors: undefined,
+ })
+ })
+ })
+
+ describe('change location "Paris" to "Hamburg"', () => {
+ it('has updated the location to "Hamburg"', async () => {
+ await expect(
+ mutate({
+ mutation: updateGroupMutation(),
+ variables: {
+ id: 'my-group',
+ locationName: 'Hamburg, Germany',
+ },
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ UpdateGroup: {
+ id: 'my-group',
+ locationName: 'Hamburg, Germany',
+ location: expect.objectContaining({
+ name: 'Hamburg',
+ nameDE: 'Hamburg',
+ nameEN: 'Hamburg',
+ }),
+ myRole: 'owner',
+ },
+ },
+ errors: undefined,
+ })
+ })
+ })
+
+ describe('"locationName" is empty string – shall change location "Hamburg" to unset location ', () => {
+ it('has updated the location to unset', async () => {
+ await expect(
+ mutate({
+ mutation: updateGroupMutation(),
+ variables: {
+ id: 'my-group',
+ locationName: '', // empty string '' sets it to null
+ },
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ UpdateGroup: {
+ id: 'my-group',
+ locationName: null,
+ location: null,
+ myRole: 'owner',
+ },
+ },
+ errors: undefined,
+ })
+ })
})
})
@@ -2689,7 +2867,7 @@ describe('in mode', () => {
describe('less then 100 chars', () => {
it('throws error: "Description too short!"', async () => {
const { errors } = await mutate({
- mutation: updateGroupMutation,
+ mutation: updateGroupMutation(),
variables: {
id: 'my-group',
description:
@@ -2712,7 +2890,7 @@ describe('in mode', () => {
it('has new categories', async () => {
await expect(
mutate({
- mutation: updateGroupMutation,
+ mutation: updateGroupMutation(),
variables: {
id: 'my-group',
categoryIds: ['cat4', 'cat27'],
@@ -2738,7 +2916,7 @@ describe('in mode', () => {
describe('by "categoryIds: []"', () => {
it('throws error: "Too view categories!"', async () => {
const { errors } = await mutate({
- mutation: updateGroupMutation,
+ mutation: updateGroupMutation(),
variables: {
id: 'my-group',
categoryIds: [],
@@ -2752,7 +2930,7 @@ describe('in mode', () => {
describe('four', () => {
it('throws error: "Too many categories!"', async () => {
const { errors } = await mutate({
- mutation: updateGroupMutation,
+ mutation: updateGroupMutation(),
variables: {
id: 'my-group',
categoryIds: ['cat9', 'cat4', 'cat15', 'cat27'],
@@ -2764,20 +2942,36 @@ describe('in mode', () => {
})
})
- describe('as no(!) owner', () => {
+ describe('as "usual-member-user" member, no(!) owner', () => {
it('throws authorization error', async () => {
- authenticatedUser = await otherUser.toJson()
+ authenticatedUser = await usualMemberUser.toJson()
const { errors } = await mutate({
- mutation: updateGroupMutation,
+ mutation: updateGroupMutation(),
variables: {
id: 'my-group',
name: 'The New Group For Our Country',
about: 'We will change the land!',
description: 'Some country relevant description' + descriptionAdditional100,
actionRadius: 'national',
- categoryIds: ['cat4', 'cat27'], // test this as result
- // avatar, // test this as result
- // locationName, // test this as result
+ categoryIds: ['cat4', 'cat27'],
+ },
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
+ })
+ })
+
+ describe('as "none-member-user"', () => {
+ it('throws authorization error', async () => {
+ authenticatedUser = await noMemberUser.toJson()
+ const { errors } = await mutate({
+ mutation: updateGroupMutation(),
+ variables: {
+ id: 'my-group',
+ name: 'The New Group For Our Country',
+ about: 'We will change the land!',
+ description: 'Some country relevant description' + descriptionAdditional100,
+ actionRadius: 'national',
+ categoryIds: ['cat4', 'cat27'],
},
})
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
diff --git a/backend/src/schema/resolvers/users.js b/backend/src/schema/resolvers/users.js
index 12f00ffb6..1ce3b986f 100644
--- a/backend/src/schema/resolvers/users.js
+++ b/backend/src/schema/resolvers/users.js
@@ -4,7 +4,7 @@ import { UserInputError, ForbiddenError } from 'apollo-server'
import { mergeImage, deleteImage } from './images/images'
import Resolver from './helpers/Resolver'
import log from './helpers/databaseLogger'
-import createOrUpdateLocations from './users/location'
+import { createOrUpdateLocations } from './users/location'
const neode = getNeode()
@@ -139,9 +139,10 @@ export default {
return blockedUser.toJson()
},
UpdateUser: async (_parent, params, context, _resolveInfo) => {
- const { termsAndConditionsAgreedVersion } = params
const { avatar: avatarInput } = params
delete params.avatar
+ params.locationName = params.locationName === '' ? null : params.locationName
+ const { termsAndConditionsAgreedVersion } = params
if (termsAndConditionsAgreedVersion) {
const regEx = new RegExp(/^[0-9]+\.[0-9]+\.[0-9]+$/g)
if (!regEx.test(termsAndConditionsAgreedVersion)) {
@@ -169,7 +170,8 @@ export default {
})
try {
const user = await writeTxResultPromise
- await createOrUpdateLocations(params.id, params.locationName, session)
+ // TODO: put in a middleware, see "CreateGroup", "UpdateGroup"
+ await createOrUpdateLocations('User', params.id, params.locationName, session)
return user
} catch (error) {
throw new UserInputError(error.message)
diff --git a/backend/src/schema/resolvers/users.spec.js b/backend/src/schema/resolvers/users.spec.js
index d8fce3b29..87226ec4d 100644
--- a/backend/src/schema/resolvers/users.spec.js
+++ b/backend/src/schema/resolvers/users.spec.js
@@ -161,7 +161,7 @@ describe('UpdateUser', () => {
$id: ID!
$name: String
$termsAndConditionsAgreedVersion: String
- $locationName: String
+ $locationName: String # empty string '' sets it to null
) {
UpdateUser(
id: $id
@@ -174,6 +174,11 @@ describe('UpdateUser', () => {
termsAndConditionsAgreedVersion
termsAndConditionsAgreedAt
locationName
+ location {
+ name
+ nameDE
+ nameEN
+ }
}
}
`
@@ -289,11 +294,39 @@ describe('UpdateUser', () => {
expect(errors[0]).toHaveProperty('message', 'Invalid version format!')
})
- it('supports updating location', async () => {
- variables = { ...variables, locationName: 'Hamburg, New Jersey, United States' }
- await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject({
- data: { UpdateUser: { locationName: 'Hamburg, New Jersey, United States' } },
- errors: undefined,
+ describe('supports updating location', () => {
+ describe('change location to "Hamburg, New Jersey, United States"', () => {
+ it('has updated location to "Hamburg, New Jersey, United States"', async () => {
+ variables = { ...variables, locationName: 'Hamburg, New Jersey, United States' }
+ await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject({
+ data: {
+ UpdateUser: {
+ locationName: 'Hamburg, New Jersey, United States',
+ location: expect.objectContaining({
+ name: 'Hamburg',
+ nameDE: 'Hamburg',
+ nameEN: 'Hamburg',
+ }),
+ },
+ },
+ errors: undefined,
+ })
+ })
+ })
+
+ describe('change location to unset location', () => {
+ it('has updated location to unset location', async () => {
+ variables = { ...variables, locationName: '' }
+ await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject({
+ data: {
+ UpdateUser: {
+ locationName: null,
+ location: null,
+ },
+ },
+ errors: undefined,
+ })
+ })
})
})
})
diff --git a/backend/src/schema/resolvers/users/location.js b/backend/src/schema/resolvers/users/location.js
index affd3267e..9d8a11f89 100644
--- a/backend/src/schema/resolvers/users/location.js
+++ b/backend/src/schema/resolvers/users/location.js
@@ -1,6 +1,5 @@
import request from 'request'
import { UserInputError } from 'apollo-server'
-import isEmpty from 'lodash/isEmpty'
import Debug from 'debug'
import asyncForEach from '../../../helpers/asyncForEach'
import CONFIG from '../../../config'
@@ -62,77 +61,86 @@ const createLocation = async (session, mapboxData) => {
})
}
-const createOrUpdateLocations = async (userId, locationName, session) => {
- if (isEmpty(locationName)) {
- return
- }
- const res = await fetch(
- `https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(
- locationName,
- )}.json?access_token=${CONFIG.MAPBOX_TOKEN}&types=region,place,country&language=${locales.join(
- ',',
- )}`,
- )
+export const createOrUpdateLocations = async (nodeLabel, nodeId, locationName, session) => {
+ if (locationName === undefined) return
- debug(res)
+ let locationId
- if (!res || !res.features || !res.features[0]) {
- throw new UserInputError('locationName is invalid')
- }
+ if (locationName !== null) {
+ const res = await fetch(
+ `https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(
+ locationName,
+ )}.json?access_token=${
+ CONFIG.MAPBOX_TOKEN
+ }&types=region,place,country&language=${locales.join(',')}`,
+ )
- let data
+ debug(res)
- res.features.forEach((item) => {
- if (item.matching_place_name === locationName) {
- data = item
+ if (!res || !res.features || !res.features[0]) {
+ throw new UserInputError('locationName is invalid')
}
- })
- if (!data) {
- data = res.features[0]
- }
- if (!data || !data.place_type || !data.place_type.length) {
- throw new UserInputError('locationName is invalid')
- }
+ let data
- if (data.place_type.length > 1) {
- data.id = 'region.' + data.id.split('.')[1]
- }
- await createLocation(session, data)
-
- let parent = data
-
- if (data.context) {
- await asyncForEach(data.context, async (ctx) => {
- await createLocation(session, ctx)
- await session.writeTransaction((transaction) => {
- return transaction.run(
- `
- MATCH (parent:Location {id: $parentId}), (child:Location {id: $childId})
- MERGE (child)<-[:IS_IN]-(parent)
- RETURN child.id, parent.id
- `,
- {
- parentId: parent.id,
- childId: ctx.id,
- },
- )
- })
- parent = ctx
+ res.features.forEach((item) => {
+ if (item.matching_place_name === locationName) {
+ data = item
+ }
})
+ if (!data) {
+ data = res.features[0]
+ }
+
+ if (!data || !data.place_type || !data.place_type.length) {
+ throw new UserInputError('locationName is invalid')
+ }
+
+ if (data.place_type.length > 1) {
+ data.id = 'region.' + data.id.split('.')[1]
+ }
+ await createLocation(session, data)
+
+ let parent = data
+
+ if (data.context) {
+ await asyncForEach(data.context, async (ctx) => {
+ await createLocation(session, ctx)
+ await session.writeTransaction((transaction) => {
+ return transaction.run(
+ `
+ MATCH (parent:Location {id: $parentId}), (child:Location {id: $childId})
+ MERGE (child)<-[:IS_IN]-(parent)
+ RETURN child.id, parent.id
+ `,
+ {
+ parentId: parent.id,
+ childId: ctx.id,
+ },
+ )
+ })
+ parent = ctx
+ })
+ }
+
+ locationId = data.id
+ } else {
+ locationId = 'non-existent-id'
}
- // delete all current locations from user and add new location
+
+ // delete all current locations from node and add new location
await session.writeTransaction((transaction) => {
return transaction.run(
`
- MATCH (user:User {id: $userId})-[relationship:IS_IN]->(location:Location)
- DETACH DELETE relationship
- WITH user
- MATCH (location:Location {id: $locationId})
- MERGE (user)-[:IS_IN]->(location)
- RETURN location.id, user.id
- `,
- { userId: userId, locationId: data.id },
+ MATCH (node:${nodeLabel} {id: $nodeId})
+ OPTIONAL MATCH (node)-[relationship:IS_IN]->(:Location)
+ DELETE relationship
+ WITH node
+ MATCH (location:Location {id: $locationId})
+ MERGE (node)-[:IS_IN]->(location)
+ RETURN location.id, node.id
+ `,
+ { nodeId, locationId },
)
})
}
@@ -147,5 +155,3 @@ export const queryLocations = async ({ place, lang }) => {
}
return res.features
}
-
-export default createOrUpdateLocations
diff --git a/backend/src/schema/resolvers/users/location.spec.js b/backend/src/schema/resolvers/users/location.spec.js
index 92f08893e..c0525ca1c 100644
--- a/backend/src/schema/resolvers/users/location.spec.js
+++ b/backend/src/schema/resolvers/users/location.spec.js
@@ -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',
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
index bab19307e..15b980345 100644
--- a/backend/src/schema/types/type/Group.gql
+++ b/backend/src/schema/types/type/Group.gql
@@ -29,11 +29,12 @@ type Group {
about: String # goal
description: String!
+ descriptionExcerpt: String!
groupType: GroupType!
actionRadius: GroupActionRadius!
- location: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l")
locationName: String
+ location: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l")
categories: [Category] @relation(name: "CATEGORIZED", direction: "OUT")
@@ -104,7 +105,7 @@ type Mutation {
actionRadius: GroupActionRadius!
categoryIds: [ID]
# avatar: ImageInput # a group can not be created with an avatar
- locationName: String # test this as result
+ locationName: String # empty string '' sets it to null
): Group
UpdateGroup(
@@ -117,7 +118,7 @@ type Mutation {
actionRadius: GroupActionRadius
categoryIds: [ID]
avatar: ImageInput # test this as result
- locationName: String # test this as result
+ locationName: String # empty string '' sets it to null
): Group
# DeleteGroup(id: ID!): Group
diff --git a/backend/src/schema/types/type/User.gql b/backend/src/schema/types/type/User.gql
index 2861b0fda..fdab73d17 100644
--- a/backend/src/schema/types/type/User.gql
+++ b/backend/src/schema/types/type/User.gql
@@ -33,8 +33,8 @@ type User {
invitedBy: User @relation(name: "INVITED", direction: "IN")
invited: [User] @relation(name: "INVITED", direction: "OUT")
- location: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l")
locationName: String
+ location: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l")
about: String
socialMedia: [SocialMedia]! @relation(name: "OWNED_BY", direction: "IN")
@@ -212,7 +212,7 @@ type Mutation {
email: String
slug: String
avatar: ImageInput
- locationName: String
+ locationName: String # empty string '' sets it to null
about: String
termsAndConditionsAgreedVersion: String
termsAndConditionsAgreedAt: String
diff --git a/package.json b/package.json
index d7463adac..54560aebd 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ocelot-social",
- "version": "1.1.0",
+ "version": "1.1.1",
"description": "Free and open source software program code available to run social networks.",
"author": "ocelot.social Community",
"license": "MIT",
diff --git a/webapp/assets/_new/styles/tokens.scss b/webapp/assets/_new/styles/tokens.scss
index 53abd01f8..180f9c820 100644
--- a/webapp/assets/_new/styles/tokens.scss
+++ b/webapp/assets/_new/styles/tokens.scss
@@ -268,6 +268,7 @@ $size-avatar-large: 114px;
* @presenter Spacing
*/
+ $size-button-large: 50px;
$size-button-base: 36px;
$size-button-small: 26px;
@@ -356,3 +357,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;
\ No newline at end of file
diff --git a/webapp/assets/styles/imports/_branding.scss b/webapp/assets/styles/imports/_branding.scss
new file mode 100644
index 000000000..75058595d
--- /dev/null
+++ b/webapp/assets/styles/imports/_branding.scss
@@ -0,0 +1,5 @@
+/*
+ *
+ * Here, all SCSS variables and classes can be adapted to your custom design.
+ *
+*/
\ No newline at end of file
diff --git a/webapp/assets/styles/main.scss b/webapp/assets/styles/main.scss
index d6821e013..144701f83 100644
--- a/webapp/assets/styles/main.scss
+++ b/webapp/assets/styles/main.scss
@@ -179,4 +179,4 @@ hr {
.dropdown-arrow {
font-size: $font-size-xx-small;
-}
+}
\ No newline at end of file
diff --git a/webapp/components/AvatarMenu/AvatarMenu.spec.js b/webapp/components/AvatarMenu/AvatarMenu.spec.js
index 77de68de4..15f536ee7 100644
--- a/webapp/components/AvatarMenu/AvatarMenu.spec.js
+++ b/webapp/components/AvatarMenu/AvatarMenu.spec.js
@@ -90,6 +90,13 @@ describe('AvatarMenu.vue', () => {
expect(profileLink.exists()).toBe(true)
})
+ it('displays a link to "My groups"', () => {
+ const profileLink = wrapper
+ .findAll('.ds-menu-item span')
+ .at(wrapper.vm.routes.findIndex((route) => route.path === '/my-groups'))
+ expect(profileLink.exists()).toBe(true)
+ })
+
it('displays a link to the notifications page', () => {
const notificationsLink = wrapper
.findAll('.ds-menu-item span')
@@ -103,6 +110,11 @@ describe('AvatarMenu.vue', () => {
.at(wrapper.vm.routes.findIndex((route) => route.path === '/settings'))
expect(settingsLink.exists()).toBe(true)
})
+
+ it('displays a total of 4 links', () => {
+ const allLinks = wrapper.findAll('.ds-menu-item')
+ expect(allLinks).toHaveLength(4)
+ })
})
describe('role moderator', () => {
@@ -125,9 +137,9 @@ describe('AvatarMenu.vue', () => {
expect(moderationLink.exists()).toBe(true)
})
- it('displays a total of 4 links', () => {
+ it('displays a total of 5 links', () => {
const allLinks = wrapper.findAll('.ds-menu-item')
- expect(allLinks).toHaveLength(4)
+ expect(allLinks).toHaveLength(5)
})
})
@@ -151,9 +163,9 @@ describe('AvatarMenu.vue', () => {
expect(adminLink.exists()).toBe(true)
})
- it('displays a total of 5 links', () => {
+ it('displays a total of 6 links', () => {
const allLinks = wrapper.findAll('.ds-menu-item')
- expect(allLinks).toHaveLength(5)
+ expect(allLinks).toHaveLength(6)
})
})
})
diff --git a/webapp/components/AvatarMenu/AvatarMenu.vue b/webapp/components/AvatarMenu/AvatarMenu.vue
index 061e96221..5caec07f2 100644
--- a/webapp/components/AvatarMenu/AvatarMenu.vue
+++ b/webapp/components/AvatarMenu/AvatarMenu.vue
@@ -72,10 +72,15 @@ export default {
}
const routes = [
{
- name: this.$t('profile.name'),
+ name: this.$t('header.avatarMenu.myProfile'),
path: `/profile/${this.user.id}/${this.user.slug}`,
icon: 'user',
},
+ {
+ name: this.$t('header.avatarMenu.myGroups'),
+ path: '/my-groups',
+ icon: 'users',
+ },
{
name: this.$t('notifications.pageLink'),
path: '/notifications',
diff --git a/webapp/components/Button/JoinLeaveButton.vue b/webapp/components/Button/JoinLeaveButton.vue
index 993631065..9bcb5042a 100644
--- a/webapp/components/Button/JoinLeaveButton.vue
+++ b/webapp/components/Button/JoinLeaveButton.vue
@@ -1,6 +1,6 @@
diff --git a/webapp/components/Group/GroupForm.spec.js b/webapp/components/Group/GroupForm.spec.js
new file mode 100644
index 000000000..0300bacd0
--- /dev/null
+++ b/webapp/components/Group/GroupForm.spec.js
@@ -0,0 +1,39 @@
+import { config, mount } from '@vue/test-utils'
+import GroupForm from './GroupForm.vue'
+
+const localVue = global.localVue
+
+config.stubs['nuxt-link'] = ' '
+
+const propsData = {
+ update: false,
+ group: {},
+}
+
+describe('GroupForm', () => {
+ let wrapper
+ let mocks
+
+ beforeEach(() => {
+ mocks = {
+ $t: jest.fn(),
+ $env: {
+ CATEGORIES_ACTIVE: true,
+ },
+ }
+ })
+
+ describe('mount', () => {
+ const Wrapper = () => {
+ return mount(GroupForm, { propsData, mocks, localVue })
+ }
+
+ beforeEach(() => {
+ wrapper = Wrapper()
+ })
+
+ it('renders', () => {
+ expect(wrapper.findAll('.group-form')).toHaveLength(1)
+ })
+ })
+})
diff --git a/webapp/components/Group/GroupForm.vue b/webapp/components/Group/GroupForm.vue
index d79349fda..daf0c1d2a 100644
--- a/webapp/components/Group/GroupForm.vue
+++ b/webapp/components/Group/GroupForm.vue
@@ -1,68 +1,132 @@
-
-
+
+
+
+
+
+
+
+ {{ $t('group.type') }}
+
+
+
-
-
-
-
-
-
-
-
-
+ {{ $t(`group.types.${groupType}`) }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('group.actionRadius') }}
+
+
+
+
+ {{ $t(`group.actionRadii.${actionRadius}`) }}
+
+
+
+
+
+
+
+
-
- {{ formData }}
-
-
- Reset form
-
- {{ update ? $t('group.update') : $t('group.save') }}
-
-
-
-
- zurück
-
+
+
+ {{ $t('actions.cancel') }}
+
+
+ {{ update ? $t('group.update') : $t('group.save') }}
+
+
+
@@ -70,6 +134,9 @@
import CategoriesSelect from '~/components/CategoriesSelect/CategoriesSelect'
import { CATEGORIES_MIN, CATEGORIES_MAX } from '~/constants/categories.js'
import { NAME_LENGTH_MIN, NAME_LENGTH_MAX } from '~/constants/groups.js'
+import { queryLocations } from '~/graphql/location'
+
+let timeout
export default {
name: 'GroupForm',
@@ -89,24 +156,33 @@ export default {
},
},
data() {
- const { name, groupType, about, description, actionRadius, categories } = this.group
+ const { name, slug, groupType, about, description, actionRadius, locationName, categories } =
+ this.group
return {
categoriesActive: this.$env.CATEGORIES_ACTIVE,
disabled: false,
+ groupTypeOptions: ['public', 'closed', 'hidden'],
+ actionRadiusOptions: ['regional', 'national', 'continental', 'global'],
+ loadingGeo: false,
+ cities: [],
formData: {
name: name || '',
+ slug: slug || '',
groupType: groupType || '',
about: about || '',
description: description || '',
+ locationName: locationName || '',
actionRadius: actionRadius || '',
categoryIds: categories ? categories.map((category) => category.id) : [],
},
formSchema: {
name: { required: true, min: NAME_LENGTH_MIN, max: NAME_LENGTH_MAX },
+ slug: { required: false },
groupType: { required: true },
about: { required: true },
description: { required: true },
actionRadius: { required: true },
+ locationName: { required: false },
categoryIds: {
type: 'array',
required: this.categoriesActive,
@@ -123,16 +199,54 @@ export default {
},
}
},
+ computed: {
+ submitDisable() {
+ return (
+ this.formData.name === '' ||
+ this.formData.groupType === '' ||
+ // this.formData.about === '' || // not mandatory
+ this.formData.description === '' ||
+ this.formData.actionRadius === '' ||
+ // this.formData.locationName === '' || // not mandatory
+ this.formData.categoryIds.length === 0
+ )
+ },
+ submitDisableEdit() {
+ return (
+ this.formData.name === this.group.name &&
+ this.formData.slug === this.group.slug &&
+ // this.formData.groupType === this.group.groupType && // can not be changed for now
+ this.formData.about === this.group.about &&
+ this.formData.description === this.group.description &&
+ this.formData.actionRadius === this.group.actionRadius &&
+ this.formData.locationName === (this.group.locationName ? this.group.locationName : '') &&
+ this.sameCategories
+ )
+ },
+ sameCategories() {
+ const formDataCategories = this.formData.categoryIds.map((id) => id).sort()
+ const groupDataCategories = this.group.categories.map((category) => category.id).sort()
+ let equal = true
+ if (formDataCategories.length !== groupDataCategories.length) return false
+
+ formDataCategories.forEach((id, index) => {
+ equal = equal && id === groupDataCategories[index]
+ })
+ return equal
+ },
+ },
methods: {
submit() {
- const { name, about, description, groupType, actionRadius, categoryIds } = this.formData
+ const { name, about, description, groupType, actionRadius, locationName, categoryIds } =
+ this.formData
const variables = {
name,
about,
description,
groupType,
actionRadius,
+ locationName: locationName.label,
categoryIds,
}
this.update
@@ -142,9 +256,64 @@ export default {
})
: this.$emit('createGroup', variables)
},
- reset() {
- alert('reset')
+ changeGroupType(event) {
+ this.formData.groupType = event.target.value
+ },
+ changeActionRadius(event) {
+ this.formData.actionRadius = event.target.value
+ },
+ handleCityInput(value) {
+ clearTimeout(timeout)
+ timeout = setTimeout(() => this.requestGeoData(value), 500)
+ },
+ processLocationsResult(places) {
+ if (!places.length) {
+ return []
+ }
+ const result = []
+ places.forEach((place) => {
+ result.push({
+ label: place.place_name,
+ value: place.place_name,
+ id: place.id,
+ })
+ })
+
+ return result
+ },
+ async requestGeoData(e) {
+ const value = e.target ? e.target.value.trim() : ''
+ if (value === '') {
+ this.cities = []
+ return
+ }
+ this.loadingGeo = true
+
+ const place = encodeURIComponent(value)
+ const lang = this.$i18n.locale()
+
+ const {
+ data: { queryLocations: res },
+ } = await this.$apollo.query({ query: queryLocations(), variables: { place, lang } })
+
+ this.cities = this.processLocationsResult(res)
+ this.loadingGeo = false
},
},
}
+
+
diff --git a/webapp/components/Group/GroupList.vue b/webapp/components/Group/GroupList.vue
new file mode 100644
index 000000000..645f670d2
--- /dev/null
+++ b/webapp/components/Group/GroupList.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
diff --git a/webapp/components/Group/GroupMember.vue b/webapp/components/Group/GroupMember.vue
index f522ee795..28412d33a 100644
--- a/webapp/components/Group/GroupMember.vue
+++ b/webapp/components/Group/GroupMember.vue
@@ -1,56 +1,148 @@
- Members
-
-
-
+
+
+
+
+
-
- {{ scope.row.name }} loves {{ scope.row.loves }}
+
+
+
+ {{ scope.row.name | truncate(20) }}
+
+
-
- delete
+
+
+
+ {{ `@${scope.row.slug}` | truncate(20) }}
+
+
+
+
+
+
+ {{ $t(`group.roles.${role}`) }}
+
+
+
+ {{ $t(`group.roles.${scope.row.myRoleInGroup}`) }}
+
+
+
+
+
+
+ {{ $t('group.removeMemberButton') }}
+
+
+
+
diff --git a/webapp/components/Group/GroupTeaser.vue b/webapp/components/Group/GroupTeaser.vue
index 33a50dca7..9a6eb9bcc 100644
--- a/webapp/components/Group/GroupTeaser.vue
+++ b/webapp/components/Group/GroupTeaser.vue
@@ -1,27 +1,178 @@
-
-
-
-
-
+
+ {{ group.name }}
+
+
+
+
+
+ {{ group.slug }}
+
+
+
+
+
+
+ {{ group && group.location ? group.location.name : '' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/webapp/components/LocaleSwitch/LocaleSwitch.vue b/webapp/components/LocaleSwitch/LocaleSwitch.vue
index 353fc406a..2535ca145 100644
--- a/webapp/components/LocaleSwitch/LocaleSwitch.vue
+++ b/webapp/components/LocaleSwitch/LocaleSwitch.vue
@@ -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;
diff --git a/webapp/components/Logo/Logo.vue b/webapp/components/Logo/Logo.vue
index ed5c147e8..3d7d336ce 100644
--- a/webapp/components/Logo/Logo.vue
+++ b/webapp/components/Logo/Logo.vue
@@ -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;
}
diff --git a/webapp/components/Logo/style.scss b/webapp/components/Logo/style.scss
index 305e907c5..69897422f 100644
--- a/webapp/components/Logo/style.scss
+++ b/webapp/components/Logo/style.scss
@@ -8,10 +8,4 @@
.ds-logo-inverse {
color: $text-color-primary-inverse;
-}
-
-.ds-logo-svg {
- width: 130px;
- height: auto;
- fill: currentColor;
}
\ No newline at end of file
diff --git a/webapp/components/PageFooter/PageFooter.vue b/webapp/components/PageFooter/PageFooter.vue
index 24d35244b..c71fc4b5c 100644
--- a/webapp/components/PageFooter/PageFooter.vue
+++ b/webapp/components/PageFooter/PageFooter.vue
@@ -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;
diff --git a/webapp/components/PostTeaser/PostTeaser.vue b/webapp/components/PostTeaser/PostTeaser.vue
index a973ca31f..dc1829a4d 100644
--- a/webapp/components/PostTeaser/PostTeaser.vue
+++ b/webapp/components/PostTeaser/PostTeaser.vue
@@ -19,9 +19,8 @@
{{ post.title }}
-
+
-
diff --git a/webapp/components/Uploader/AvatarUploader.spec.js b/webapp/components/Uploader/AvatarUploader.spec.js
index 8fc313c43..070302038 100644
--- a/webapp/components/Uploader/AvatarUploader.spec.js
+++ b/webapp/components/Uploader/AvatarUploader.spec.js
@@ -29,6 +29,7 @@ describe('AvatarUploader', () => {
profile: {
avatar: { url: '/api/generic.jpg' },
},
+ updateMutation: jest.fn(),
}
beforeEach(() => {
@@ -40,7 +41,7 @@ describe('AvatarUploader', () => {
jest.clearAllMocks()
})
- it('sends a the UpdateUser mutation when vddrop is called', () => {
+ it('sends the UpdateUser mutation when vddrop is called', () => {
wrapper.vm.vddrop([{ filename: 'avatar.jpg' }])
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
})
diff --git a/webapp/components/Uploader/AvatarUploader.vue b/webapp/components/Uploader/AvatarUploader.vue
index be8744ac2..eacb44d3a 100644
--- a/webapp/components/Uploader/AvatarUploader.vue
+++ b/webapp/components/Uploader/AvatarUploader.vue
@@ -1,5 +1,5 @@
-
+
$emit('click', event)"
>
@@ -46,7 +46,7 @@ export default {
type: String,
default: 'regular',
validator(value) {
- return value.match(/(small|regular)/)
+ return value.match(/(small|regular|large)/)
},
},
type: {
@@ -56,6 +56,10 @@ export default {
return value.match(/(button|submit)/)
},
},
+ disabled: {
+ // type: Boolean, // makes some errors that an Object was passed instead a Boolean and could not find how to solve in a acceptable time
+ default: false,
+ },
},
computed: {
buttonClass() {
@@ -66,6 +70,7 @@ export default {
if (this.danger) buttonClass += ' --danger'
if (this.loading) buttonClass += ' --loading'
if (this.size === 'small') buttonClass += ' --small'
+ if (this.size === 'large') buttonClass += ' --large'
if (this.filled) buttonClass += ' --filled'
else if (this.ghost) buttonClass += ' --ghost'
@@ -123,6 +128,15 @@ export default {
}
}
+ &.--large {
+ height: $size-button-large;
+ font-size: $font-size-large;
+
+ &.--circle {
+ width: $size-button-large;
+ }
+ }
+
&:not(.--icon-only) > .base-icon {
margin-right: $space-xx-small;
}
diff --git a/webapp/constants/groups.js b/webapp/constants/groups.js
index 3abf0d12a..1c49d3ff3 100644
--- a/webapp/constants/groups.js
+++ b/webapp/constants/groups.js
@@ -2,3 +2,4 @@
export const NAME_LENGTH_MIN = 3
export const NAME_LENGTH_MAX = 50
export const DESCRIPTION_WITHOUT_HTML_LENGTH_MIN = 100 // with removed HTML tags
+export const SHOW_GROUP_BUTTON_IN_HEADER = true
diff --git a/webapp/constants/headerMenu.js b/webapp/constants/headerMenu.js
new file mode 100644
index 000000000..7055c44e5
--- /dev/null
+++ b/webapp/constants/headerMenu.js
@@ -0,0 +1,13 @@
+export default {
+ MENU: [
+ // {
+ // name: 'Beiträge',
+ // path: '/#',
+ // },
+ // {
+ // name: 'Über Yunite',
+ // path: '/#',
+ // url: 'https://yunite.org',
+ // },
+ ],
+}
diff --git a/webapp/constants/logos.js b/webapp/constants/logos.js
index d093c7b46..2bea199da 100644
--- a/webapp/constants/logos.js
+++ b/webapp/constants/logos.js
@@ -2,6 +2,7 @@
// this are the paths in the webapp
export default {
LOGO_HEADER_PATH: '/img/custom/logo-horizontal.svg',
+ LOGO_HEADER_WIDTH: '130px',
LOGO_SIGNUP_PATH: '/img/custom/logo-squared.svg',
LOGO_WELCOME_PATH: '/img/custom/logo-squared.svg',
LOGO_LOGOUT_PATH: '/img/custom/logo-squared.svg',
diff --git a/webapp/graphql/Fragments.js b/webapp/graphql/Fragments.js
index b67851873..68cd02c6a 100644
--- a/webapp/graphql/Fragments.js
+++ b/webapp/graphql/Fragments.js
@@ -12,6 +12,7 @@ export const userFragment = gql`
deleted
}
`
+
export const locationAndBadgesFragment = (lang) => gql`
fragment locationAndBadges on User {
location {
diff --git a/webapp/graphql/User.js b/webapp/graphql/User.js
index 053cb022f..5c29bc0b4 100644
--- a/webapp/graphql/User.js
+++ b/webapp/graphql/User.js
@@ -221,25 +221,25 @@ export const updateUserMutation = () => {
$id: ID!
$slug: String
$name: String
- $locationName: String
$about: String
$allowEmbedIframes: Boolean
$showShoutsPublicly: Boolean
$sendNotificationEmails: Boolean
$termsAndConditionsAgreedVersion: String
$avatar: ImageInput
+ $locationName: String # empty string '' sets it to null
) {
UpdateUser(
id: $id
slug: $slug
name: $name
- locationName: $locationName
about: $about
allowEmbedIframes: $allowEmbedIframes
showShoutsPublicly: $showShoutsPublicly
sendNotificationEmails: $sendNotificationEmails
termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
avatar: $avatar
+ locationName: $locationName
) {
id
slug
diff --git a/webapp/graphql/groups.js b/webapp/graphql/groups.js
index 4350e19c9..5ee5869ce 100644
--- a/webapp/graphql/groups.js
+++ b/webapp/graphql/groups.js
@@ -2,170 +2,191 @@ import gql from 'graphql-tag'
// ------ mutations
-export const createGroupMutation = gql`
- mutation (
- $id: ID
- $name: String!
- $slug: String
- $about: String
- $description: String!
- $groupType: GroupType!
- $actionRadius: GroupActionRadius!
- $categoryIds: [ID]
- $locationName: String
- ) {
- CreateGroup(
- id: $id
- name: $name
- slug: $slug
- about: $about
- description: $description
- groupType: $groupType
- actionRadius: $actionRadius
- categoryIds: $categoryIds
- locationName: $locationName
+export const createGroupMutation = () => {
+ return gql`
+ mutation (
+ $id: ID
+ $name: String!
+ $slug: String
+ $about: String
+ $description: String!
+ $groupType: GroupType!
+ $actionRadius: GroupActionRadius!
+ $categoryIds: [ID]
+ $locationName: String # empty string '' sets it to null
) {
- id
- name
- slug
- createdAt
- updatedAt
- disabled
- deleted
- about
- description
- groupType
- actionRadius
- categories {
+ CreateGroup(
+ id: $id
+ name: $name
+ slug: $slug
+ about: $about
+ description: $description
+ groupType: $groupType
+ actionRadius: $actionRadius
+ categoryIds: $categoryIds
+ locationName: $locationName
+ ) {
id
- slug
name
- icon
+ slug
+ createdAt
+ updatedAt
+ disabled
+ deleted
+ about
+ description
+ descriptionExcerpt
+ groupType
+ actionRadius
+ categories {
+ id
+ slug
+ name
+ icon
+ }
+ locationName
+ myRole
}
- # locationName # test this as result
- myRole
}
- }
-`
+ `
+}
-export const updateGroupMutation = gql`
- mutation (
- $id: ID!
- $name: String
- $slug: String
- $about: String
- $description: String
- $actionRadius: GroupActionRadius
- $categoryIds: [ID]
- $avatar: ImageInput
- $locationName: String
- ) {
- UpdateGroup(
- id: $id
- name: $name
- slug: $slug
- about: $about
- description: $description
- actionRadius: $actionRadius
- categoryIds: $categoryIds
- avatar: $avatar
- locationName: $locationName
+export const updateGroupMutation = () => {
+ return gql`
+ mutation (
+ $id: ID!
+ $name: String
+ $slug: String
+ $about: String
+ $description: String
+ $actionRadius: GroupActionRadius
+ $categoryIds: [ID]
+ $avatar: ImageInput
+ $locationName: String # empty string '' sets it to null
) {
- id
- name
- slug
- createdAt
- updatedAt
- disabled
- deleted
- about
- description
- groupType
- actionRadius
- categories {
+ UpdateGroup(
+ id: $id
+ name: $name
+ slug: $slug
+ about: $about
+ description: $description
+ actionRadius: $actionRadius
+ categoryIds: $categoryIds
+ avatar: $avatar
+ locationName: $locationName
+ ) {
id
- slug
name
- icon
+ slug
+ createdAt
+ updatedAt
+ disabled
+ deleted
+ about
+ description
+ descriptionExcerpt
+ groupType
+ actionRadius
+ categories {
+ id
+ slug
+ name
+ icon
+ }
+ # avatar # test this as result
+ locationName
+ myRole
}
- # avatar # test this as result
- # locationName # test this as result
- myRole
}
- }
-`
+ `
+}
-export const joinGroupMutation = gql`
- mutation ($groupId: ID!, $userId: ID!) {
- JoinGroup(groupId: $groupId, userId: $userId) {
- id
- name
- slug
- myRoleInGroup
+export const joinGroupMutation = () => {
+ return gql`
+ mutation ($groupId: ID!, $userId: ID!) {
+ JoinGroup(groupId: $groupId, userId: $userId) {
+ id
+ name
+ slug
+ myRoleInGroup
+ }
}
- }
-`
+ `
+}
-export const leaveGroupMutation = gql`
- mutation ($groupId: ID!, $userId: ID!) {
- LeaveGroup(groupId: $groupId, userId: $userId) {
- id
- name
- slug
- myRoleInGroup
+export const leaveGroupMutation = () => {
+ return gql`
+ mutation ($groupId: ID!, $userId: ID!) {
+ LeaveGroup(groupId: $groupId, userId: $userId) {
+ id
+ name
+ slug
+ myRoleInGroup
+ }
}
- }
-`
+ `
+}
-export const changeGroupMemberRoleMutation = gql`
- mutation ($groupId: ID!, $userId: ID!, $roleInGroup: GroupMemberRole!) {
- ChangeGroupMemberRole(groupId: $groupId, userId: $userId, roleInGroup: $roleInGroup) {
- id
- name
- slug
- myRoleInGroup
+export const changeGroupMemberRoleMutation = () => {
+ return gql`
+ mutation ($groupId: ID!, $userId: ID!, $roleInGroup: GroupMemberRole!) {
+ ChangeGroupMemberRole(groupId: $groupId, userId: $userId, roleInGroup: $roleInGroup) {
+ id
+ name
+ slug
+ myRoleInGroup
+ }
}
- }
-`
+ `
+}
// ------ queries
-export const groupQuery = gql`
- query ($isMember: Boolean, $id: ID, $slug: String) {
- Group(isMember: $isMember, id: $id, slug: $slug) {
- id
- name
- slug
- createdAt
- updatedAt
- disabled
- deleted
- about
- description
- groupType
- actionRadius
- categories {
+export const groupQuery = (i18n) => {
+ const lang = i18n ? i18n.locale().toUpperCase() : 'EN'
+ return gql`
+ query ($isMember: Boolean, $id: ID, $slug: String) {
+ Group(isMember: $isMember, id: $id, slug: $slug) {
id
- slug
name
- icon
+ slug
+ createdAt
+ updatedAt
+ disabled
+ deleted
+ about
+ description
+ descriptionExcerpt
+ groupType
+ actionRadius
+ categories {
+ id
+ slug
+ name
+ icon
+ }
+ avatar {
+ url
+ }
+ locationName
+ location {
+ name: name${lang}
+ }
+ myRole
}
- avatar {
- url
- }
- # locationName # test this as result
- myRole
}
- }
-`
+ `
+}
-export const groupMembersQuery = gql`
- query ($id: ID!) {
- GroupMembers(id: $id) {
- id
- name
- slug
- myRoleInGroup
+export const groupMembersQuery = () => {
+ return gql`
+ query ($id: ID!) {
+ GroupMembers(id: $id) {
+ id
+ name
+ slug
+ myRoleInGroup
+ }
}
- }
-`
+ `
+}
diff --git a/webapp/layouts/default.vue b/webapp/layouts/default.vue
index 1e4fe5cf5..6fb3a2f54 100644
--- a/webapp/layouts/default.vue
+++ b/webapp/layouts/default.vue
@@ -4,11 +4,39 @@
+
+
+
@@ -88,18 +115,22 @@
diff --git a/webapp/pages/group/create.vue b/webapp/pages/group/create.vue
index 1c95fffbc..b16cf933f 100644
--- a/webapp/pages/group/create.vue
+++ b/webapp/pages/group/create.vue
@@ -1,26 +1,33 @@
-
Create Groupe
-
-
-
-
-
-
-
-
+
+ {{ $t('group.createNewGroup.title') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/webapp/pages/index.vue b/webapp/pages/index.vue
index 729edc415..2b142648e 100644
--- a/webapp/pages/index.vue
+++ b/webapp/pages/index.vue
@@ -38,7 +38,6 @@
v-tooltip="{
content: $t('contribution.newPost'),
placement: 'left',
- delay: { show: 500 },
}"
class="post-add-button"
icon="plus"
diff --git a/webapp/pages/my-groups.vue b/webapp/pages/my-groups.vue
index 49efa4aa1..7302b92ad 100644
--- a/webapp/pages/my-groups.vue
+++ b/webapp/pages/my-groups.vue
@@ -1,44 +1,73 @@
-
my groups
-
-
-
-
+
+ {{ $t('group.myGroups') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/webapp/pages/post/_id/_slug/index.vue b/webapp/pages/post/_id/_slug/index.vue
index d02a448da..724704f67 100644
--- a/webapp/pages/post/_id/_slug/index.vue
+++ b/webapp/pages/post/_id/_slug/index.vue
@@ -46,7 +46,6 @@
-
diff --git a/webapp/pages/profile/_id/_slug.vue b/webapp/pages/profile/_id/_slug.vue
index b4ed11e79..81bb5c49b 100644
--- a/webapp/pages/profile/_id/_slug.vue
+++ b/webapp/pages/profile/_id/_slug.vue
@@ -31,6 +31,7 @@
{{ userName }}
+
{{ userSlug }}
@@ -120,7 +121,6 @@
v-tooltip="{
content: $t('contribution.newPost'),
placement: 'left',
- delay: { show: 500 },
}"
:path="{ name: 'post-create' }"
class="profile-post-add-button"
@@ -238,7 +238,7 @@ export default {
followedByCountStartValue: 0,
followedByCount: followListVisibleCount,
followingCount: followListVisibleCount,
- updateUserMutation: updateUserMutation(),
+ updateUserMutation,
}
},
computed: {
@@ -254,7 +254,7 @@ export default {
},
userSlug() {
const { slug } = this.user || {}
- return slug && `@${slug}`
+ return slug
},
tabOptions() {
return [
diff --git a/webapp/pages/settings.vue b/webapp/pages/settings.vue
index 660449fb6..1fce64d8f 100644
--- a/webapp/pages/settings.vue
+++ b/webapp/pages/settings.vue
@@ -1,6 +1,9 @@
- {{ $t('settings.name') }}
+
+ {{ $t('settings.name') }}
+
+
@@ -39,10 +42,6 @@ export default {
name: this.$t('settings.social-media.name'),
path: `/settings/my-social-media`,
},
- {
- name: this.$t('settings.myGroups'),
- path: `/my-groups`,
- },
{
name: this.$t('settings.muted-users.name'),
path: `/settings/muted-users`,
diff --git a/webapp/pages/settings/index.vue b/webapp/pages/settings/index.vue
index 5d59f5ecb..b060e1327 100644
--- a/webapp/pages/settings/index.vue
+++ b/webapp/pages/settings/index.vue
@@ -48,6 +48,7 @@ import { queryLocations } from '~/graphql/location'
let timeout
export default {
+ name: 'NewsFeed',
data() {
return {
cities: [],
diff --git a/webapp/pages/terms-and-conditions-confirm.vue b/webapp/pages/terms-and-conditions-confirm.vue
index 30b4db6ef..5eef8ce66 100644
--- a/webapp/pages/terms-and-conditions-confirm.vue
+++ b/webapp/pages/terms-and-conditions-confirm.vue
@@ -25,6 +25,7 @@ import { VERSION } from '~/constants/terms-and-conditions-version.js'
import { updateUserMutation } from '~/graphql/User.js'
export default {
+ name: 'TermsAndConditionsConfirm',
layout: 'default',
head() {
return {
diff --git a/webapp/plugins/v-tooltip.js b/webapp/plugins/v-tooltip.js
index 77d0ac1b8..0c0cbc8d6 100644
--- a/webapp/plugins/v-tooltip.js
+++ b/webapp/plugins/v-tooltip.js
@@ -3,7 +3,7 @@ import VTooltip from 'v-tooltip'
Vue.use(VTooltip, {
defaultDelay: {
- show: 500,
+ show: 750,
hide: 50,
},
defaultOffset: 2,