diff --git a/.github/file-filters.yml b/.github/file-filters.yml
index d7f9cb6c0..8d2d93fac 100644
--- a/.github/file-filters.yml
+++ b/.github/file-filters.yml
@@ -1,4 +1,5 @@
backend: &backend
+ - '.github/workflows/test-backend.yml'
- 'backend/**/*'
- 'neo4j/**/*'
@@ -6,4 +7,5 @@ docker: &docker
- 'docker-compose.*'
webapp: &webapp
+ - '.github/workflows/test-webapp.yml'
- 'webapp/**/*'
diff --git a/.github/workflows/cleanup-cache-at-pr-closing.yml b/.github/workflows/cleanup-cache-at-pr-closing.yml
new file mode 100644
index 000000000..284702e76
--- /dev/null
+++ b/.github/workflows/cleanup-cache-at-pr-closing.yml
@@ -0,0 +1,42 @@
+###############################################################################
+# A Github repo has max 10 GB of cache.
+# https://github.blog/changelog/2021-11-23-github-actions-cache-size-is-now-increased-to-10gb-per-repository/
+#
+# To avoid "cache thrashing" by their cache eviction policy it is recommended
+# to apply a cache cleanup workflow at PR closing to dele cache leftovers of
+# the current branch:
+# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#force-deleting-cache-entries
+###############################################################################
+
+name: ocelot.social cache cleanup on pr closing
+
+on:
+ pull_request:
+ types:
+ - closed
+
+jobs:
+ clean-branch-cache:
+ name: Cleanup branch cache
+ runs-on: ubuntu-latest
+ continue-on-error: true
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ - name: Cleanup
+ run: |
+ gh extension install actions/gh-actions-cache
+ REPO=${{ github.repository }}
+ BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge"
+ echo "Fetching list of cache key"
+ cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 )
+ set +e
+ echo "Deleting caches..."
+ for cacheKey in $cacheKeysForPR
+ do
+ gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
+ done
+ echo "Done"
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml
index 84d87c770..03e517826 100644
--- a/.github/workflows/test-backend.yml
+++ b/.github/workflows/test-backend.yml
@@ -1,7 +1,7 @@
name: ocelot.social backend test CI
-on: [push]
+on: push
jobs:
files-changed:
@@ -13,7 +13,7 @@ jobs:
steps:
- uses: actions/checkout@v3.3.0
- - name: Check for frontend file changes
+ - name: Check for backend file changes
uses: dorny/paths-filter@v2.11.1
id: changes
with:
@@ -34,12 +34,13 @@ jobs:
run: |
docker build --target community -t "ocelotsocialnetwork/neo4j-community:test" neo4j/
docker save "ocelotsocialnetwork/neo4j-community:test" > /tmp/neo4j.tar
-
- - name: Upload Artifact
- uses: actions/upload-artifact@v3
+
+ - name: Cache docker images
+ id: cache-neo4j
+ uses: actions/cache/save@v3.3.1
with:
- name: docker-neo4j-image
path: /tmp/neo4j.tar
+ key: ${{ github.run_id }}-backend-neo4j-cache
build_test_backend:
name: Docker Build Test - Backend
@@ -54,12 +55,13 @@ jobs:
run: |
docker build --target test -t "ocelotsocialnetwork/backend:test" backend/
docker save "ocelotsocialnetwork/backend:test" > /tmp/backend.tar
-
- - name: Upload Artifact
- uses: actions/upload-artifact@v3
+
+ - name: Cache docker images
+ id: cache-backend
+ uses: actions/cache/save@v3.3.1
with:
- name: docker-backend-test
path: /tmp/backend.tar
+ key: ${{ github.run_id }}-backend-cache
lint_backend:
name: Lint Backend
@@ -84,28 +86,29 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3
- - name: Download Docker Image (Neo4J)
- uses: actions/download-artifact@v3
+ - name: Restore Neo4J cache
+ uses: actions/cache/restore@v3.3.1
with:
- name: docker-neo4j-image
- path: /tmp
+ path: /tmp/neo4j.tar
+ key: ${{ github.run_id }}-backend-neo4j-cache
+ fail-on-cache-miss: true
- - name: Load Docker Image
- run: docker load < /tmp/neo4j.tar
-
- - name: Download Docker Image (Backend)
- uses: actions/download-artifact@v3
+ - name: Restore Backend cache
+ uses: actions/cache/restore@v3.3.1
with:
- name: docker-backend-test
- path: /tmp
+ path: /tmp/backend.tar
+ key: ${{ github.run_id }}-backend-cache
+ fail-on-cache-miss: true
- - name: Load Docker Image
- run: docker load < /tmp/backend.tar
+ - name: Load Docker Images
+ run: |
+ docker load < /tmp/neo4j.tar
+ docker load < /tmp/backend.tar
- - name: backend | copy env files webapp
- run: cp webapp/.env.template webapp/.env
- - name: backend | copy env files backend
- run: cp backend/.env.template backend/.env
+ - name: backend | copy env files
+ run: |
+ cp webapp/.env.template webapp/.env
+ cp backend/.env.template backend/.env
- name: backend | docker-compose
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps neo4j backend
@@ -118,3 +121,20 @@ jobs:
- name: backend | Unit test incl. coverage check
run: docker-compose exec -T backend yarn test
+
+ cleanup:
+ name: Cleanup
+ if: ${{ needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.docker == 'true' }}
+ needs: [files-changed, unit_test_backend]
+ runs-on: ubuntu-latest
+ continue-on-error: true
+ steps:
+ - name: Delete cache
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ gh extension install actions/gh-actions-cache
+ KEY="${{ github.run_id }}-backend-neo4j-cache"
+ gh actions-cache delete $KEY -R Ocelot-Social-Community/Ocelot-Social --confirm
+ KEY="${{ github.run_id }}-backend-cache"
+ gh actions-cache delete $KEY -R Ocelot-Social-Community/Ocelot-Social --confirm
diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml
index 2e6986722..02d65ba9e 100644
--- a/.github/workflows/test-e2e.yml
+++ b/.github/workflows/test-e2e.yml
@@ -1,12 +1,11 @@
name: ocelot.social end-to-end test CI
+
on: push
jobs:
docker_preparation:
name: Fullstack test preparation
runs-on: ubuntu-latest
- outputs:
- pr-number: ${{ steps.pr.outputs.number }}
steps:
- name: Checkout code
uses: actions/checkout@v3
@@ -34,10 +33,6 @@ jobs:
yarn build
cd ..
yarn install
-
- - name: Get pr number
- id: pr
- uses: 8BitJonny/gh-get-current-pr@2.2.0
- name: Cache docker images
id: cache
@@ -48,7 +43,7 @@ jobs:
/home/runner/.cache/Cypress
/home/runner/work/Ocelot-Social/Ocelot-Social
/tmp/images/
- key: e2e-preparation-cache-pr${{ steps.pr.outputs.number }}
+ key: ${{ github.run_id }}-e2e-preparation-cache
fullstack_tests:
name: Fullstack tests
@@ -71,7 +66,7 @@ jobs:
/home/runner/.cache/Cypress
/home/runner/work/Ocelot-Social/Ocelot-Social
/tmp/images/
- key: e2e-preparation-cache-pr${{ needs.docker_preparation.outputs.pr-number }}
+ key: ${{ github.run_id }}-e2e-preparation-cache
fail-on-cache-miss: true
- name: Boot up test system | docker-compose
@@ -104,14 +99,14 @@ jobs:
cleanup:
name: Cleanup
- if: always()
needs: [docker_preparation, fullstack_tests]
runs-on: ubuntu-latest
+ continue-on-error: true
steps:
- name: Delete cache
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh extension install actions/gh-actions-cache
- KEY="e2e-preparation-cache-pr${{ needs.docker_preparation.outputs.pr-number }}"
+ KEY="${{ github.run_id }}-e2e-preparation-cache"
gh actions-cache delete $KEY -R Ocelot-Social-Community/Ocelot-Social --confirm
\ No newline at end of file
diff --git a/.github/workflows/test-webapp.yml b/.github/workflows/test-webapp.yml
index c1aee47cf..2b1e144a5 100644
--- a/.github/workflows/test-webapp.yml
+++ b/.github/workflows/test-webapp.yml
@@ -1,7 +1,7 @@
name: ocelot.social webapp test CI
-on: [push]
+on: push
jobs:
files-changed:
@@ -34,7 +34,7 @@ jobs:
run: |
scripts/translations/sort.sh
scripts/translations/missing-keys.sh
-
+
build_test_webapp:
name: Docker Build Test - Webapp
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.webapp == 'true'
@@ -44,16 +44,16 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3
- - name: webapp | Build 'test' image
+ - name: Webapp | Build 'test' image
run: |
docker build --target test -t "ocelotsocialnetwork/webapp:test" webapp/
docker save "ocelotsocialnetwork/webapp:test" > /tmp/webapp.tar
- - name: Upload Artifact
- uses: actions/upload-artifact@v3
+ - name: Cache docker image
+ uses: actions/cache/save@v3.3.1
with:
- name: docker-webapp-test
path: /tmp/webapp.tar
+ key: ${{ github.run_id }}-webapp-cache
lint_webapp:
name: Lint Webapp
@@ -78,20 +78,19 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3
- - name: Download Docker Image (Webapp)
- uses: actions/download-artifact@v3
+ - name: Restore webapp cache
+ uses: actions/cache/restore@v3.3.1
with:
- name: docker-webapp-test
- path: /tmp
+ path: /tmp/webapp.tar
+ key: ${{ github.run_id }}-webapp-cache
- name: Load Docker Image
run: docker load < /tmp/webapp.tar
- - name: backend | copy env files webapp
- run: cp webapp/.env.template webapp/.env
-
- - name: backend | copy env files backend
- run: cp backend/.env.template backend/.env
+ - name: Copy env files
+ run: |
+ cp webapp/.env.template webapp/.env
+ cp backend/.env.template backend/.env
- name: backend | docker-compose
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp
@@ -99,3 +98,18 @@ jobs:
- name: webapp | Unit tests incl. coverage check
run: docker-compose exec -T webapp yarn test
+ cleanup:
+ name: Cleanup
+ if: ${{ needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.webapp == 'true' }}
+ needs: [files-changed, unit_test_webapp]
+ runs-on: ubuntu-latest
+ continue-on-error: true
+ steps:
+ - name: Delete cache
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ gh extension install actions/gh-actions-cache
+ KEY="${{ github.run_id }}-webapp-cache"
+ gh actions-cache delete $KEY -R Ocelot-Social-Community/Ocelot-Social --confirm
+
diff --git a/backend/src/db/seed.ts b/backend/src/db/seed.ts
index 7286683dd..53cd4cea6 100644
--- a/backend/src/db/seed.ts
+++ b/backend/src/db/seed.ts
@@ -269,17 +269,17 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
await dagobert.relateTo(louie, 'blocked')
// categories
- await Promise.all(
- categories.map(({ icon, name }, index) => {
- return Factory.build('category', {
- id: `cat${index + 1}`,
- slug: name,
- name,
- icon,
- })
- }),
- )
+ let i = 0
+ for (const category of categories) {
+ await Factory.build('category', {
+ id: `cat${i++}`,
+ slug: category.name,
+ naem: category.name,
+ icon: category.icon,
+ })
+ }
+ // tags
const environment = await Factory.build('tag', {
id: 'Environment',
})
@@ -295,361 +295,324 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
// groups
authenticatedUser = await peterLustig.toJson()
- await Promise.all([
- mutate({
- mutation: createGroupMutation(),
- variables: {
- id: 'g0',
- name: 'Investigative Journalism',
- about: 'Investigative journalists share ideas and insights and can collaborate.',
- 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', 'cat12', 'cat16'],
- locationName: 'Hamburg, Germany',
- },
- }),
- ])
- await Promise.all([
- mutate({
- mutation: joinGroupMutation(),
- variables: {
- groupId: 'g0',
- userId: 'u2',
- },
- }),
- mutate({
- mutation: joinGroupMutation(),
- variables: {
- groupId: 'g0',
- userId: 'u4',
- },
- }),
- mutate({
- mutation: joinGroupMutation(),
- variables: {
- groupId: 'g0',
- userId: 'u6',
- },
- }),
- ])
- await Promise.all([
- mutate({
- mutation: changeGroupMemberRoleMutation(),
- variables: {
- groupId: 'g0',
- userId: 'u2',
- roleInGroup: 'usual',
- },
- }),
- mutate({
- mutation: changeGroupMemberRoleMutation(),
- variables: {
- groupId: 'g0',
- userId: 'u4',
- roleInGroup: 'admin',
- },
- }),
- ])
+ await mutate({
+ mutation: createGroupMutation(),
+ variables: {
+ id: 'g0',
+ name: 'Investigative Journalism',
+ about: 'Investigative journalists share ideas and insights and can collaborate.',
+ 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', 'cat12', 'cat16'],
+ locationName: 'Hamburg, Germany',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation(),
+ variables: {
+ groupId: 'g0',
+ userId: 'u2',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation(),
+ variables: {
+ groupId: 'g0',
+ userId: 'u4',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation(),
+ variables: {
+ groupId: 'g0',
+ userId: 'u6',
+ },
+ })
+
+ await mutate({
+ mutation: changeGroupMemberRoleMutation(),
+ variables: {
+ groupId: 'g0',
+ userId: 'u2',
+ roleInGroup: 'usual',
+ },
+ })
+
+ await mutate({
+ mutation: changeGroupMemberRoleMutation(),
+ variables: {
+ groupId: 'g0',
+ userId: 'u4',
+ roleInGroup: 'admin',
+ },
+ })
// post into group
- await Promise.all([
- mutate({
- mutation: createPostMutation(),
- variables: {
- id: 'p0-g0',
- groupId: 'g0',
- title: `What happend in Shanghai?`,
- content: 'A sack of rise dropped in Shanghai. Should we further investigate?',
- categoryIds: ['cat6'],
- },
- }),
- ])
+ await mutate({
+ mutation: createPostMutation(),
+ variables: {
+ id: 'p0-g0',
+ groupId: 'g0',
+ title: `What happend in Shanghai?`,
+ content: 'A sack of rise dropped in Shanghai. Should we further investigate?',
+ categoryIds: ['cat6'],
+ },
+ })
+
authenticatedUser = await bobDerBaumeister.toJson()
- await Promise.all([
- mutate({
- mutation: createPostMutation(),
- variables: {
- id: 'p1-g0',
- groupId: 'g0',
- title: `The man on the moon`,
- content: 'We have to further investigate about the stories of a man living on the moon.',
- categoryIds: ['cat12', 'cat16'],
- },
- }),
- ])
+ await mutate({
+ mutation: createPostMutation(),
+ variables: {
+ id: 'p1-g0',
+ groupId: 'g0',
+ title: `The man on the moon`,
+ content: 'We have to further investigate about the stories of a man living on the moon.',
+ categoryIds: ['cat12', 'cat16'],
+ },
+ })
authenticatedUser = await jennyRostock.toJson()
- await Promise.all([
- mutate({
- mutation: createGroupMutation(),
- variables: {
- id: 'g1',
- name: 'School For Citizens',
- about: 'Our children shall receive education for life.',
- 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: ['cat8', 'cat14'],
- locationName: 'France',
- },
- }),
- ])
- await Promise.all([
- mutate({
- mutation: joinGroupMutation(),
- variables: {
- groupId: 'g1',
- userId: 'u1',
- },
- }),
- mutate({
- mutation: joinGroupMutation(),
- variables: {
- groupId: 'g1',
- userId: 'u2',
- },
- }),
- mutate({
- mutation: joinGroupMutation(),
- variables: {
- groupId: 'g1',
- userId: 'u5',
- },
- }),
- mutate({
- mutation: joinGroupMutation(),
- variables: {
- groupId: 'g1',
- userId: 'u6',
- },
- }),
- mutate({
- mutation: joinGroupMutation(),
- variables: {
- groupId: 'g1',
- userId: 'u7',
- },
- }),
- ])
- await Promise.all([
- mutate({
- mutation: changeGroupMemberRoleMutation(),
- variables: {
- groupId: 'g1',
- userId: 'u1',
- roleInGroup: 'usual',
- },
- }),
- mutate({
- mutation: changeGroupMemberRoleMutation(),
- variables: {
- groupId: 'g1',
- userId: 'u5',
- roleInGroup: 'admin',
- },
- }),
- mutate({
- mutation: changeGroupMemberRoleMutation(),
- variables: {
- groupId: 'g1',
- userId: 'u6',
- roleInGroup: 'owner',
- },
- }),
- ])
+ await mutate({
+ mutation: createGroupMutation(),
+ variables: {
+ id: 'g1',
+ name: 'School For Citizens',
+ about: 'Our children shall receive education for life.',
+ 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: ['cat8', 'cat14'],
+ locationName: 'France',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation(),
+ variables: {
+ groupId: 'g1',
+ userId: 'u1',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation(),
+ variables: {
+ groupId: 'g1',
+ userId: 'u2',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation(),
+ variables: {
+ groupId: 'g1',
+ userId: 'u5',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation(),
+ variables: {
+ groupId: 'g1',
+ userId: 'u6',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation(),
+ variables: {
+ groupId: 'g1',
+ userId: 'u7',
+ },
+ })
+
+ await mutate({
+ mutation: changeGroupMemberRoleMutation(),
+ variables: {
+ groupId: 'g1',
+ userId: 'u1',
+ roleInGroup: 'usual',
+ },
+ })
+ await mutate({
+ mutation: changeGroupMemberRoleMutation(),
+ variables: {
+ groupId: 'g1',
+ userId: 'u5',
+ roleInGroup: 'admin',
+ },
+ })
+ await mutate({
+ mutation: changeGroupMemberRoleMutation(),
+ variables: {
+ groupId: 'g1',
+ userId: 'u6',
+ roleInGroup: 'owner',
+ },
+ })
// post into group
- await Promise.all([
- mutate({
- mutation: createPostMutation(),
- variables: {
- id: 'p0-g1',
- groupId: 'g1',
- title: `Can we use ocelot for education?`,
- content: 'I like the concept of this school. Can we use our software in this?',
- categoryIds: ['cat8'],
- },
- }),
- ])
+ await mutate({
+ mutation: createPostMutation(),
+ variables: {
+ id: 'p0-g1',
+ groupId: 'g1',
+ title: `Can we use ocelot for education?`,
+ content: 'I like the concept of this school. Can we use our software in this?',
+ categoryIds: ['cat8'],
+ },
+ })
authenticatedUser = await peterLustig.toJson()
- await Promise.all([
- mutate({
- mutation: createPostMutation(),
- variables: {
- id: 'p1-g1',
- groupId: 'g1',
- title: `Can we push this idea out of France?`,
- content: 'This idea is too inportant to have the scope only on France.',
- categoryIds: ['cat14'],
- },
- }),
- ])
+ await mutate({
+ mutation: createPostMutation(),
+ variables: {
+ id: 'p1-g1',
+ groupId: 'g1',
+ title: `Can we push this idea out of France?`,
+ content: 'This idea is too inportant to have the scope only on France.',
+ categoryIds: ['cat14'],
+ },
+ })
authenticatedUser = await bobDerBaumeister.toJson()
- await Promise.all([
- mutate({
- mutation: createGroupMutation(),
- variables: {
- id: 'g2',
- name: 'Yoga Practice',
- about: 'We do yoga around the clock.',
- 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: ['cat4', 'cat5', 'cat17'],
- },
- }),
- ])
- await Promise.all([
- mutate({
- mutation: joinGroupMutation(),
- variables: {
- groupId: 'g2',
- userId: 'u3',
- },
- }),
- mutate({
- mutation: joinGroupMutation(),
- variables: {
- groupId: 'g2',
- userId: 'u4',
- },
- }),
- mutate({
- mutation: joinGroupMutation(),
- variables: {
- groupId: 'g2',
- userId: 'u5',
- },
- }),
- mutate({
- mutation: joinGroupMutation(),
- variables: {
- groupId: 'g2',
- userId: 'u6',
- },
- }),
- mutate({
- mutation: joinGroupMutation(),
- variables: {
- groupId: 'g2',
- userId: 'u7',
- },
- }),
- ])
- await Promise.all([
- mutate({
- 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: 'u5',
- roleInGroup: 'admin',
- },
- }),
- mutate({
- mutation: changeGroupMemberRoleMutation(),
- variables: {
- groupId: 'g2',
- userId: 'u6',
- roleInGroup: 'usual',
- },
- }),
- ])
+ await mutate({
+ mutation: createGroupMutation(),
+ variables: {
+ id: 'g2',
+ name: 'Yoga Practice',
+ about: 'We do yoga around the clock.',
+ 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: ['cat4', 'cat5', 'cat17'],
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation(),
+ variables: {
+ groupId: 'g2',
+ userId: 'u3',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation(),
+ variables: {
+ groupId: 'g2',
+ userId: 'u4',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation(),
+ variables: {
+ groupId: 'g2',
+ userId: 'u5',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation(),
+ variables: {
+ groupId: 'g2',
+ userId: 'u6',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation(),
+ variables: {
+ groupId: 'g2',
+ userId: 'u7',
+ },
+ })
+
+ await mutate({
+ mutation: changeGroupMemberRoleMutation(),
+ variables: {
+ groupId: 'g2',
+ userId: 'u3',
+ roleInGroup: 'usual',
+ },
+ })
+ await mutate({
+ mutation: changeGroupMemberRoleMutation(),
+ variables: {
+ groupId: 'g2',
+ userId: 'u4',
+ roleInGroup: 'pending',
+ },
+ })
+ await mutate({
+ mutation: changeGroupMemberRoleMutation(),
+ variables: {
+ groupId: 'g2',
+ userId: 'u5',
+ roleInGroup: 'admin',
+ },
+ })
+ await mutate({
+ mutation: changeGroupMemberRoleMutation(),
+ variables: {
+ groupId: 'g2',
+ userId: 'u6',
+ roleInGroup: 'usual',
+ },
+ })
authenticatedUser = await louie.toJson()
- await Promise.all([
- mutate({
- mutation: createPostMutation(),
- variables: {
- id: 'p0-g2',
- groupId: 'g2',
- title: `I am a Noob`,
- content: 'I am new to Yoga and did not join this group so far.',
- categoryIds: ['cat4'],
- },
- }),
- ])
+ await mutate({
+ mutation: createPostMutation(),
+ variables: {
+ id: 'p0-g2',
+ groupId: 'g2',
+ title: `I am a Noob`,
+ content: 'I am new to Yoga and did not join this group so far.',
+ categoryIds: ['cat4'],
+ },
+ })
// Create Events (by peter lustig)
authenticatedUser = await peterLustig.toJson()
const now = new Date()
- await Promise.all([
- mutate({
- mutation: createPostMutation(),
- variables: {
- id: 'e0',
- title: 'Illegaler Kindergeburtstag',
- content: 'Elli hat nächste Woche Geburtstag. Wir feiern das!',
- categoryIds: ['cat4'],
- postType: 'Event',
- eventInput: {
- eventStart: new Date(
- now.getFullYear(),
- now.getMonth(),
- now.getDate() + 7,
- ).toISOString(),
- eventVenue: 'Ellis Kinderzimmer',
- eventLocationName: 'Deutschland',
- },
+ await mutate({
+ mutation: createPostMutation(),
+ variables: {
+ id: 'e0',
+ title: 'Illegaler Kindergeburtstag',
+ content: 'Elli hat nächste Woche Geburtstag. Wir feiern das!',
+ categoryIds: ['cat4'],
+ postType: 'Event',
+ eventInput: {
+ eventStart: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 7).toISOString(),
+ eventVenue: 'Ellis Kinderzimmer',
+ eventLocationName: 'Deutschland',
},
- }),
- mutate({
- mutation: createPostMutation(),
- variables: {
- id: 'e1',
- title: 'Wir Schützen den Stuttgarter Schlossgarten',
- content: 'Kein Baum wird gefällt werden!',
- categoryIds: ['cat5'],
- postType: 'Event',
- eventInput: {
- eventStart: new Date(
- now.getFullYear(),
- now.getMonth(),
- now.getDate() + 1,
- ).toISOString(),
- eventVenue: 'Schlossgarten',
- eventLocationName: 'Stuttgart',
- },
+ },
+ })
+ await mutate({
+ mutation: createPostMutation(),
+ variables: {
+ id: 'e1',
+ title: 'Wir Schützen den Stuttgarter Schlossgarten',
+ content: 'Kein Baum wird gefällt werden!',
+ categoryIds: ['cat5'],
+ postType: 'Event',
+ eventInput: {
+ eventStart: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1).toISOString(),
+ eventVenue: 'Schlossgarten',
+ eventLocationName: 'Stuttgart',
},
- }),
- mutate({
- mutation: createPostMutation(),
- variables: {
- id: 'e2',
- title: 'IT 4 Change Treffen',
- content: 'Wir sitzen eine Woche zusammen rum und glotzen uns blöde an.',
- categoryIds: ['cat5'],
- postType: 'Event',
- eventInput: {
- eventStart: new Date(
- now.getFullYear(),
- now.getMonth(),
- now.getDate() + 1,
- ).toISOString(),
- eventEnd: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 4).toISOString(),
- eventVenue: 'Ferienlager',
- eventLocationName: 'Bahra, Sachsen',
- },
+ },
+ })
+ await mutate({
+ mutation: createPostMutation(),
+ variables: {
+ id: 'e2',
+ title: 'IT 4 Change Treffen',
+ content: 'Wir sitzen eine Woche zusammen rum und glotzen uns blöde an.',
+ categoryIds: ['cat5'],
+ postType: 'Event',
+ eventInput: {
+ eventStart: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1).toISOString(),
+ eventEnd: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 4).toISOString(),
+ eventVenue: 'Ferienlager',
+ eventLocationName: 'Bahra, Sachsen',
},
- }),
- ])
+ },
+ })
let passedEvent = await neode.find('Post', 'e1')
await passedEvent.update({ eventStart: new Date(2010, 8, 30, 10).toISOString() })
@@ -836,48 +799,49 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
const hashtagAndMention1 =
'The new physics of #QuantenFlussTheorie can explain #QuantumGravity! @peter-lustig got that already. ;-)'
- await Promise.all([
- mutate({
- mutation: createPostMutation(),
- variables: {
- id: 'p2',
- title: `Nature Philosophy Yoga`,
- content: hashtag1,
- categoryIds: ['cat2'],
- },
- }),
- mutate({
- mutation: createPostMutation(),
- variables: {
- id: 'p7',
- title: 'This is post #7',
- content: `${mention1} ${faker.lorem.paragraph()}`,
- categoryIds: ['cat7'],
- },
- }),
- mutate({
- mutation: createPostMutation(),
- variables: {
- id: 'p8',
- image: faker.image.unsplash.nature(),
- title: `Quantum Flow Theory explains Quantum Gravity`,
- content: hashtagAndMention1,
- categoryIds: ['cat8'],
- },
- }),
- mutate({
- mutation: createPostMutation(),
- variables: {
- id: 'p12',
- title: 'This is post #12',
- content: `${mention2} ${faker.lorem.paragraph()}`,
- categoryIds: ['cat12'],
- },
- }),
- ])
- const [p2, p7, p8, p12] = await Promise.all(
- ['p2', 'p7', 'p8', 'p12'].map((id) => neode.find('Post', id)),
- )
+ await mutate({
+ mutation: createPostMutation(),
+ variables: {
+ id: 'p2',
+ title: `Nature Philosophy Yoga`,
+ content: hashtag1,
+ categoryIds: ['cat2'],
+ },
+ })
+ await mutate({
+ mutation: createPostMutation(),
+ variables: {
+ id: 'p7',
+ title: 'This is post #7',
+ content: `${mention1} ${faker.lorem.paragraph()}`,
+ categoryIds: ['cat7'],
+ },
+ })
+ await mutate({
+ mutation: createPostMutation(),
+ variables: {
+ id: 'p8',
+ image: faker.image.unsplash.nature(),
+ title: `Quantum Flow Theory explains Quantum Gravity`,
+ content: hashtagAndMention1,
+ categoryIds: ['cat8'],
+ },
+ })
+ await mutate({
+ mutation: createPostMutation(),
+ variables: {
+ id: 'p12',
+ title: 'This is post #12',
+ content: `${mention2} ${faker.lorem.paragraph()}`,
+ categoryIds: ['cat12'],
+ },
+ })
+
+ const p2 = await neode.find('Post', 'p2')
+ const p7 = await neode.find('Post', 'p7')
+ const p8 = await neode.find('Post', 'p8')
+ const p12 = await neode.find('Post', 'p12')
+
authenticatedUser = null
authenticatedUser = await dewey.toJson()
@@ -885,31 +849,30 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
'I heard @jenny-rostock has practiced it for 3 years now.'
const mentionInComment2 =
'Did @peter-lustig tell you?'
- await Promise.all([
- mutate({
- mutation: createCommentMutation,
- variables: {
- id: 'c4',
- postId: 'p2',
- content: mentionInComment1,
- },
- }),
- mutate({
- mutation: createCommentMutation,
- variables: {
- id: 'c4-1',
- postId: 'p2',
- content: mentionInComment2,
- },
- }),
- mutate({
- mutation: createCommentMutation,
- variables: {
- postId: 'p14',
- content: faker.lorem.paragraph(),
- },
- }), // should send a notification
- ])
+ await mutate({
+ mutation: createCommentMutation,
+ variables: {
+ id: 'c4',
+ postId: 'p2',
+ content: mentionInComment1,
+ },
+ })
+ await mutate({
+ mutation: createCommentMutation,
+ variables: {
+ id: 'c4-1',
+ postId: 'p2',
+ content: mentionInComment2,
+ },
+ })
+ await mutate({
+ mutation: createCommentMutation,
+ variables: {
+ postId: 'p14',
+ content: faker.lorem.paragraph(),
+ },
+ }) // should send a notification
+
authenticatedUser = null
const comments: any[] = []
@@ -1193,366 +1156,324 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
closed: true,
})
- const additionalUsers = await Promise.all(
- [...Array(30).keys()].map(() => Factory.build('user')),
- )
+ const additionalUsers: any[] = []
+ for (let i = 0; i < 30; i++) {
+ const user = await Factory.build('user')
+ await jennyRostock.relateTo(user, 'following')
+ await user.relateTo(jennyRostock, 'following')
+ additionalUsers.push(user)
+ }
- await Promise.all(
- additionalUsers.map(async (user) => {
- await jennyRostock.relateTo(user, 'following')
- await user.relateTo(jennyRostock, 'following')
- }),
- )
+ // Jenny users
+ for (let i = 0; i < 30; i++) {
+ await Factory.build('user', { name: `Jenny${i}` })
+ }
- await Promise.all(
- [...Array(30).keys()].map((index) => Factory.build('user', { name: `Jenny${index}` })),
- )
+ // Jenny posts
+ for (let i = 0; i < 30; i++) {
+ await Factory.build(
+ 'post',
+ { content: `Jenny ${faker.lorem.sentence()}` },
+ {
+ categoryIds: ['cat1'],
+ author: jennyRostock,
+ image: Factory.build('image', {
+ url: faker.image.unsplash.objects(),
+ }),
+ },
+ )
+ }
- await Promise.all(
- [...Array(30).keys()].map(() =>
- Factory.build(
- 'post',
- { content: `Jenny ${faker.lorem.sentence()}` },
- {
- categoryIds: ['cat1'],
- author: jennyRostock,
- image: Factory.build('image', {
- url: faker.image.unsplash.objects(),
- }),
- },
- ),
- ),
- )
+ // comments on p2 jenny
+ for (let i = 0; i < 6; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ author: jennyRostock,
+ postId: 'p2',
+ },
+ )
+ }
- await Promise.all(
- [...Array(30).keys()].map(() =>
- Factory.build(
- 'post',
- {},
- {
- categoryIds: ['cat1'],
- author: jennyRostock,
- image: Factory.build('image', {
- url: faker.image.unsplash.objects(),
- }),
- },
- ),
- ),
- )
+ // comments on p15 jenny
+ for (let i = 0; i < 4; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ author: jennyRostock,
+ postId: 'p15',
+ },
+ )
+ }
- await Promise.all(
- [...Array(6).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- author: jennyRostock,
- postId: 'p2',
- },
- ),
- ),
- )
+ // comments on p4 jenny
+ for (let i = 0; i < 2; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ author: jennyRostock,
+ postId: 'p4',
+ },
+ )
+ }
- await Promise.all(
- [...Array(4).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- author: jennyRostock,
- postId: 'p15',
- },
- ),
- ),
- )
+ // Posts Peter Lustig
+ for (let i = 0; i < 21; i++) {
+ await Factory.build(
+ 'post',
+ {},
+ {
+ categoryIds: ['cat1'],
+ author: peterLustig,
+ image: Factory.build('image', {
+ url: faker.image.unsplash.buildings(),
+ }),
+ },
+ )
+ }
- await Promise.all(
- [...Array(2).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- author: jennyRostock,
- postId: 'p4',
- },
- ),
- ),
- )
+ // comments p4 peter
+ for (let i = 0; i < 3; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ author: peterLustig,
+ postId: 'p4',
+ },
+ )
+ }
- await Promise.all(
- [...Array(21).keys()].map(() =>
- Factory.build(
- 'post',
- {},
- {
- categoryIds: ['cat1'],
- author: peterLustig,
- image: Factory.build('image', {
- url: faker.image.unsplash.buildings(),
- }),
- },
- ),
- ),
- )
+ // comments p14 peter
+ for (let i = 0; i < 3; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ author: peterLustig,
+ postId: 'p14',
+ },
+ )
+ }
- await Promise.all(
- [...Array(3).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- author: peterLustig,
- postId: 'p4',
- },
- ),
- ),
- )
+ // comments p0 peter
+ for (let i = 0; i < 3; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ author: peterLustig,
+ postId: 'p0',
+ },
+ )
+ }
- await Promise.all(
- [...Array(3).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- author: peterLustig,
- postId: 'p14',
- },
- ),
- ),
- )
+ // Posts dewey
+ for (let i = 0; i < 11; i++) {
+ await Factory.build(
+ 'post',
+ {},
+ {
+ categoryIds: ['cat1'],
+ author: dewey,
+ image: Factory.build('image', {
+ url: faker.image.unsplash.food(),
+ }),
+ },
+ )
+ }
- await Promise.all(
- [...Array(6).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- author: peterLustig,
- postId: 'p0',
- },
- ),
- ),
- )
+ // Comments p2 dewey
+ for (let i = 0; i < 7; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ author: dewey,
+ postId: 'p2',
+ },
+ )
+ }
- await Promise.all(
- [...Array(11).keys()].map(() =>
- Factory.build(
- 'post',
- {},
- {
- categoryIds: ['cat1'],
- author: dewey,
- image: Factory.build('image', {
- url: faker.image.unsplash.food(),
- }),
- },
- ),
- ),
- )
+ // Comments p6 dewey
+ for (let i = 0; i < 5; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ author: dewey,
+ postId: 'p6',
+ },
+ )
+ }
- await Promise.all(
- [...Array(7).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- author: dewey,
- postId: 'p2',
- },
- ),
- ),
- )
+ // Comments p9 dewey
+ for (let i = 0; i < 2; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ author: dewey,
+ postId: 'p9',
+ },
+ )
+ }
- await Promise.all(
- [...Array(5).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- author: dewey,
- postId: 'p6',
- },
- ),
- ),
- )
+ // Posts louie
+ for (let i = 0; i < 16; i++) {
+ await Factory.build(
+ 'post',
+ {},
+ {
+ categoryIds: ['cat1'],
+ author: louie,
+ image: Factory.build('image', {
+ url: faker.image.unsplash.technology(),
+ }),
+ },
+ )
+ }
- await Promise.all(
- [...Array(2).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- author: dewey,
- postId: 'p9',
- },
- ),
- ),
- )
+ // Comments p1 louie
+ for (let i = 0; i < 4; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ postId: 'p1',
+ author: louie,
+ },
+ )
+ }
- await Promise.all(
- [...Array(16).keys()].map(() =>
- Factory.build(
- 'post',
- {},
- {
- categoryIds: ['cat1'],
- author: louie,
- image: Factory.build('image', {
- url: faker.image.unsplash.technology(),
- }),
- },
- ),
- ),
- )
+ // Comments p10 louie
+ for (let i = 0; i < 8; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ author: louie,
+ postId: 'p10',
+ },
+ )
+ }
- await Promise.all(
- [...Array(4).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- postId: 'p1',
- author: louie,
- },
- ),
- ),
- )
+ // Comments p13 louie
+ for (let i = 0; i < 5; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ author: louie,
+ postId: 'p13',
+ },
+ )
+ }
- await Promise.all(
- [...Array(8).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- author: louie,
- postId: 'p10',
- },
- ),
- ),
- )
+ // Posts Bob der Baumeister
+ for (let i = 0; i < 45; i++) {
+ await Factory.build(
+ 'post',
+ {},
+ {
+ categoryIds: ['cat1'],
+ author: bobDerBaumeister,
+ image: Factory.build('image', {
+ url: faker.image.unsplash.people(),
+ }),
+ },
+ )
+ }
- await Promise.all(
- [...Array(5).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- author: louie,
- postId: 'p13',
- },
- ),
- ),
- )
+ // Comments p2 bob
+ for (let i = 0; i < 2; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ author: bobDerBaumeister,
+ postId: 'p2',
+ },
+ )
+ }
- await Promise.all(
- [...Array(45).keys()].map(() =>
- Factory.build(
- 'post',
- {},
- {
- categoryIds: ['cat1'],
- author: bobDerBaumeister,
- image: Factory.build('image', {
- url: faker.image.unsplash.people(),
- }),
- },
- ),
- ),
- )
+ // Comments p12 bob
+ for (let i = 0; i < 3; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ author: bobDerBaumeister,
+ postId: 'p12',
+ },
+ )
+ }
- await Promise.all(
- [...Array(2).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- author: bobDerBaumeister,
- postId: 'p2',
- },
- ),
- ),
- )
+ // Comments p13 bob
+ for (let i = 0; i < 7; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ author: bobDerBaumeister,
+ postId: 'p13',
+ },
+ )
+ }
- await Promise.all(
- [...Array(3).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- author: bobDerBaumeister,
- postId: 'p12',
- },
- ),
- ),
- )
+ // Posts huey
+ for (let i = 0; i < 8; i++) {
+ await Factory.build(
+ 'post',
+ {},
+ {
+ categoryIds: ['cat1'],
+ author: huey,
+ image: Factory.build('image', {
+ url: faker.image.unsplash.nature(),
+ }),
+ },
+ )
+ }
- await Promise.all(
- [...Array(7).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- author: bobDerBaumeister,
- postId: 'p13',
- },
- ),
- ),
- )
+ // Comments p0 huey
+ for (let i = 0; i < 6; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ author: huey,
+ postId: 'p0',
+ },
+ )
+ }
- await Promise.all(
- [...Array(8).keys()].map(() =>
- Factory.build(
- 'post',
- {},
- {
- categoryIds: ['cat1'],
- author: huey,
- image: Factory.build('image', {
- url: faker.image.unsplash.nature(),
- }),
- },
- ),
- ),
- )
+ // Comments p13 huey
+ for (let i = 0; i < 8; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ author: huey,
+ postId: 'p13',
+ },
+ )
+ }
- await Promise.all(
- [...Array(6).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- author: huey,
- postId: 'p0',
- },
- ),
- ),
- )
-
- await Promise.all(
- [...Array(8).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- author: huey,
- postId: 'p13',
- },
- ),
- ),
- )
-
- await Promise.all(
- [...Array(8).keys()].map(() =>
- Factory.build(
- 'comment',
- {},
- {
- author: huey,
- postId: 'p15',
- },
- ),
- ),
- )
+ // Comments p15 huey
+ for (let i = 0; i < 8; i++) {
+ await Factory.build(
+ 'comment',
+ {},
+ {
+ author: huey,
+ postId: 'p15',
+ },
+ )
+ }
await Factory.build('donations')
diff --git a/backend/src/graphql/messages.ts b/backend/src/graphql/messages.ts
index fde45083b..2842c7230 100644
--- a/backend/src/graphql/messages.ts
+++ b/backend/src/graphql/messages.ts
@@ -27,6 +27,9 @@ export const messageQuery = () => {
indexId
content
senderId
+ author {
+ id
+ }
username
avatar
date
diff --git a/backend/src/graphql/rooms.ts b/backend/src/graphql/rooms.ts
index 294b50641..7612641f3 100644
--- a/backend/src/graphql/rooms.ts
+++ b/backend/src/graphql/rooms.ts
@@ -9,6 +9,7 @@ export const createRoomMutation = () => {
roomName
lastMessageAt
unreadCount
+ #avatar
users {
_id
id
@@ -25,10 +26,11 @@ export const createRoomMutation = () => {
export const roomQuery = () => {
return gql`
query Room($first: Int, $offset: Int, $id: ID) {
- Room(first: $first, offset: $offset, id: $id, orderBy: createdAt_desc) {
+ Room(first: $first, offset: $offset, id: $id, orderBy: lastMessageAt_desc) {
id
roomId
roomName
+ avatar
lastMessageAt
unreadCount
lastMessage {
diff --git a/backend/src/helpers/walkRecursive.ts b/backend/src/helpers/walkRecursive.ts
index f560cf9cb..f3be67575 100644
--- a/backend/src/helpers/walkRecursive.ts
+++ b/backend/src/helpers/walkRecursive.ts
@@ -9,10 +9,9 @@ function walkRecursive(data, fields, fieldName, callback, _key?) {
if (!Array.isArray(fields)) {
throw new Error('please provide an fields array for the walkRecursive helper')
}
- if (data && typeof data === 'string' && fields.includes(_key)) {
- // well we found what we searched for, lets replace the value with our callback result
- const key = _key.split('!')
- if (key.length === 1 || key[1] !== fieldName) data = callback(data, key[0])
+ const fieldDef = fields.find((f) => f.field === _key)
+ if (data && typeof data === 'string' && fieldDef) {
+ if (!fieldDef.excludes?.includes(fieldName)) data = callback(data, _key)
} else if (data && Array.isArray(data)) {
// go into the rabbit hole and dig through that array
data.forEach((res, index) => {
diff --git a/backend/src/middleware/chatMiddleware.ts b/backend/src/middleware/chatMiddleware.ts
index c28d6a70d..8ae252e13 100644
--- a/backend/src/middleware/chatMiddleware.ts
+++ b/backend/src/middleware/chatMiddleware.ts
@@ -54,4 +54,7 @@ export default {
Mutation: {
CreateRoom: roomProperties,
},
+ Subscription: {
+ chatMessageAdded: messageProperties,
+ },
}
diff --git a/backend/src/middleware/helpers/cleanHtml.ts b/backend/src/middleware/helpers/cleanHtml.ts
index ac71f6bdc..84497760d 100644
--- a/backend/src/middleware/helpers/cleanHtml.ts
+++ b/backend/src/middleware/helpers/cleanHtml.ts
@@ -30,6 +30,7 @@ const standardSanitizeHtmlOptions = {
'strike',
'span',
'blockquote',
+ 'usertag',
],
allowedAttributes: {
a: ['href', 'class', 'target', 'data-*', 'contenteditable'],
diff --git a/backend/src/middleware/xssMiddleware.ts b/backend/src/middleware/xssMiddleware.ts
index ede0cc199..c10997e8d 100644
--- a/backend/src/middleware/xssMiddleware.ts
+++ b/backend/src/middleware/xssMiddleware.ts
@@ -3,11 +3,11 @@ import { cleanHtml } from '../middleware/helpers/cleanHtml'
// exclamation mark separetes field names, that should not be sanitized
const fields = [
- 'content',
- 'contentExcerpt',
- 'reasonDescription',
- 'description!embed',
- 'descriptionExcerpt',
+ { field: 'content', excludes: ['CreateMessage', 'Message'] },
+ { field: 'contentExcerpt' },
+ { field: 'reasonDescription' },
+ { field: 'description', excludes: ['embed'] },
+ { field: 'descriptionExcerpt' },
]
export default {
diff --git a/backend/src/schema/resolvers/messages.spec.ts b/backend/src/schema/resolvers/messages.spec.ts
index 1679b0c34..83d9fdc6b 100644
--- a/backend/src/schema/resolvers/messages.spec.ts
+++ b/backend/src/schema/resolvers/messages.spec.ts
@@ -117,7 +117,7 @@ describe('Message', () => {
})
describe('user chats in room', () => {
- it('returns the message and publishes subscription', async () => {
+ it('returns the message and publishes subscriptions', async () => {
await expect(
mutate({
mutation: createMessageMutation(),
@@ -146,6 +146,20 @@ describe('Message', () => {
roomCountUpdated: '1',
userId: 'other-chatting-user',
})
+ expect(pubsubSpy).toBeCalledWith('CHAT_MESSAGE_ADDED', {
+ chatMessageAdded: expect.objectContaining({
+ id: expect.any(String),
+ content: 'Some nice message to other chatting user',
+ senderId: 'chatting-user',
+ username: 'Chatting User',
+ avatar: expect.any(String),
+ date: expect.any(String),
+ saved: true,
+ distributed: false,
+ seen: false,
+ }),
+ userId: 'other-chatting-user',
+ })
})
describe('room is updated as well', () => {
diff --git a/backend/src/schema/resolvers/messages.ts b/backend/src/schema/resolvers/messages.ts
index a908f3fd8..b7e7a7a73 100644
--- a/backend/src/schema/resolvers/messages.ts
+++ b/backend/src/schema/resolvers/messages.ts
@@ -1,7 +1,9 @@
import { neo4jgraphql } from 'neo4j-graphql-js'
import Resolver from './helpers/Resolver'
+
import { getUnreadRoomsCount } from './rooms'
-import { pubsub, ROOM_COUNT_UPDATED } from '../../server'
+import { pubsub, ROOM_COUNT_UPDATED, CHAT_MESSAGE_ADDED } from '../../server'
+import { withFilter } from 'graphql-subscriptions'
const setMessagesAsDistributed = async (undistributedMessagesIds, session) => {
return session.writeTransaction(async (transaction) => {
@@ -19,6 +21,16 @@ const setMessagesAsDistributed = async (undistributedMessagesIds, session) => {
}
export default {
+ Subscription: {
+ chatMessageAdded: {
+ subscribe: withFilter(
+ () => pubsub.asyncIterator(CHAT_MESSAGE_ADDED),
+ (payload, variables) => {
+ return payload.userId === variables.userId
+ },
+ ),
+ },
+ },
Query: {
Message: async (object, params, context, resolveInfo) => {
const { roomId } = params
@@ -69,7 +81,7 @@ export default {
createdAt: toString(datetime()),
id: apoc.create.uuid(),
indexId: CASE WHEN maxIndex IS NOT NULL THEN maxIndex + 1 ELSE 0 END,
- content: $content,
+ content: LEFT($content,2000),
saved: true,
distributed: false,
seen: false
@@ -77,6 +89,7 @@ export default {
SET room.lastMessageAt = toString(datetime())
RETURN message {
.*,
+ indexId: toString(message.indexId),
recipientId: recipientUser.id,
senderId: currentUser.id,
username: currentUser.name,
@@ -102,10 +115,14 @@ export default {
const roomCountUpdated = await getUnreadRoomsCount(message.recipientId, session)
// send subscriptions
- await pubsub.publish(ROOM_COUNT_UPDATED, {
+ void pubsub.publish(ROOM_COUNT_UPDATED, {
roomCountUpdated,
userId: message.recipientId,
})
+ void pubsub.publish(CHAT_MESSAGE_ADDED, {
+ chatMessageAdded: message,
+ userId: message.recipientId,
+ })
}
return message
diff --git a/backend/src/schema/resolvers/rooms.spec.ts b/backend/src/schema/resolvers/rooms.spec.ts
index ee291a6c9..2e26dc1e3 100644
--- a/backend/src/schema/resolvers/rooms.spec.ts
+++ b/backend/src/schema/resolvers/rooms.spec.ts
@@ -423,125 +423,147 @@ describe('Room', () => {
})
it('returns the rooms paginated', async () => {
- expect(await query({ query: roomQuery(), variables: { first: 3, offset: 0 } })).toMatchObject(
- {
- errors: undefined,
- data: {
- Room: [
- {
+ await expect(
+ query({ query: roomQuery(), variables: { first: 3, offset: 0 } }),
+ ).resolves.toMatchObject({
+ errors: undefined,
+ data: {
+ Room: expect.arrayContaining([
+ expect.objectContaining({
+ id: expect.any(String),
+ roomId: expect.any(String),
+ roomName: 'Third Chatting User',
+ lastMessageAt: null,
+ unreadCount: 0,
+ lastMessage: null,
+ users: expect.arrayContaining([
+ expect.objectContaining({
+ _id: 'chatting-user',
+ id: 'chatting-user',
+ name: 'Chatting User',
+ avatar: {
+ url: expect.any(String),
+ },
+ }),
+ expect.objectContaining({
+ _id: 'third-chatting-user',
+ id: 'third-chatting-user',
+ name: 'Third Chatting User',
+ avatar: {
+ url: expect.any(String),
+ },
+ }),
+ ]),
+ }),
+ expect.objectContaining({
+ id: expect.any(String),
+ roomId: expect.any(String),
+ roomName: 'Second Chatting User',
+ lastMessageAt: null,
+ unreadCount: 0,
+ lastMessage: null,
+ users: expect.arrayContaining([
+ expect.objectContaining({
+ _id: 'chatting-user',
+ id: 'chatting-user',
+ name: 'Chatting User',
+ avatar: {
+ url: expect.any(String),
+ },
+ }),
+ expect.objectContaining({
+ _id: 'second-chatting-user',
+ id: 'second-chatting-user',
+ name: 'Second Chatting User',
+ avatar: {
+ url: expect.any(String),
+ },
+ }),
+ ]),
+ }),
+ expect.objectContaining({
+ id: expect.any(String),
+ roomId: expect.any(String),
+ roomName: 'Other Chatting User',
+ lastMessageAt: expect.any(String),
+ unreadCount: 0,
+ lastMessage: {
+ _id: expect.any(String),
id: expect.any(String),
- roomId: expect.any(String),
- roomName: 'Third Chatting User',
- users: expect.arrayContaining([
- {
- _id: 'chatting-user',
- id: 'chatting-user',
- name: 'Chatting User',
- avatar: {
- url: expect.any(String),
- },
- },
- {
- _id: 'third-chatting-user',
- id: 'third-chatting-user',
- name: 'Third Chatting User',
- avatar: {
- url: expect.any(String),
- },
- },
- ]),
+ content: '2nd message to other chatting user',
+ senderId: 'chatting-user',
+ username: 'Chatting User',
+ avatar: expect.any(String),
+ date: expect.any(String),
+ saved: true,
+ distributed: false,
+ seen: false,
},
- {
- id: expect.any(String),
- roomId: expect.any(String),
- roomName: 'Second Chatting User',
- users: expect.arrayContaining([
- {
- _id: 'chatting-user',
- id: 'chatting-user',
- name: 'Chatting User',
- avatar: {
- url: expect.any(String),
- },
+ users: expect.arrayContaining([
+ expect.objectContaining({
+ _id: 'chatting-user',
+ id: 'chatting-user',
+ name: 'Chatting User',
+ avatar: {
+ url: expect.any(String),
},
- {
- _id: 'second-chatting-user',
- id: 'second-chatting-user',
- name: 'Second Chatting User',
- avatar: {
- url: expect.any(String),
- },
+ }),
+ expect.objectContaining({
+ _id: 'other-chatting-user',
+ id: 'other-chatting-user',
+ name: 'Other Chatting User',
+ avatar: {
+ url: expect.any(String),
},
- ]),
- },
- {
- id: expect.any(String),
- roomId: expect.any(String),
- roomName: 'Not Chatting User',
- users: expect.arrayContaining([
- {
- _id: 'chatting-user',
- id: 'chatting-user',
- name: 'Chatting User',
- avatar: {
- url: expect.any(String),
- },
- },
- {
- _id: 'not-chatting-user',
- id: 'not-chatting-user',
- name: 'Not Chatting User',
- avatar: {
- url: expect.any(String),
- },
- },
- ]),
- },
- ],
- },
+ }),
+ ]),
+ }),
+ ]),
},
- )
- expect(await query({ query: roomQuery(), variables: { first: 3, offset: 3 } })).toMatchObject(
- {
- errors: undefined,
- data: {
- Room: [
- {
- id: expect.any(String),
- roomId: expect.any(String),
- roomName: 'Other Chatting User',
- users: expect.arrayContaining([
- {
- _id: 'chatting-user',
- id: 'chatting-user',
- name: 'Chatting User',
- avatar: {
- url: expect.any(String),
- },
+ })
+ await expect(
+ query({ query: roomQuery(), variables: { first: 3, offset: 3 } }),
+ ).resolves.toMatchObject({
+ errors: undefined,
+ data: {
+ Room: [
+ expect.objectContaining({
+ id: expect.any(String),
+ roomId: expect.any(String),
+ roomName: 'Not Chatting User',
+ users: expect.arrayContaining([
+ {
+ _id: 'chatting-user',
+ id: 'chatting-user',
+ name: 'Chatting User',
+ avatar: {
+ url: expect.any(String),
},
- {
- _id: 'other-chatting-user',
- id: 'other-chatting-user',
- name: 'Other Chatting User',
- avatar: {
- url: expect.any(String),
- },
+ },
+ {
+ _id: 'not-chatting-user',
+ id: 'not-chatting-user',
+ name: 'Not Chatting User',
+ avatar: {
+ url: expect.any(String),
},
- ]),
- },
- ],
- },
+ },
+ ]),
+ }),
+ ],
},
- )
+ })
})
})
describe('query single room', () => {
let result: any = null
+
beforeAll(async () => {
authenticatedUser = await chattingUser.toJson()
result = await query({ query: roomQuery() })
})
+
describe('as chatter of room', () => {
it('returns the room', async () => {
expect(
@@ -556,34 +578,19 @@ describe('Room', () => {
{
id: expect.any(String),
roomId: expect.any(String),
- roomName: 'Third Chatting User',
- users: expect.arrayContaining([
- {
- _id: 'chatting-user',
- id: 'chatting-user',
- name: 'Chatting User',
- avatar: {
- url: expect.any(String),
- },
- },
- {
- _id: 'third-chatting-user',
- id: 'third-chatting-user',
- name: 'Third Chatting User',
- avatar: {
- url: expect.any(String),
- },
- },
- ]),
+ roomName: result.data.Room[0].roomName,
+ users: expect.any(Array),
},
],
},
})
})
+
describe('as not chatter of room', () => {
beforeAll(async () => {
authenticatedUser = await notChattingUser.toJson()
})
+
it('returns no room', async () => {
authenticatedUser = await notChattingUser.toJson()
expect(
diff --git a/backend/src/schema/types/type/Message.gql b/backend/src/schema/types/type/Message.gql
index 764181dd9..71d175e1c 100644
--- a/backend/src/schema/types/type/Message.gql
+++ b/backend/src/schema/types/type/Message.gql
@@ -44,3 +44,7 @@ type Query {
orderBy: [_MessageOrdering]
): [Message]
}
+
+type Subscription {
+ chatMessageAdded(userId: ID!): Message
+}
diff --git a/backend/src/schema/types/type/Room.gql b/backend/src/schema/types/type/Room.gql
index fdce6865b..d758808bb 100644
--- a/backend/src/schema/types/type/Room.gql
+++ b/backend/src/schema/types/type/Room.gql
@@ -7,6 +7,7 @@
# TODO change this to last message date
enum _RoomOrdering {
+ lastMessageAt_desc
createdAt_desc
}
diff --git a/backend/src/server.ts b/backend/src/server.ts
index feceeb9eb..0522f5fc8 100644
--- a/backend/src/server.ts
+++ b/backend/src/server.ts
@@ -14,7 +14,7 @@ import bodyParser from 'body-parser'
import { graphqlUploadExpress } from 'graphql-upload'
export const NOTIFICATION_ADDED = 'NOTIFICATION_ADDED'
-// export const CHAT_MESSAGE_ADDED = 'CHAT_MESSAGE_ADDED'
+export const CHAT_MESSAGE_ADDED = 'CHAT_MESSAGE_ADDED'
export const ROOM_COUNT_UPDATED = 'ROOM_COUNT_UPDATED'
const { REDIS_DOMAIN, REDIS_PORT, REDIS_PASSWORD } = CONFIG
let prodPubsub, devPubsub
diff --git a/webapp/assets/_new/styles/export.scss b/webapp/assets/_new/styles/export.scss
index 88b42bfc9..5b866d6b7 100644
--- a/webapp/assets/_new/styles/export.scss
+++ b/webapp/assets/_new/styles/export.scss
@@ -27,4 +27,8 @@
chatMessageBgOthers: $chat-message-bg-others;
chatNewMessageColor: $chat-new-message-color;
+
+ chatMessageTimestamp: $chat-message-timestamp;
+ chatMessageCheckmarkSeen: $chat-message-checkmark-seen;
+ chatMessageCheckmark: $chat-message-checkmark;
}
\ No newline at end of file
diff --git a/webapp/assets/_new/styles/tokens.scss b/webapp/assets/_new/styles/tokens.scss
index e001ffa84..ef5086240 100644
--- a/webapp/assets/_new/styles/tokens.scss
+++ b/webapp/assets/_new/styles/tokens.scss
@@ -417,3 +417,6 @@ $chat-message-color: $text-color-base;
$chat-message-bg-others: $color-neutral-80;
$chat-sidemenu-bg: $color-secondary-active;
$chat-new-message-color: $color-secondary-active;
+$chat-message-timestamp: $text-color-soft;
+$chat-message-checkmark-seen: $text-color-secondary;
+$chat-message-checkmark: $text-color-soft;
diff --git a/webapp/components/Chat/AddChatRoomByUserSearch.vue b/webapp/components/Chat/AddChatRoomByUserSearch.vue
new file mode 100644
index 000000000..b48120603
--- /dev/null
+++ b/webapp/components/Chat/AddChatRoomByUserSearch.vue
@@ -0,0 +1,67 @@
+
+
+
+ {{ $t('chat.addRoomHeadline') }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue
index 43994ef5d..c18c12a58 100644
--- a/webapp/components/Chat/Chat.vue
+++ b/webapp/components/Chat/Chat.vue
@@ -4,7 +4,7 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -46,7 +65,7 @@
@@ -61,7 +80,12 @@
@@ -353,4 +476,8 @@ body {
transform: translate(-50%, -50%);
}
}
+
+.ds-flex-item.single-chat-bubble {
+ margin-right: 1em;
+}
diff --git a/webapp/components/CommentForm/CommentForm.vue b/webapp/components/CommentForm/CommentForm.vue
index 5f6a2420d..6d9b59de6 100644
--- a/webapp/components/CommentForm/CommentForm.vue
+++ b/webapp/components/CommentForm/CommentForm.vue
@@ -8,7 +8,6 @@
:disabled="disabled && !update"
@click="handleCancel"
data-test="cancel-button"
- danger
>
{{ $t('actions.cancel') }}
diff --git a/webapp/components/ContentMenu/GroupContentMenu.vue b/webapp/components/ContentMenu/GroupContentMenu.vue
index 7a7737320..1ca1b5b33 100644
--- a/webapp/components/ContentMenu/GroupContentMenu.vue
+++ b/webapp/components/ContentMenu/GroupContentMenu.vue
@@ -58,14 +58,14 @@ export default {
routes.push({
label: this.$t('group.contentMenu.visitGroupPage'),
icon: 'home',
- name: 'group-id-slug',
+ path: `/groups/${this.group.id}`,
params: { id: this.group.id, slug: this.group.slug },
})
}
if (this.group.myRole === 'owner') {
routes.push({
label: this.$t('admin.settings.name'),
- path: `/group/edit/${this.group.id}`,
+ path: `/groups/edit/${this.group.id}`,
icon: 'edit',
})
}
diff --git a/webapp/components/ContributionForm/ContributionForm.vue b/webapp/components/ContributionForm/ContributionForm.vue
index 0067dab72..b595357f9 100644
--- a/webapp/components/ContributionForm/ContributionForm.vue
+++ b/webapp/components/ContributionForm/ContributionForm.vue
@@ -169,12 +169,7 @@
-
+
{{ $t('actions.cancel') }}
diff --git a/webapp/components/Group/AddGroupMember.vue b/webapp/components/Group/AddGroupMember.vue
index ff049ca4d..cc3e3b1e8 100644
--- a/webapp/components/Group/AddGroupMember.vue
+++ b/webapp/components/Group/AddGroupMember.vue
@@ -3,33 +3,7 @@
{{ $t('group.addUser') }}
-
-
-
-
-
-
-
+
+
+