mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Merge branch 'event-master' into seed-posts-as-articles
This commit is contained in:
commit
5065265dc6
9
.github/file-filters.yml
vendored
Normal file
9
.github/file-filters.yml
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
backend: &backend
|
||||
- 'backend/**/*'
|
||||
- 'neo4j/**/*'
|
||||
|
||||
docker: &docker
|
||||
- 'docker-compose.*'
|
||||
|
||||
webapp: &webapp
|
||||
- 'webapp/**/*'
|
||||
8
.github/workflows/publish.yml
vendored
8
.github/workflows/publish.yml
vendored
@ -306,4 +306,12 @@ jobs:
|
||||
token: ${{ secrets.OCELOT_PUBLISH_EVENT_PAT }} # this token is required to access the other repository
|
||||
event-type: trigger-ocelot-build-success
|
||||
repository: 'Ocelot-Social-Community/stage.ocelot.social'
|
||||
client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "GITHUB_RUN_NUMBER": "${{ env.GITHUB_RUN_NUMBER }}", "VERSION": "${VERSION}", "BUILD_DATE": "${BUILD_DATE}", "BUILD_COMMIT": "${BUILD_COMMIT}", "BUILD_VERSION": "${BUILD_VERSION}"}'
|
||||
|
||||
- name: Repository Dispatch stage.yunite.me
|
||||
uses: peter-evans/repository-dispatch@v2
|
||||
with:
|
||||
token: ${{ secrets.OCELOT_PUBLISH_EVENT_PAT }} # this token is required to access the other repository
|
||||
event-type: trigger-ocelot-build-success
|
||||
repository: 'Yunite-Net/stage.yunite.me'
|
||||
client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "GITHUB_RUN_NUMBER": "${{ env.GITHUB_RUN_NUMBER }}", "VERSION": "${VERSION}", "BUILD_DATE": "${BUILD_DATE}", "BUILD_COMMIT": "${BUILD_COMMIT}", "BUILD_VERSION": "${BUILD_VERSION}"}'
|
||||
120
.github/workflows/test-backend.yml
vendored
Normal file
120
.github/workflows/test-backend.yml
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
name: ocelot.social backend test CI
|
||||
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Detect File Changes - Backend
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
backend: ${{ steps.changes.outputs.backend }}
|
||||
docker: ${{ steps.changes.outputs.docker }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3.3.0
|
||||
|
||||
- name: Check for frontend file changes
|
||||
uses: dorny/paths-filter@v2.11.1
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/file-filters.yml
|
||||
list-files: shell
|
||||
|
||||
build_test_neo4j:
|
||||
name: Docker Build Test - Neo4J
|
||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.docker == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Neo4J | Build 'community' image
|
||||
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
|
||||
with:
|
||||
name: docker-neo4j-image
|
||||
path: /tmp/neo4j.tar
|
||||
|
||||
build_test_backend:
|
||||
name: Docker Build Test - Backend
|
||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.docker == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: backend | Build 'test' image
|
||||
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
|
||||
with:
|
||||
name: docker-backend-test
|
||||
path: /tmp/backend.tar
|
||||
|
||||
lint_backend:
|
||||
name: Lint Backend
|
||||
if: needs.files-changed.outputs.backend == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: backend | Lint
|
||||
run: cd backend && yarn && yarn run lint
|
||||
|
||||
unit_test_backend:
|
||||
name: Unit tests - Backend
|
||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.docker == 'true'
|
||||
needs: [files-changed, build_test_neo4j, build_test_backend]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
checks: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download Docker Image (Neo4J)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: docker-neo4j-image
|
||||
path: /tmp
|
||||
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/neo4j.tar
|
||||
|
||||
- name: Download Docker Image (Backend)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: docker-backend-test
|
||||
path: /tmp
|
||||
|
||||
- name: Load Docker Image
|
||||
run: 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 | docker-compose
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps neo4j backend
|
||||
|
||||
- name: backend | Initialize Database
|
||||
run: docker-compose exec -T backend yarn db:migrate init
|
||||
|
||||
- name: backend | Migrate Database Up
|
||||
run: docker-compose exec -T backend yarn db:migrate up
|
||||
|
||||
- name: backend | Unit test incl. coverage check
|
||||
run: docker-compose exec -T backend yarn test
|
||||
41
.github/workflows/test-e2e.yml
vendored
Normal file
41
.github/workflows/test-e2e.yml
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
name: ocelot.social end-to-end test CI
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
fullstack_tests:
|
||||
name: Fullstack tests
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
jobs: 8
|
||||
strategy:
|
||||
matrix:
|
||||
# run copies of the current job in parallel
|
||||
job: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: webapp | copy env file
|
||||
run: cp webapp/.env.template webapp/.env
|
||||
|
||||
- name: backend | copy env file
|
||||
run: cp backend/.env.template backend/.env
|
||||
|
||||
- name: boot up test system | docker-compose
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp neo4j backend
|
||||
|
||||
- name: cypress | Fullstack tests
|
||||
id: e2e-tests
|
||||
run: |
|
||||
yarn install
|
||||
yarn run cypress:run --spec $(cypress/parallel-features.sh ${{ matrix.job }} ${{ env.jobs }} )
|
||||
|
||||
##########################################################################
|
||||
# UPLOAD SCREENSHOTS - IF TESTS FAIL #####################################
|
||||
##########################################################################
|
||||
- name: Full stack tests | if any test failed, upload screenshots
|
||||
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: cypress-screenshots
|
||||
path: cypress/screenshots/
|
||||
101
.github/workflows/test-webapp.yml
vendored
Normal file
101
.github/workflows/test-webapp.yml
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
name: ocelot.social webapp test CI
|
||||
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Detect File Changes - Webapp
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
docker: ${{ steps.changes.outputs.docker }}
|
||||
webapp: ${{ steps.changes.outputs.webapp }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3.3.0
|
||||
|
||||
- name: Check for frontend file changes
|
||||
uses: dorny/paths-filter@v2.11.1
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/file-filters.yml
|
||||
list-files: shell
|
||||
|
||||
prepare:
|
||||
name: Prepare
|
||||
if: needs.files-changed.outputs.webapp
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Check translation files
|
||||
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
|
||||
needs: [files-changed, prepare]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- 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
|
||||
with:
|
||||
name: docker-webapp-test
|
||||
path: /tmp/webapp.tar
|
||||
|
||||
lint_webapp:
|
||||
name: Lint Webapp
|
||||
if: needs.files-changed.outputs.webapp
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: webapp | Lint
|
||||
run: cd webapp && yarn && yarn run lint
|
||||
|
||||
unit_test_webapp:
|
||||
name: Unit Tests - Webapp
|
||||
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.webapp
|
||||
needs: [files-changed, build_test_webapp]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
checks: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download Docker Image (Webapp)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: docker-webapp-test
|
||||
path: /tmp
|
||||
|
||||
- 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: backend | docker-compose
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp
|
||||
|
||||
- name: webapp | Unit tests incl. coverage check
|
||||
run: docker-compose exec -T webapp yarn test
|
||||
|
||||
344
.github/workflows/test.yml
vendored
344
.github/workflows/test.yml
vendored
@ -1,344 +0,0 @@
|
||||
name: ocelot.social test CI
|
||||
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
##############################################################################
|
||||
# JOB: PREPARE #####################################################
|
||||
##############################################################################
|
||||
prepare:
|
||||
name: Prepare
|
||||
runs-on: ubuntu-latest
|
||||
# needs: [nothing]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
##########################################################################
|
||||
# TODO: DO STUFF ??? #####################################################
|
||||
##########################################################################
|
||||
- name: Check translation files
|
||||
run: |
|
||||
scripts/translations/sort.sh
|
||||
scripts/translations/missing-keys.sh
|
||||
|
||||
##############################################################################
|
||||
# JOB: DOCKER BUILD TEST NEO4J ###############################################
|
||||
##############################################################################
|
||||
build_test_neo4j:
|
||||
name: Docker Build Test - Neo4J
|
||||
runs-on: ubuntu-latest
|
||||
needs: [prepare]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
##########################################################################
|
||||
# NEO4J ##################################################################
|
||||
##########################################################################
|
||||
- name: Neo4J | Build `community` image
|
||||
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
|
||||
with:
|
||||
name: docker-neo4j-image
|
||||
path: /tmp/neo4j.tar
|
||||
|
||||
##############################################################################
|
||||
# JOB: DOCKER BUILD TEST BACKEND #############################################
|
||||
##############################################################################
|
||||
build_test_backend:
|
||||
name: Docker Build Test - Backend
|
||||
runs-on: ubuntu-latest
|
||||
needs: [prepare]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
##########################################################################
|
||||
# BUILD BACKEND DOCKER IMAGE (build) #####################################
|
||||
##########################################################################
|
||||
- name: backend | Build `test` image
|
||||
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
|
||||
with:
|
||||
name: docker-backend-test
|
||||
path: /tmp/backend.tar
|
||||
|
||||
##############################################################################
|
||||
# JOB: DOCKER BUILD TEST WEBAPP ##############################################
|
||||
##############################################################################
|
||||
build_test_webapp:
|
||||
name: Docker Build Test - WebApp
|
||||
runs-on: ubuntu-latest
|
||||
needs: [prepare]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
##########################################################################
|
||||
# BUILD WEBAPP DOCKER IMAGE (build) ######################################
|
||||
##########################################################################
|
||||
- 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
|
||||
with:
|
||||
name: docker-webapp-test
|
||||
path: /tmp/webapp.tar
|
||||
|
||||
##############################################################################
|
||||
# JOB: LINT BACKEND ##########################################################
|
||||
##############################################################################
|
||||
lint_backend:
|
||||
name: Lint backend
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_test_backend]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
##########################################################################
|
||||
# DOWNLOAD DOCKER IMAGE ##################################################
|
||||
##########################################################################
|
||||
- name: Download Docker Image (Backend)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: docker-backend-test
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/backend.tar
|
||||
##########################################################################
|
||||
# LINT BACKEND ###########################################################
|
||||
##########################################################################
|
||||
- name: backend | Lint
|
||||
run: docker run --rm ocelotsocialnetwork/backend:test yarn run lint
|
||||
|
||||
##############################################################################
|
||||
# JOB: LINT WEBAPP ###########################################################
|
||||
##############################################################################
|
||||
lint_webapp:
|
||||
name: Lint webapp
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_test_webapp]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
##########################################################################
|
||||
# DOWNLOAD DOCKER IMAGE ##################################################
|
||||
##########################################################################
|
||||
- name: Download Docker Image (Webapp)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: docker-webapp-test
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/webapp.tar
|
||||
##########################################################################
|
||||
# LINT WEBAPP ############################################################
|
||||
##########################################################################
|
||||
- name: webapp | Lint
|
||||
run: docker run --rm ocelotsocialnetwork/webapp:test yarn run lint
|
||||
|
||||
##############################################################################
|
||||
# JOB: UNIT TEST BACKEND #####################################################
|
||||
##############################################################################
|
||||
unit_test_backend:
|
||||
name: Unit tests - backend
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_test_neo4j,build_test_backend]
|
||||
permissions:
|
||||
checks: write
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
##########################################################################
|
||||
# DOWNLOAD DOCKER IMAGES #################################################
|
||||
##########################################################################
|
||||
- name: Download Docker Image (Neo4J)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: docker-neo4j-image
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/neo4j.tar
|
||||
- name: Download Docker Image (Backend)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: docker-backend-test
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/backend.tar
|
||||
##########################################################################
|
||||
# UNIT TESTS BACKEND #####################################################
|
||||
##########################################################################
|
||||
- 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 | docker-compose
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps neo4j backend
|
||||
- name: backend | Initialize Database
|
||||
run: docker-compose exec -T backend yarn db:migrate init
|
||||
- name: backend | Migrate Database Up
|
||||
run: docker-compose exec -T backend yarn db:migrate up
|
||||
- name: backend | Unit test
|
||||
run: docker-compose exec -T backend yarn test
|
||||
##########################################################################
|
||||
# COVERAGE CHECK BACKEND #################################################
|
||||
##########################################################################
|
||||
- name: backend | Coverage check
|
||||
uses: webcraftmedia/coverage-check-action@master
|
||||
with:
|
||||
report_name: Coverage Backend
|
||||
type: lcov
|
||||
result_path: ./coverage/lcov.info
|
||||
min_coverage: 57
|
||||
token: ${{ github.token }}
|
||||
|
||||
##############################################################################
|
||||
# JOB: UNIT TEST WEBAPP ######################################################
|
||||
##############################################################################
|
||||
unit_test_webapp:
|
||||
name: Unit tests - webapp
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_test_webapp]
|
||||
permissions:
|
||||
checks: write
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
##########################################################################
|
||||
# DOWNLOAD DOCKER IMAGES #################################################
|
||||
##########################################################################
|
||||
- name: Download Docker Image (Webapp)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: docker-webapp-test
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/webapp.tar
|
||||
##########################################################################
|
||||
# UNIT TESTS WEBAPP ######################################################
|
||||
##########################################################################
|
||||
- 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 | docker-compose
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp
|
||||
- name: webapp | Unit tests
|
||||
run: docker-compose exec -T webapp yarn test
|
||||
##########################################################################
|
||||
# COVERAGE REPORT FRONTEND ################################################
|
||||
##########################################################################
|
||||
#- name: frontend | Coverage report
|
||||
# uses: romeovs/lcov-reporter-action@v0.2.21
|
||||
# with:
|
||||
# github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# lcov-file: ./coverage/lcov.info
|
||||
##########################################################################
|
||||
# COVERAGE CHECK WEBAPP ##################################################
|
||||
##########################################################################
|
||||
- name: webapp | Coverage check
|
||||
uses: webcraftmedia/coverage-check-action@master
|
||||
with:
|
||||
report_name: Coverage Webapp
|
||||
type: lcov
|
||||
result_path: ./coverage/lcov.info
|
||||
min_coverage: 83
|
||||
token: ${{ github.token }}
|
||||
|
||||
##############################################################################
|
||||
# JOB: FULLSTACK TESTS #######################################################
|
||||
##############################################################################
|
||||
fullstack_tests:
|
||||
name: Fullstack tests
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_test_webapp, build_test_backend, build_test_neo4j]
|
||||
env:
|
||||
jobs: 8
|
||||
strategy:
|
||||
matrix:
|
||||
# run copies of the current job in parallel
|
||||
job: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
##########################################################################
|
||||
# DOWNLOAD DOCKER IMAGES #################################################
|
||||
##########################################################################
|
||||
- name: Download Docker Image (Neo4J)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: docker-neo4j-image
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/neo4j.tar
|
||||
- name: Download Docker Image (Backend)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: docker-backend-test
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/backend.tar
|
||||
- name: Download Docker Image (Webapp)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: docker-webapp-test
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/webapp.tar
|
||||
##########################################################################
|
||||
# FULLSTACK TESTS CYPRESS ################################################
|
||||
##########################################################################
|
||||
- name: webapp | 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 | docker-compose
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp neo4j backend
|
||||
- name: cypress | Fullstack tests
|
||||
id: e2e-tests
|
||||
run: |
|
||||
yarn install
|
||||
yarn run cypress:run --spec $(cypress/parallel-features.sh ${{ matrix.job }} ${{ env.jobs }} )
|
||||
##########################################################################
|
||||
# UPLOAD SCREENSHOTS - IF TESTS FAIL #####################################
|
||||
##########################################################################
|
||||
- name: Full stack tests | if any test failed, upload screenshots
|
||||
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: cypress-screenshots
|
||||
path: cypress/screenshots/
|
||||
38
CHANGELOG.md
38
CHANGELOG.md
@ -4,13 +4,49 @@ 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).
|
||||
|
||||
#### [2.6.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/2.5.1...2.6.0)
|
||||
|
||||
- fix(other): docker-compose for rebranding deployment [`#6265`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6265)
|
||||
- feat(webapp): default categories of group for posts in group [`#6259`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6259)
|
||||
- refactor(webapp): make action radius select in group form a reusable component [`#6244`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6244)
|
||||
- fix(webapp): show avatar for group members [`#6258`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6258)
|
||||
- fix(webapp): fix search for 3 chars [`#6256`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6256)
|
||||
- fix(backend): group posts cannot be pinned [`#6242`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6242)
|
||||
- Bump metascraper-soundcloud from 5.33.5 to 5.34.2 in /backend [`#6213`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6213)
|
||||
- refactor(other): refactor test workflows [`#6151`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6151)
|
||||
- fix(other): deployment fix typo, update stage.ocelot.social reference [`#6230`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6230)
|
||||
- fix(other): bump metascraper-url from 5.33.5 to 5.34.2 in /backend [`#6217`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6217)
|
||||
- fix(other): workflow typo [`#6235`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6235)
|
||||
- feat(other): publish stage.yunite.me hook [`#6234`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6234)
|
||||
- fix(other): reduce kubernetes memory limits [`#6229`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6229)
|
||||
- fix(other): deployment for branded image with custom names [`#6228`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6228)
|
||||
- Bump node from 19.8.1-alpine3.17 to 19.9.0-alpine3.17 in /backend [`#6219`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6219)
|
||||
- Bump node from 19.8.1-alpine3.17 to 19.9.0-alpine3.17 in /webapp [`#6220`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6220)
|
||||
- feat(other): deployment pod resources [`#6132`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6132)
|
||||
- Bump @babel/core from 7.12.17 to 7.21.4 in /webapp [`#6215`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6215)
|
||||
- Bump jsonwebtoken from 8.5.1 to 9.0.0 in /webapp [`#6079`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6079)
|
||||
- Bump jest from 29.4.2 to 29.5.0 in /webapp [`#6094`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6094)
|
||||
- Bump babel-jest from 29.4.2 to 29.5.0 in /webapp [`#6095`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6095)
|
||||
- Bump neode from 0.4.8 to 0.4.9 in /backend [`#6075`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6075)
|
||||
- Bump node from 19.4.0-alpine3.17 to 19.8.1-alpine3.17 in /webapp [`#6155`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6155)
|
||||
- Bump node from 19.4.0-alpine3.17 to 19.8.1-alpine3.17 in /backend [`#6156`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6156)
|
||||
- Bump @babel/core from 7.9.0 to 7.21.4 [`#6200`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6200)
|
||||
- Bump @babel/preset-env from 7.12.7 to 7.21.4 [`#6204`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6204)
|
||||
- Bump expect from 25.3.0 to 29.5.0 [`#6098`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6098)
|
||||
- separate test workflows [`3533a36`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/3533a36cdc811c0e1dae218fbc2184f7c4bc3951)
|
||||
- get it working [`8df7d5d`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/8df7d5d265b0c5ba16f167a213631d765d2f985e)
|
||||
- feat(webapp): group categories on posts [`3244f3f`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/3244f3f86d1e8c09e0fd49f43c49f0a3aa8b85ab)
|
||||
|
||||
#### [2.5.1](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/2.5.0...2.5.1)
|
||||
|
||||
> 23 March 2023
|
||||
|
||||
- chore(other): release v2.5.1 – fix filter menu width [`#6180`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6180)
|
||||
- feat(webapp): add tooltips to all menu icons [`#6185`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6185)
|
||||
- fix(webapp): popup filter max-width [`#6177`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6177)
|
||||
- Add tooltip to header notifications menu [`28505a5`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/28505a5b181008ebcde6fa58b7a4a8459a492018)
|
||||
- Add tooltip to header avatar menu [`4c0469f`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/4c0469f61a3c2fae23e50c6a5a2a91b63fac149a)
|
||||
- Add tooltip to locale switch menu [`26b90fc`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/26b90fce4704e537126fd15ad950ea04e14890ba)
|
||||
- Release v2.5.1 - fix filter menu width [`08def14`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/08def14cafef7816d8e43f1896430400bda9635d)
|
||||
|
||||
#### [2.5.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/2.4.0...2.5.0)
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
##################################################################################
|
||||
# BASE (Is pushed to DockerHub for rebranding) ###################################
|
||||
##################################################################################
|
||||
FROM node:19.4.0-alpine3.17 as base
|
||||
FROM node:19.9.0-alpine3.17 as base
|
||||
|
||||
# ENVs
|
||||
## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame
|
||||
|
||||
@ -8,7 +8,11 @@ module.exports = {
|
||||
'!**/dist/**',
|
||||
'!**/src/**/?(*.)+(spec|test).js?(x)'
|
||||
],
|
||||
coverageReporters: ['lcov', 'text'],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
lines: 57,
|
||||
},
|
||||
},
|
||||
testMatch: ['**/src/**/?(*.)+(spec|test).js?(x)'],
|
||||
setupFilesAfterEnv: ['<rootDir>/test/setup.js']
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ocelot-social-backend",
|
||||
"version": "2.5.1",
|
||||
"version": "2.6.0",
|
||||
"description": "GraphQL Backend for ocelot.social",
|
||||
"repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social",
|
||||
"author": "ocelot.social Community",
|
||||
@ -58,7 +58,7 @@
|
||||
"graphql-redis-subscriptions": "^2.2.1",
|
||||
"graphql-shield": "~7.2.2",
|
||||
"graphql-tag": "~2.10.3",
|
||||
"helmet": "~3.22.0",
|
||||
"helmet": "~7.0.0",
|
||||
"ioredis": "^4.16.1",
|
||||
"jsonwebtoken": "~8.5.1",
|
||||
"languagedetect": "^2.0.0",
|
||||
@ -76,9 +76,9 @@
|
||||
"metascraper-lang-detector": "^4.10.2",
|
||||
"metascraper-logo": "^5.33.5",
|
||||
"metascraper-publisher": "^5.33.5",
|
||||
"metascraper-soundcloud": "^5.33.5",
|
||||
"metascraper-soundcloud": "^5.34.2",
|
||||
"metascraper-title": "^5.33.5",
|
||||
"metascraper-url": "^5.33.5",
|
||||
"metascraper-url": "^5.34.2",
|
||||
"metascraper-video": "^5.33.5",
|
||||
"metascraper-youtube": "^5.33.5",
|
||||
"migrate": "^1.7.0",
|
||||
@ -87,7 +87,7 @@
|
||||
"mustache": "^4.2.0",
|
||||
"neo4j-driver": "^4.0.2",
|
||||
"neo4j-graphql-js": "^2.11.5",
|
||||
"neode": "^0.4.8",
|
||||
"neode": "^0.4.9",
|
||||
"node-fetch": "~2.6.1",
|
||||
"nodemailer": "^6.4.4",
|
||||
"nodemailer-html-to-text": "^3.2.0",
|
||||
|
||||
@ -38,8 +38,10 @@ export const createPostMutation = () => {
|
||||
id
|
||||
}
|
||||
eventStart
|
||||
eventEnd
|
||||
eventLocationName
|
||||
eventVenue
|
||||
eventIsOnline
|
||||
eventLocation {
|
||||
lng
|
||||
lat
|
||||
@ -70,6 +72,7 @@ export const filterPosts = () => {
|
||||
id
|
||||
title
|
||||
content
|
||||
eventStart
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@ -126,7 +126,7 @@ describe('Query', () => {
|
||||
author: null,
|
||||
date: expect.any(String),
|
||||
description:
|
||||
'Human Connection, Weilheim an der Teck. Gefällt 24.407 Mal. An upcoming non-profit social network focused on local and global positive change. Twitter accounts : @hc_world (EN), @hc_deutschland (GE),...',
|
||||
'Human Connection, Weilheim an der Teck. Gefällt 24.407 Mal. An upcoming non-profit social network focused on local and global positive change. Twitter accounts : @hc_world (EN), @hc_deutschland (GE),…',
|
||||
html: null,
|
||||
image:
|
||||
'https://scontent.ftxl3-1.fna.fbcdn.net/v/t1.0-1/c5.0.200.200a/p200x200/12108307_997373093648222_70057205881020137_n.jpg?_nc_cat=110&_nc_oc=AQnPPYQlR0dU556gOfl4xkXr7IPZdRIAUfQeXl3fpUv4DAsFN8T4PfgOjPwuq85GPKGZ5S5E5mWQ8IVV1UiRBAIZ&_nc_ht=scontent.ftxl3-1.fna&oh=90309adddaab38839782f16e7d4b7bcf&oe=5DEEDFE5',
|
||||
@ -196,7 +196,7 @@ Have all the information for the brand in separate config files. Set these defau
|
||||
publisher: 'YouTube',
|
||||
date: expect.any(String),
|
||||
description:
|
||||
'She’s incapable of controlling her limbs when her kitty is around. The obsession grows every day. Ps. That’s a sleep sack she’s in. Not a starfish outfit. Al...',
|
||||
'She’s incapable of controlling her limbs when her kitty is around. The obsession grows every day. Ps. That’s a sleep sack she’s in. Not a starfish outfit. Al…',
|
||||
url: 'https://www.youtube.com/watch?v=qkdXAtO40Fo',
|
||||
image: 'https://i.ytimg.com/vi/qkdXAtO40Fo/maxresdefault.jpg',
|
||||
audio: null,
|
||||
|
||||
230
backend/src/schema/resolvers/filter-posts.spec.js
Normal file
230
backend/src/schema/resolvers/filter-posts.spec.js
Normal file
@ -0,0 +1,230 @@
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import Factory, { cleanDatabase } from '../../db/factories'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import createServer from '../../server'
|
||||
import CONFIG from '../../config'
|
||||
import { filterPosts, createPostMutation } from '../../graphql/posts'
|
||||
|
||||
CONFIG.CATEGORIES_ACTIVE = false
|
||||
|
||||
const driver = getDriver()
|
||||
const neode = getNeode()
|
||||
|
||||
let query
|
||||
let mutate
|
||||
let authenticatedUser
|
||||
let user
|
||||
|
||||
beforeAll(async () => {
|
||||
await cleanDatabase()
|
||||
|
||||
const { server } = createServer({
|
||||
context: () => {
|
||||
return {
|
||||
driver,
|
||||
neode,
|
||||
user: authenticatedUser,
|
||||
}
|
||||
},
|
||||
})
|
||||
query = createTestClient(server).query
|
||||
mutate = createTestClient(server).mutate
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDatabase()
|
||||
driver.close()
|
||||
})
|
||||
|
||||
describe('Filter Posts', () => {
|
||||
const now = new Date()
|
||||
|
||||
beforeAll(async () => {
|
||||
user = await Factory.build('user', {
|
||||
id: 'user',
|
||||
name: 'User',
|
||||
about: 'I am a user.',
|
||||
})
|
||||
authenticatedUser = await user.toJson()
|
||||
await mutate({
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'a1',
|
||||
title: 'I am an article',
|
||||
content: 'I am an article written by user.',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'a2',
|
||||
title: 'I am anonther article',
|
||||
content: 'I am another article written by user.',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'e1',
|
||||
title: 'Illegaler Kindergeburtstag',
|
||||
content: 'Elli wird fünf. Wir feiern ihren Geburtstag.',
|
||||
postType: 'Event',
|
||||
eventInput: {
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
|
||||
eventVenue: 'Garten der Familie Maier',
|
||||
},
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'e2',
|
||||
title: 'Räuber-Treffen',
|
||||
content: 'Planung der nächsten Räuberereien',
|
||||
postType: 'Event',
|
||||
eventInput: {
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1).toISOString(),
|
||||
eventVenue: 'Wirtshaus im Spessart',
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('no filters set', () => {
|
||||
it('finds all posts', async () => {
|
||||
const {
|
||||
data: { Post: result },
|
||||
} = await query({ query: filterPosts() })
|
||||
expect(result).toHaveLength(4)
|
||||
expect(result).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ id: 'a1' }),
|
||||
expect.objectContaining({ id: 'a2' }),
|
||||
expect.objectContaining({ id: 'e1' }),
|
||||
expect.objectContaining({ id: 'e2' }),
|
||||
]),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('post type filter set to ["Article"]', () => {
|
||||
it('finds the articles', async () => {
|
||||
const {
|
||||
data: { Post: result },
|
||||
} = await query({ query: filterPosts(), variables: { filter: { postType_in: ['Article'] } } })
|
||||
expect(result).toHaveLength(2)
|
||||
expect(result).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ id: 'a1' }),
|
||||
expect.objectContaining({ id: 'a2' }),
|
||||
]),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('post type filter set to ["Event"]', () => {
|
||||
it('finds the articles', async () => {
|
||||
const {
|
||||
data: { Post: result },
|
||||
} = await query({ query: filterPosts(), variables: { filter: { postType_in: ['Event'] } } })
|
||||
expect(result).toHaveLength(2)
|
||||
expect(result).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ id: 'e1' }),
|
||||
expect.objectContaining({ id: 'e2' }),
|
||||
]),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('post type filter set to ["Article", "Event"]', () => {
|
||||
it('finds all posts', async () => {
|
||||
const {
|
||||
data: { Post: result },
|
||||
} = await query({
|
||||
query: filterPosts(),
|
||||
variables: { filter: { postType_in: ['Article', 'Event'] } },
|
||||
})
|
||||
expect(result).toHaveLength(4)
|
||||
expect(result).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ id: 'a1' }),
|
||||
expect.objectContaining({ id: 'a2' }),
|
||||
expect.objectContaining({ id: 'e1' }),
|
||||
expect.objectContaining({ id: 'e2' }),
|
||||
]),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('order events by event start descending', () => {
|
||||
it('finds the events orderd accordingly', async () => {
|
||||
const {
|
||||
data: { Post: result },
|
||||
} = await query({
|
||||
query: filterPosts(),
|
||||
variables: { filter: { postType_in: ['Event'] }, orderBy: ['eventStart_desc'] },
|
||||
})
|
||||
expect(result).toHaveLength(2)
|
||||
expect(result).toEqual([
|
||||
expect.objectContaining({
|
||||
id: 'e1',
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: 'e2',
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1).toISOString(),
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('order events by event start ascending', () => {
|
||||
it('finds the events orderd accordingly', async () => {
|
||||
const {
|
||||
data: { Post: result },
|
||||
} = await query({
|
||||
query: filterPosts(),
|
||||
variables: { filter: { postType_in: ['Event'] }, orderBy: ['eventStart_asc'] },
|
||||
})
|
||||
expect(result).toHaveLength(2)
|
||||
expect(result).toEqual([
|
||||
expect.objectContaining({
|
||||
id: 'e2',
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1).toISOString(),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: 'e1',
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('filter events by event start date', () => {
|
||||
it('finds only events after given date', async () => {
|
||||
const {
|
||||
data: { Post: result },
|
||||
} = await query({
|
||||
query: filterPosts(),
|
||||
variables: {
|
||||
filter: {
|
||||
postType_in: ['Event'],
|
||||
eventStart_gte: new Date(
|
||||
now.getFullYear(),
|
||||
now.getMonth(),
|
||||
now.getDate() + 2,
|
||||
).toISOString(),
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result).toEqual([
|
||||
expect.objectContaining({
|
||||
id: 'e1',
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -5,22 +5,25 @@ export const validateEventParams = (params) => {
|
||||
const { eventInput } = params
|
||||
validateEventDate(eventInput.eventStart)
|
||||
params.eventStart = eventInput.eventStart
|
||||
if (eventInput.eventLocation && !eventInput.eventVenue) {
|
||||
if (eventInput.eventEnd) {
|
||||
validateEventEnd(eventInput.eventStart, eventInput.eventEnd)
|
||||
params.eventEnd = eventInput.eventEnd
|
||||
}
|
||||
if (eventInput.eventLocationName && !eventInput.eventVenue) {
|
||||
throw new UserInputError('Event venue must be present if event location is given!')
|
||||
}
|
||||
params.eventVenue = eventInput.eventVenue
|
||||
params.eventLocation = eventInput.eventLocation
|
||||
params.eventLocationName = eventInput.eventLocationName
|
||||
params.eventIsOnline = !!eventInput.eventIsOnline
|
||||
}
|
||||
delete params.eventInput
|
||||
let locationName
|
||||
if (params.eventLocation) {
|
||||
params.eventLocationName = params.eventLocation
|
||||
locationName = params.eventLocation
|
||||
if (params.eventLocationName) {
|
||||
locationName = params.eventLocationName
|
||||
} else {
|
||||
params.eventLocationName = null
|
||||
locationName = null
|
||||
}
|
||||
delete params.eventLocation
|
||||
return locationName
|
||||
}
|
||||
|
||||
@ -33,3 +36,12 @@ const validateEventDate = (dateString) => {
|
||||
throw new UserInputError('Event start date must be in the future!')
|
||||
}
|
||||
}
|
||||
|
||||
const validateEventEnd = (start, end) => {
|
||||
const endDate = new Date(end)
|
||||
if (endDate.toString() === 'Invalid Date')
|
||||
throw new UserInputError('Event end date must be a valid date!')
|
||||
const startDate = new Date(start)
|
||||
if (endDate < startDate)
|
||||
throw new UserInputError('Event end date must be a after event start date!')
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ export default {
|
||||
OPTIONAL MATCH (resource)<-[membership:MEMBER_OF]-(relatedUser)
|
||||
WITH user, notification, resource, membership, relatedUser,
|
||||
[(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors,
|
||||
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post {.*, author: properties(author)} ] AS posts
|
||||
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post {.*, author: properties(author), postType: filter(l IN labels(post) WHERE NOT l = "Post")} ] AS posts
|
||||
WITH resource, user, notification, authors, posts, relatedUser, membership,
|
||||
resource {.*,
|
||||
__typename: labels(resource)[0],
|
||||
@ -90,7 +90,7 @@ export default {
|
||||
SET notification.read = TRUE
|
||||
WITH user, notification, resource,
|
||||
[(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors,
|
||||
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author)} ] AS posts
|
||||
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author), postType: filter(l IN labels(post) WHERE NOT l = "Post")} ] AS posts
|
||||
OPTIONAL MATCH (resource)<-[membership:MEMBER_OF]-(user)
|
||||
WITH resource, user, notification, authors, posts, membership,
|
||||
resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0], myRole: membership.role } AS finalResource
|
||||
@ -120,7 +120,7 @@ export default {
|
||||
SET notification.read = TRUE
|
||||
WITH user, notification, resource,
|
||||
[(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors,
|
||||
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author)} ] AS posts
|
||||
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author), postType: filter(l IN labels(post) WHERE NOT l = "Post")} ] AS posts
|
||||
OPTIONAL MATCH (resource)<-[membership:MEMBER_OF]-(user)
|
||||
WITH resource, user, notification, authors, posts, membership,
|
||||
resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0], myRole: membership.role} AS finalResource
|
||||
|
||||
@ -334,6 +334,7 @@ export default {
|
||||
`
|
||||
MATCH (user:User {id: $userId}) WHERE user.role = 'admin'
|
||||
MATCH (post:Post {id: $params.id})
|
||||
WHERE NOT((post)-[:IN]->(:Group))
|
||||
MERGE (user)-[pinned:PINNED {createdAt: toString(datetime())}]->(post)
|
||||
SET post.pinned = true
|
||||
RETURN post, pinned.createdAt as pinnedAt
|
||||
@ -346,10 +347,12 @@ export default {
|
||||
}))
|
||||
})
|
||||
const [transactionResult] = await writeTxResultPromise
|
||||
const { pinnedPost, pinnedAt } = transactionResult
|
||||
pinnedPostWithNestedAttributes = {
|
||||
...pinnedPost,
|
||||
pinnedAt,
|
||||
if (transactionResult) {
|
||||
const { pinnedPost, pinnedAt } = transactionResult
|
||||
pinnedPostWithNestedAttributes = {
|
||||
...pinnedPost,
|
||||
pinnedAt,
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
session.close()
|
||||
@ -416,6 +419,8 @@ export default {
|
||||
'eventLocation',
|
||||
'eventLocationName',
|
||||
'eventStart',
|
||||
'eventEnd',
|
||||
'eventIsOnline',
|
||||
],
|
||||
hasMany: {
|
||||
tags: '-[:TAGGED]->(related:Tag)',
|
||||
|
||||
@ -398,7 +398,7 @@ describe('CreatePost', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('event location is given but event venue is missing', () => {
|
||||
describe('with valid start date and invalid end date', () => {
|
||||
it('throws an error', async () => {
|
||||
const now = new Date()
|
||||
await expect(
|
||||
@ -409,7 +409,114 @@ describe('CreatePost', () => {
|
||||
postType: 'Event',
|
||||
eventInput: {
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
|
||||
eventLocation: 'Berlin',
|
||||
eventEnd: 'not-valid',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
errors: [
|
||||
{
|
||||
message: 'Event end date must be a valid date!',
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with valid start date and end date before start date', () => {
|
||||
it('throws an error', async () => {
|
||||
const now = new Date()
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
...variables,
|
||||
postType: 'Event',
|
||||
eventInput: {
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth() + 2).toISOString(),
|
||||
eventEnd: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
errors: [
|
||||
{
|
||||
message: 'Event end date must be a after event start date!',
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with valid start date and valid end date', () => {
|
||||
it('creates the event', async () => {
|
||||
const now = new Date()
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
...variables,
|
||||
postType: 'Event',
|
||||
eventInput: {
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
|
||||
eventEnd: new Date(now.getFullYear(), now.getMonth() + 2).toISOString(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
CreatePost: {
|
||||
postType: ['Event'],
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
|
||||
eventEnd: new Date(now.getFullYear(), now.getMonth() + 2).toISOString(),
|
||||
eventIsOnline: false,
|
||||
},
|
||||
},
|
||||
errors: undefined,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with valid start date and event is online', () => {
|
||||
it('creates the event', async () => {
|
||||
const now = new Date()
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
...variables,
|
||||
postType: 'Event',
|
||||
eventInput: {
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
|
||||
eventIsOnline: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
CreatePost: {
|
||||
postType: ['Event'],
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
|
||||
eventIsOnline: true,
|
||||
},
|
||||
},
|
||||
errors: undefined,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('event location name is given but event venue is missing', () => {
|
||||
it('throws an error', async () => {
|
||||
const now = new Date()
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
...variables,
|
||||
postType: 'Event',
|
||||
eventInput: {
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
|
||||
eventLocationName: 'Berlin',
|
||||
},
|
||||
},
|
||||
}),
|
||||
@ -442,6 +549,7 @@ describe('CreatePost', () => {
|
||||
CreatePost: {
|
||||
postType: ['Event'],
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
|
||||
eventIsOnline: false,
|
||||
},
|
||||
},
|
||||
errors: undefined,
|
||||
@ -449,7 +557,7 @@ describe('CreatePost', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('valid event input with location', () => {
|
||||
describe('valid event input with location name', () => {
|
||||
it('has label "Event" set', async () => {
|
||||
const now = new Date()
|
||||
await expect(
|
||||
@ -460,7 +568,7 @@ describe('CreatePost', () => {
|
||||
postType: 'Event',
|
||||
eventInput: {
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
|
||||
eventLocation: 'Leipzig',
|
||||
eventLocationName: 'Leipzig',
|
||||
eventVenue: 'Connewitzer Kreuz',
|
||||
},
|
||||
},
|
||||
@ -713,7 +821,7 @@ describe('UpdatePost', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('event location is given but event venue is missing', () => {
|
||||
describe('event location name is given but event venue is missing', () => {
|
||||
it('throws an error', async () => {
|
||||
const now = new Date()
|
||||
await expect(
|
||||
@ -724,7 +832,7 @@ describe('UpdatePost', () => {
|
||||
postType: 'Event',
|
||||
eventInput: {
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
|
||||
eventLocation: 'Berlin',
|
||||
eventLocationName: 'Berlin',
|
||||
},
|
||||
},
|
||||
}),
|
||||
@ -738,7 +846,7 @@ describe('UpdatePost', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('valid event input without location', () => {
|
||||
describe('valid event input without location name', () => {
|
||||
it('has label "Event" set', async () => {
|
||||
const now = new Date()
|
||||
await expect(
|
||||
@ -764,7 +872,7 @@ describe('UpdatePost', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('valid event input with location', () => {
|
||||
describe('valid event input with location name', () => {
|
||||
it('has label "Event" set', async () => {
|
||||
const now = new Date()
|
||||
await expect(
|
||||
@ -775,7 +883,7 @@ describe('UpdatePost', () => {
|
||||
postType: 'Event',
|
||||
eventInput: {
|
||||
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
|
||||
eventLocation: 'Leipzig',
|
||||
eventLocationName: 'Leipzig',
|
||||
eventVenue: 'Connewitzer Kreuz',
|
||||
},
|
||||
},
|
||||
|
||||
@ -818,11 +818,13 @@ describe('Posts in Groups', () => {
|
||||
id: 'post-to-public-group',
|
||||
title: 'A post to a public group',
|
||||
content: 'I am posting into a public group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-without-group',
|
||||
title: 'A post without a group',
|
||||
content: 'I am a user who does not belong to a group yet.',
|
||||
eventStart: null,
|
||||
},
|
||||
]),
|
||||
},
|
||||
@ -846,11 +848,13 @@ describe('Posts in Groups', () => {
|
||||
id: 'post-to-public-group',
|
||||
title: 'A post to a public group',
|
||||
content: 'I am posting into a public group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-without-group',
|
||||
title: 'A post without a group',
|
||||
content: 'I am a user who does not belong to a group yet.',
|
||||
eventStart: null,
|
||||
},
|
||||
]),
|
||||
},
|
||||
@ -874,11 +878,13 @@ describe('Posts in Groups', () => {
|
||||
id: 'post-to-public-group',
|
||||
title: 'A post to a public group',
|
||||
content: 'I am posting into a public group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-without-group',
|
||||
title: 'A post without a group',
|
||||
content: 'I am a user who does not belong to a group yet.',
|
||||
eventStart: null,
|
||||
},
|
||||
]),
|
||||
},
|
||||
@ -902,11 +908,13 @@ describe('Posts in Groups', () => {
|
||||
id: 'post-to-public-group',
|
||||
title: 'A post to a public group',
|
||||
content: 'I am posting into a public group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-without-group',
|
||||
title: 'A post without a group',
|
||||
content: 'I am a user who does not belong to a group yet.',
|
||||
eventStart: null,
|
||||
},
|
||||
]),
|
||||
},
|
||||
@ -930,21 +938,25 @@ describe('Posts in Groups', () => {
|
||||
id: 'post-to-public-group',
|
||||
title: 'A post to a public group',
|
||||
content: 'I am posting into a public group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-without-group',
|
||||
title: 'A post without a group',
|
||||
content: 'I am a user who does not belong to a group yet.',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-to-closed-group',
|
||||
title: 'A post to a closed group',
|
||||
content: 'I am posting into a closed group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-to-hidden-group',
|
||||
title: 'A post to a hidden group',
|
||||
content: 'I am posting into a hidden group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
]),
|
||||
},
|
||||
@ -1319,16 +1331,19 @@ describe('Posts in Groups', () => {
|
||||
id: 'post-to-public-group',
|
||||
title: 'A post to a public group',
|
||||
content: 'I am posting into a public group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-without-group',
|
||||
title: 'A post without a group',
|
||||
content: 'I am a user who does not belong to a group yet.',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-to-closed-group',
|
||||
title: 'A post to a closed group',
|
||||
content: 'I am posting into a closed group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
]),
|
||||
},
|
||||
@ -1361,21 +1376,25 @@ describe('Posts in Groups', () => {
|
||||
id: 'post-to-public-group',
|
||||
title: 'A post to a public group',
|
||||
content: 'I am posting into a public group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-without-group',
|
||||
title: 'A post without a group',
|
||||
content: 'I am a user who does not belong to a group yet.',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-to-closed-group',
|
||||
title: 'A post to a closed group',
|
||||
content: 'I am posting into a closed group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-to-hidden-group',
|
||||
title: 'A post to a hidden group',
|
||||
content: 'I am posting into a hidden group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
]),
|
||||
},
|
||||
@ -1410,16 +1429,19 @@ describe('Posts in Groups', () => {
|
||||
id: 'post-to-public-group',
|
||||
title: 'A post to a public group',
|
||||
content: 'I am posting into a public group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-without-group',
|
||||
title: 'A post without a group',
|
||||
content: 'I am a user who does not belong to a group yet.',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-to-hidden-group',
|
||||
title: 'A post to a hidden group',
|
||||
content: 'I am posting into a hidden group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
]),
|
||||
},
|
||||
@ -1452,11 +1474,13 @@ describe('Posts in Groups', () => {
|
||||
id: 'post-to-public-group',
|
||||
title: 'A post to a public group',
|
||||
content: 'I am posting into a public group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-without-group',
|
||||
title: 'A post without a group',
|
||||
content: 'I am a user who does not belong to a group yet.',
|
||||
eventStart: null,
|
||||
},
|
||||
]),
|
||||
},
|
||||
@ -1489,21 +1513,25 @@ describe('Posts in Groups', () => {
|
||||
id: 'post-to-public-group',
|
||||
title: 'A post to a public group',
|
||||
content: 'I am posting into a public group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-without-group',
|
||||
title: 'A post without a group',
|
||||
content: 'I am a user who does not belong to a group yet.',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-to-closed-group',
|
||||
title: 'A post to a closed group',
|
||||
content: 'I am posting into a closed group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-to-hidden-group',
|
||||
title: 'A post to a hidden group',
|
||||
content: 'I am posting into a hidden group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
]),
|
||||
},
|
||||
@ -1534,21 +1562,25 @@ describe('Posts in Groups', () => {
|
||||
id: 'post-to-public-group',
|
||||
title: 'A post to a public group',
|
||||
content: 'I am posting into a public group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-without-group',
|
||||
title: 'A post without a group',
|
||||
content: 'I am a user who does not belong to a group yet.',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-to-closed-group',
|
||||
title: 'A post to a closed group',
|
||||
content: 'I am posting into a closed group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-to-hidden-group',
|
||||
title: 'A post to a hidden group',
|
||||
content: 'I am posting into a hidden group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
]),
|
||||
},
|
||||
@ -1579,21 +1611,25 @@ describe('Posts in Groups', () => {
|
||||
id: 'post-to-public-group',
|
||||
title: 'A post to a public group',
|
||||
content: 'I am posting into a public group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-without-group',
|
||||
title: 'A post without a group',
|
||||
content: 'I am a user who does not belong to a group yet.',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-to-closed-group',
|
||||
title: 'A post to a closed group',
|
||||
content: 'I am posting into a closed group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-to-hidden-group',
|
||||
title: 'A post to a hidden group',
|
||||
content: 'I am posting into a hidden group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
]),
|
||||
},
|
||||
@ -1628,21 +1664,25 @@ describe('Posts in Groups', () => {
|
||||
id: 'post-to-public-group',
|
||||
title: 'A post to a public group',
|
||||
content: 'I am posting into a public group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-without-group',
|
||||
title: 'A post without a group',
|
||||
content: 'I am a user who does not belong to a group yet.',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-to-closed-group',
|
||||
title: 'A post to a closed group',
|
||||
content: 'I am posting into a closed group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-to-hidden-group',
|
||||
title: 'A post to a hidden group',
|
||||
content: 'I am posting into a hidden group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
]),
|
||||
},
|
||||
@ -1675,21 +1715,25 @@ describe('Posts in Groups', () => {
|
||||
id: 'post-to-public-group',
|
||||
title: 'A post to a public group',
|
||||
content: 'I am posting into a public group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-without-group',
|
||||
title: 'A post without a group',
|
||||
content: 'I am a user who does not belong to a group yet.',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-to-closed-group',
|
||||
title: 'A post to a closed group',
|
||||
content: 'I am posting into a closed group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-to-hidden-group',
|
||||
title: 'A post to a hidden group',
|
||||
content: 'I am posting into a hidden group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
]),
|
||||
},
|
||||
@ -1739,11 +1783,13 @@ describe('Posts in Groups', () => {
|
||||
id: 'post-to-closed-group',
|
||||
title: 'A post to a closed group',
|
||||
content: 'I am posting into a closed group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
{
|
||||
id: 'post-to-hidden-group',
|
||||
title: 'A post to a hidden group',
|
||||
content: 'I am posting into a hidden group as a member of the group',
|
||||
eventStart: null,
|
||||
},
|
||||
]),
|
||||
},
|
||||
|
||||
@ -223,8 +223,7 @@ export default {
|
||||
},
|
||||
searchResults: async (_parent, args, context, _resolveInfo) => {
|
||||
const { query, limit } = args
|
||||
let userId = null
|
||||
if (context.user) userId = context.user.id
|
||||
const userId = context.user?.id || null
|
||||
|
||||
const searchType = query.replace(/^([!@#&]?).*$/, '$1')
|
||||
const searchString = query.replace(/^([!@#&])/, '')
|
||||
|
||||
@ -33,7 +33,7 @@ const matchSomeWordsExactly = (str, boost = 2) => {
|
||||
const matchBeginningOfWords = (str) => {
|
||||
return str
|
||||
.split(' ')
|
||||
.filter((s) => s.length > 3)
|
||||
.filter((s) => s.length >= 2)
|
||||
.map((s) => s + '*')
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ describe('queryString', () => {
|
||||
|
||||
describe('globbing for longer words', () => {
|
||||
it('globs words with more than three characters', () => {
|
||||
expect(queryString('a couple of words')).toContain('couple* words*')
|
||||
expect(queryString('a couple of words')).toContain('couple* of* words*')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -22,7 +22,7 @@ const locales = ['en', 'de', 'fr', 'nl', 'it', 'es', 'pt', 'pl', 'ru']
|
||||
|
||||
const createLocation = async (session, mapboxData) => {
|
||||
const data = {
|
||||
id: mapboxData.id,
|
||||
id: mapboxData.id + (mapboxData.address ? `-${mapboxData.address}` : ''),
|
||||
nameEN: mapboxData.text_en,
|
||||
nameDE: mapboxData.text_de,
|
||||
nameFR: mapboxData.text_fr,
|
||||
@ -33,6 +33,7 @@ const createLocation = async (session, mapboxData) => {
|
||||
namePL: mapboxData.text_pl,
|
||||
nameRU: mapboxData.text_ru,
|
||||
type: mapboxData.id.split('.')[0].toLowerCase(),
|
||||
address: mapboxData.address,
|
||||
lng: mapboxData.center && mapboxData.center.length ? mapboxData.center[0] : null,
|
||||
lat: mapboxData.center && mapboxData.center.length ? mapboxData.center[1] : null,
|
||||
}
|
||||
@ -54,6 +55,10 @@ const createLocation = async (session, mapboxData) => {
|
||||
if (data.lat && data.lng) {
|
||||
mutation += ', l.lat = $lat, l.lng = $lng'
|
||||
}
|
||||
if (data.address) {
|
||||
mutation += ', l.address = $address'
|
||||
}
|
||||
|
||||
mutation += ' RETURN l.id'
|
||||
|
||||
await session.writeTransaction((transaction) => {
|
||||
@ -72,7 +77,7 @@ export const createOrUpdateLocations = async (nodeLabel, nodeId, locationName, s
|
||||
locationName,
|
||||
)}.json?access_token=${
|
||||
CONFIG.MAPBOX_TOKEN
|
||||
}&types=region,place,country&language=${locales.join(',')}`,
|
||||
}&types=region,place,country,address&language=${locales.join(',')}`,
|
||||
)
|
||||
|
||||
debug(res)
|
||||
@ -103,6 +108,10 @@ export const createOrUpdateLocations = async (nodeLabel, nodeId, locationName, s
|
||||
|
||||
let parent = data
|
||||
|
||||
if (parent.address) {
|
||||
parent.id += `-${parent.address}`
|
||||
}
|
||||
|
||||
if (data.context) {
|
||||
await asyncForEach(data.context, async (ctx) => {
|
||||
await createLocation(session, ctx)
|
||||
|
||||
@ -83,6 +83,8 @@ input _PostFilter {
|
||||
emotions_every: _PostEMOTEDFilter
|
||||
group: _GroupFilter
|
||||
postsInMyGroups: Boolean
|
||||
postType_in: [PostType]
|
||||
eventStart_gte: String
|
||||
}
|
||||
|
||||
enum _PostOrdering {
|
||||
@ -104,6 +106,8 @@ enum _PostOrdering {
|
||||
language_desc
|
||||
pinned_asc
|
||||
pinned_desc
|
||||
eventStart_asc
|
||||
eventStart_desc
|
||||
}
|
||||
|
||||
|
||||
@ -179,6 +183,8 @@ type Post {
|
||||
eventLocation: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l")
|
||||
eventVenue: String
|
||||
eventStart: String
|
||||
eventEnd: String
|
||||
eventIsOnline: Boolean
|
||||
}
|
||||
|
||||
input _PostInput {
|
||||
@ -187,8 +193,10 @@ input _PostInput {
|
||||
|
||||
input _EventInput {
|
||||
eventStart: String!
|
||||
eventLocation: String
|
||||
eventEnd: String
|
||||
eventVenue: String
|
||||
eventLocationName: String
|
||||
eventIsOnline: Boolean
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
|
||||
@ -82,7 +82,13 @@ const createServer = (options) => {
|
||||
const app = express()
|
||||
|
||||
app.set('driver', driver)
|
||||
app.use(helmet())
|
||||
// TODO: this exception is required for the graphql playground, since the playground loads external resources
|
||||
// See: https://github.com/graphql/graphql-playground/issues/1283
|
||||
app.use(
|
||||
helmet(
|
||||
(CONFIG.DEBUG && { contentSecurityPolicy: false, crossOriginEmbedderPolicy: false }) || {},
|
||||
),
|
||||
)
|
||||
app.use('/.well-known/', webfinger())
|
||||
app.use(express.static('public'))
|
||||
app.use(bodyParser.json({ limit: '10mb' }))
|
||||
|
||||
@ -1516,13 +1516,13 @@
|
||||
url-regex "~4.1.1"
|
||||
video-extensions "~1.1.0"
|
||||
|
||||
"@metascraper/helpers@^5.33.5":
|
||||
version "5.33.5"
|
||||
resolved "https://registry.yarnpkg.com/@metascraper/helpers/-/helpers-5.33.5.tgz#f980b41b3b7d7e67e11f849664727ef9d4802740"
|
||||
integrity sha512-gcULKpM00CNxlf7iWRTi4hQQIXWQUjeFal0V5U60C4P4YyfLXfjuQVBk6mmKSYENSRh7oBQhAR+YVnMalVWBcw==
|
||||
"@metascraper/helpers@^5.33.5", "@metascraper/helpers@^5.34.2":
|
||||
version "5.34.2"
|
||||
resolved "https://registry.yarnpkg.com/@metascraper/helpers/-/helpers-5.34.2.tgz#3c6ef10c1ab7e8b09b67219c91c7b8d30fedd0c1"
|
||||
integrity sha512-7mlQ1uYCUGw5OZU52leHbbjO8UrmmTvLCt6p4E4yWAvEW1oi9t7Pq1AmizX4hNUW+FgMvXCcv4t5BCfxiY8LhA==
|
||||
dependencies:
|
||||
audio-extensions "0.0.0"
|
||||
chrono-node "~2.5.0"
|
||||
chrono-node "~2.6.2"
|
||||
condense-whitespace "~2.0.0"
|
||||
entities "~4.4.0"
|
||||
file-extension "~4.0.5"
|
||||
@ -1532,7 +1532,7 @@
|
||||
is-uri "~1.2.4"
|
||||
iso-639-3 "~2.2.0"
|
||||
isostring "0.0.1"
|
||||
jsdom "~21.1.0"
|
||||
jsdom "~21.1.1"
|
||||
lodash "~4.17.21"
|
||||
memoize-one "~6.0.0"
|
||||
microsoft-capitalize "~1.0.5"
|
||||
@ -1540,7 +1540,7 @@
|
||||
normalize-url "~6.1.0"
|
||||
re2 "~1.18.0"
|
||||
smartquotes "~2.3.2"
|
||||
tldts "~5.7.103"
|
||||
tldts "~6.0.1"
|
||||
url-regex-safe "~3.0.0"
|
||||
video-extensions "~1.2.0"
|
||||
|
||||
@ -2166,7 +2166,7 @@ acorn@^7.1.0:
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
||||
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
||||
|
||||
acorn@^8.1.0, acorn@^8.8.1:
|
||||
acorn@^8.1.0, acorn@^8.8.2:
|
||||
version "8.8.2"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
|
||||
integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
|
||||
@ -3022,11 +3022,6 @@ boolbase@^1.0.0, boolbase@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
|
||||
|
||||
bowser@2.9.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.9.0.tgz#3bed854233b419b9a7422d9ee3e85504373821c9"
|
||||
integrity sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA==
|
||||
|
||||
boxen@^1.2.1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b"
|
||||
@ -3243,11 +3238,6 @@ camelcase@^6.2.0:
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
|
||||
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
|
||||
|
||||
camelize@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b"
|
||||
integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=
|
||||
|
||||
caniuse-lite@^1.0.30001219:
|
||||
version "1.0.30001230"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz#8135c57459854b2240b57a4a6786044bdc5a9f71"
|
||||
@ -3424,10 +3414,10 @@ chrono-node@~1.3.11:
|
||||
dependencies:
|
||||
moment "2.21.0"
|
||||
|
||||
chrono-node@~2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/chrono-node/-/chrono-node-2.5.0.tgz#c4d5f4f4db9d72604a5beb79fdc5fd57be4b212f"
|
||||
integrity sha512-GasdFCw4tsb8UKlwyJW1S+3bdN06vsyGR2cEDMlhEGI7ic4SQRnLyl/hbItwSum6pPkkUTrzFcaR3C2tZnnO5Q==
|
||||
chrono-node@~2.6.2:
|
||||
version "2.6.3"
|
||||
resolved "https://registry.yarnpkg.com/chrono-node/-/chrono-node-2.6.3.tgz#fa257cffb2dfc33fa5a01b26bd86f93741ca6dc5"
|
||||
integrity sha512-VkWaaZnNulqzNH9i4XCdyI05OX6MFEnCMNKdZOR4w//wS5/E2qkwAss/O5sj6SfTZK84fX4SSOG4pzqjqIseiA==
|
||||
dependencies:
|
||||
dayjs "^1.10.0"
|
||||
|
||||
@ -3672,11 +3662,6 @@ content-disposition@0.5.3:
|
||||
dependencies:
|
||||
safe-buffer "5.1.2"
|
||||
|
||||
content-security-policy-builder@2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/content-security-policy-builder/-/content-security-policy-builder-2.1.0.tgz#0a2364d769a3d7014eec79ff7699804deb8cfcbb"
|
||||
integrity sha512-/MtLWhJVvJNkA9dVLAp6fg9LxD2gfI6R2Fi1hPmfjYXSahJJzcfvoeDOxSyp4NvxMuwWv3WMssE9o31DoULHrQ==
|
||||
|
||||
content-type@~1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
||||
@ -3858,22 +3843,12 @@ cssfilter@0.0.10:
|
||||
resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae"
|
||||
integrity sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=
|
||||
|
||||
cssom@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36"
|
||||
integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==
|
||||
|
||||
cssom@~0.3.6:
|
||||
version "0.3.8"
|
||||
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
|
||||
integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==
|
||||
|
||||
cssstyle@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852"
|
||||
integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==
|
||||
cssstyle@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-3.0.0.tgz#17ca9c87d26eac764bb8cfd00583cff21ce0277a"
|
||||
integrity sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==
|
||||
dependencies:
|
||||
cssom "~0.3.6"
|
||||
rrweb-cssom "^0.6.0"
|
||||
|
||||
cucumber-expressions@^8.1.0:
|
||||
version "8.2.1"
|
||||
@ -3936,19 +3911,14 @@ dashdash@^1.12.0:
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
dasherize@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/dasherize/-/dasherize-2.0.0.tgz#6d809c9cd0cf7bb8952d80fc84fa13d47ddb1308"
|
||||
integrity sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg=
|
||||
|
||||
data-urls@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143"
|
||||
integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==
|
||||
data-urls@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4"
|
||||
integrity sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==
|
||||
dependencies:
|
||||
abab "^2.0.6"
|
||||
whatwg-mimetype "^3.0.0"
|
||||
whatwg-url "^11.0.0"
|
||||
whatwg-url "^12.0.0"
|
||||
|
||||
date-fns@2.22.1:
|
||||
version "2.22.1"
|
||||
@ -3993,7 +3963,7 @@ debug@^4.1.0, debug@^4.1.1, debug@^4.3.3:
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
decimal.js@^10.4.2:
|
||||
decimal.js@^10.4.3:
|
||||
version "10.4.3"
|
||||
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
|
||||
integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
|
||||
@ -4103,11 +4073,6 @@ denque@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf"
|
||||
integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==
|
||||
|
||||
depd@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
||||
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
|
||||
|
||||
depd@^1.1.2, depd@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
||||
@ -4157,11 +4122,6 @@ dir-glob@^3.0.1:
|
||||
dependencies:
|
||||
path-type "^4.0.0"
|
||||
|
||||
dns-prefetch-control@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/dns-prefetch-control/-/dns-prefetch-control-0.2.0.tgz#73988161841f3dcc81f47686d539a2c702c88624"
|
||||
integrity sha512-hvSnros73+qyZXhHFjx2CMLwoj3Fe7eR9EJsFsqmcI1bB2OBWL/+0YzaEaKssCHnj/6crawNnUyw74Gm2EKe+Q==
|
||||
|
||||
doctrine@1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
|
||||
@ -4317,11 +4277,6 @@ domutils@^3.0.1:
|
||||
domelementtype "^2.3.0"
|
||||
domhandler "^5.0.1"
|
||||
|
||||
dont-sniff-mimetype@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/dont-sniff-mimetype/-/dont-sniff-mimetype-1.1.0.tgz#c7d0427f8bcb095762751252af59d148b0a623b2"
|
||||
integrity sha512-ZjI4zqTaxveH2/tTlzS1wFp+7ncxNZaIEWYg3lzZRHkKf5zPT/MnEG6WL0BhHMJUabkh8GeU5NL5j+rEUCb7Ug==
|
||||
|
||||
dot-prop@^4.1.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.1.tgz#45884194a71fc2cda71cbb4bceb3a4dd2f433ba4"
|
||||
@ -4881,11 +4836,6 @@ expand-brackets@^2.1.4:
|
||||
snapdragon "^0.8.1"
|
||||
to-regex "^3.0.1"
|
||||
|
||||
expect-ct@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/expect-ct/-/expect-ct-0.2.0.tgz#3a54741b6ed34cc7a93305c605f63cd268a54a62"
|
||||
integrity sha512-6SK3MG/Bbhm8MsgyJAylg+ucIOU71/FzyFalcfu5nY19dH8y/z0tBJU0wrNBXD4B27EoQtqPF/9wqH0iYAd04g==
|
||||
|
||||
expect@^29.4.2:
|
||||
version "29.4.2"
|
||||
resolved "https://registry.yarnpkg.com/expect/-/expect-29.4.2.tgz#2ae34eb88de797c64a1541ad0f1e2ea8a7a7b492"
|
||||
@ -5046,11 +4996,6 @@ fb-watchman@^2.0.0:
|
||||
dependencies:
|
||||
bser "2.1.1"
|
||||
|
||||
feature-policy@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/feature-policy/-/feature-policy-0.3.0.tgz#7430e8e54a40da01156ca30aaec1a381ce536069"
|
||||
integrity sha512-ZtijOTFN7TzCujt1fnNhfWPFPSHeZkesff9AXZj+UEjYBynWNUIYpC87Ve4wHzyexQsImicLu7WsC2LHq7/xrQ==
|
||||
|
||||
figures@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/figures/-/figures-3.0.0.tgz#756275c964646163cc6f9197c7a0295dbfd04de9"
|
||||
@ -5213,11 +5158,6 @@ fragment-cache@^0.2.1:
|
||||
dependencies:
|
||||
map-cache "^0.2.2"
|
||||
|
||||
frameguard@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/frameguard/-/frameguard-3.1.0.tgz#bd1442cca1d67dc346a6751559b6d04502103a22"
|
||||
integrity sha512-TxgSKM+7LTA6sidjOiSZK9wxY0ffMPY3Wta//MqwmX0nZuEHc8QrkV8Fh3ZhMJeiH+Uyh/tcaarImRy8u77O7g==
|
||||
|
||||
franc@~4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/franc/-/franc-4.0.0.tgz#365951bc787b92ffbb1c63c1c492d6b76cbd3a56"
|
||||
@ -5744,46 +5684,10 @@ he@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||
|
||||
helmet-crossdomain@0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/helmet-crossdomain/-/helmet-crossdomain-0.4.0.tgz#5f1fe5a836d0325f1da0a78eaa5fd8429078894e"
|
||||
integrity sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA==
|
||||
|
||||
helmet-csp@2.10.0:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/helmet-csp/-/helmet-csp-2.10.0.tgz#685dde1747bc16c5e28ad9d91e229a69f0a85e84"
|
||||
integrity sha512-Rz953ZNEFk8sT2XvewXkYN0Ho4GEZdjAZy4stjiEQV3eN7GDxg1QKmYggH7otDyIA7uGA6XnUMVSgeJwbR5X+w==
|
||||
dependencies:
|
||||
bowser "2.9.0"
|
||||
camelize "1.0.0"
|
||||
content-security-policy-builder "2.1.0"
|
||||
dasherize "2.0.0"
|
||||
|
||||
helmet@~3.22.0:
|
||||
version "3.22.0"
|
||||
resolved "https://registry.yarnpkg.com/helmet/-/helmet-3.22.0.tgz#3a6f11d931799145f0aff15dbc563cff9e13131f"
|
||||
integrity sha512-Xrqicn2nm1ZIUxP3YGuTBmbDL04neKsIT583Sjh0FkiwKDXYCMUqGqC88w3NUvVXtA75JyR2Jn6jw6ZEMOD+ZA==
|
||||
dependencies:
|
||||
depd "2.0.0"
|
||||
dns-prefetch-control "0.2.0"
|
||||
dont-sniff-mimetype "1.1.0"
|
||||
expect-ct "0.2.0"
|
||||
feature-policy "0.3.0"
|
||||
frameguard "3.1.0"
|
||||
helmet-crossdomain "0.4.0"
|
||||
helmet-csp "2.10.0"
|
||||
hide-powered-by "1.1.0"
|
||||
hpkp "2.0.0"
|
||||
hsts "2.2.0"
|
||||
ienoopen "1.1.0"
|
||||
nocache "2.1.0"
|
||||
referrer-policy "1.2.0"
|
||||
x-xss-protection "1.3.0"
|
||||
|
||||
hide-powered-by@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/hide-powered-by/-/hide-powered-by-1.1.0.tgz#be3ea9cab4bdb16f8744be873755ca663383fa7a"
|
||||
integrity sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg==
|
||||
helmet@~7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/helmet/-/helmet-7.0.0.tgz#ac3011ba82fa2467f58075afa58a49427ba6212d"
|
||||
integrity sha512-MsIgYmdBh460ZZ8cJC81q4XJknjG567wzEmv46WOBblDb6TUd3z8/GhgmsM9pn8g2B80tAJ4m5/d3Bi1KrSUBQ==
|
||||
|
||||
homedir-polyfill@^1.0.1:
|
||||
version "1.0.3"
|
||||
@ -5797,18 +5701,6 @@ hosted-git-info@^2.1.4:
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.4.tgz#44119abaf4bc64692a16ace34700fed9c03e2546"
|
||||
integrity sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==
|
||||
|
||||
hpkp@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/hpkp/-/hpkp-2.0.0.tgz#10e142264e76215a5d30c44ec43de64dee6d1672"
|
||||
integrity sha1-EOFCJk52IVpdMMROxD3mTe5tFnI=
|
||||
|
||||
hsts@2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/hsts/-/hsts-2.2.0.tgz#09119d42f7a8587035d027dda4522366fe75d964"
|
||||
integrity sha512-ToaTnQ2TbJkochoVcdXYm4HOCliNozlviNsg+X2XQLQvZNI/kCHR9rZxVYpJB3UPcHz80PgxRyWQ7PdU1r+VBQ==
|
||||
dependencies:
|
||||
depd "2.0.0"
|
||||
|
||||
html-encoding-sniffer@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9"
|
||||
@ -5991,11 +5883,6 @@ ieee754@^1.2.1:
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||
|
||||
ienoopen@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ienoopen/-/ienoopen-1.1.0.tgz#411e5d530c982287dbdc3bb31e7a9c9e32630974"
|
||||
integrity sha512-MFs36e/ca6ohEKtinTJ5VvAJ6oDRAYFdYXweUnGY9L9vcoqFOU4n2ZhmJ0C4z/cwGZ3YIQRSB3XZ1+ghZkY5NQ==
|
||||
|
||||
ignore-by-default@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
|
||||
@ -7008,18 +6895,17 @@ jsbn@~0.1.0:
|
||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
|
||||
integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==
|
||||
|
||||
jsdom@~21.1.0:
|
||||
version "21.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-21.1.0.tgz#d56ba4a84ed478260d83bd53dc181775f2d8e6ef"
|
||||
integrity sha512-m0lzlP7qOtthD918nenK3hdItSd2I+V3W9IrBcB36sqDwG+KnUs66IF5GY7laGWUnlM9vTsD0W1QwSEBYWWcJg==
|
||||
jsdom@~21.1.1:
|
||||
version "21.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-21.1.1.tgz#ab796361e3f6c01bcfaeda1fea3c06197ac9d8ae"
|
||||
integrity sha512-Jjgdmw48RKcdAIQyUD1UdBh2ecH7VqwaXPN3ehoZN6MqgVbMn+lRm1aAT1AsdJRAJpwfa4IpwgzySn61h2qu3w==
|
||||
dependencies:
|
||||
abab "^2.0.6"
|
||||
acorn "^8.8.1"
|
||||
acorn "^8.8.2"
|
||||
acorn-globals "^7.0.0"
|
||||
cssom "^0.5.0"
|
||||
cssstyle "^2.3.0"
|
||||
data-urls "^3.0.2"
|
||||
decimal.js "^10.4.2"
|
||||
cssstyle "^3.0.0"
|
||||
data-urls "^4.0.0"
|
||||
decimal.js "^10.4.3"
|
||||
domexception "^4.0.0"
|
||||
escodegen "^2.0.0"
|
||||
form-data "^4.0.0"
|
||||
@ -7028,7 +6914,8 @@ jsdom@~21.1.0:
|
||||
https-proxy-agent "^5.0.1"
|
||||
is-potential-custom-element-name "^1.0.1"
|
||||
nwsapi "^2.2.2"
|
||||
parse5 "^7.1.1"
|
||||
parse5 "^7.1.2"
|
||||
rrweb-cssom "^0.6.0"
|
||||
saxes "^6.0.0"
|
||||
symbol-tree "^3.2.4"
|
||||
tough-cookie "^4.1.2"
|
||||
@ -7036,8 +6923,8 @@ jsdom@~21.1.0:
|
||||
webidl-conversions "^7.0.0"
|
||||
whatwg-encoding "^2.0.0"
|
||||
whatwg-mimetype "^3.0.0"
|
||||
whatwg-url "^11.0.0"
|
||||
ws "^8.11.0"
|
||||
whatwg-url "^12.0.1"
|
||||
ws "^8.13.0"
|
||||
xml-name-validator "^4.0.0"
|
||||
|
||||
jsesc@^2.5.1:
|
||||
@ -7624,12 +7511,12 @@ metascraper-publisher@^5.33.5:
|
||||
dependencies:
|
||||
"@metascraper/helpers" "^5.33.5"
|
||||
|
||||
metascraper-soundcloud@^5.33.5:
|
||||
version "5.33.5"
|
||||
resolved "https://registry.yarnpkg.com/metascraper-soundcloud/-/metascraper-soundcloud-5.33.5.tgz#9cd39e7ff432e715f8ecdca5256e7ea67f485557"
|
||||
integrity sha512-hkDhRfTKEUugIN9Gxh/l9HuWlkNAOKEBXov/9fYKbIMgaj7e2UU7bkNYjaDOXzMikCRWEIrrGP3UzwwIcs6Gjg==
|
||||
metascraper-soundcloud@^5.34.2:
|
||||
version "5.34.2"
|
||||
resolved "https://registry.yarnpkg.com/metascraper-soundcloud/-/metascraper-soundcloud-5.34.2.tgz#ee1077b4836321ccfb22bcab4bfae7360dd571a3"
|
||||
integrity sha512-1VafeFnlzJaYUlq8XbppQauz0i9xM8QycJU8k4ONftZwRtikQBIjRe5BMXr/s5n32831vBooRz8ksz0CXkjVlQ==
|
||||
dependencies:
|
||||
"@metascraper/helpers" "^5.33.5"
|
||||
"@metascraper/helpers" "^5.34.2"
|
||||
|
||||
metascraper-title@^5.33.5:
|
||||
version "5.33.5"
|
||||
@ -7638,12 +7525,12 @@ metascraper-title@^5.33.5:
|
||||
dependencies:
|
||||
"@metascraper/helpers" "^5.33.5"
|
||||
|
||||
metascraper-url@^5.33.5:
|
||||
version "5.33.5"
|
||||
resolved "https://registry.yarnpkg.com/metascraper-url/-/metascraper-url-5.33.5.tgz#eb0ba736f47aef7c9a511ca04e509d11ee51c95d"
|
||||
integrity sha512-bXKfsfxCfy6uhhnb9lMlIu1Uwqkn9eXa4QVcHdq/+1UWTOE8b2u4NTH4YgJMjcPmdth7s3cKXBxG1yBnsAoN0w==
|
||||
metascraper-url@^5.34.2:
|
||||
version "5.34.2"
|
||||
resolved "https://registry.yarnpkg.com/metascraper-url/-/metascraper-url-5.34.2.tgz#1136dbf1177d08835d6b8677a64d9f10f0517fb0"
|
||||
integrity sha512-ijPe3G64kyxFRWPA7YmH01dt5JImqBgWV07WIpgPj8wuD4ThwteGqV1CtjNYJN8yo/tCQeFNJPoubwiv9RsrJw==
|
||||
dependencies:
|
||||
"@metascraper/helpers" "^5.33.5"
|
||||
"@metascraper/helpers" "^5.34.2"
|
||||
|
||||
metascraper-video@^5.33.5:
|
||||
version "5.33.5"
|
||||
@ -8034,17 +7921,7 @@ neo4j-driver-core@^4.4.7:
|
||||
resolved "https://registry.yarnpkg.com/neo4j-driver-core/-/neo4j-driver-core-4.4.7.tgz#d2475e107b3fea2b9d1c36b0c273da5c5a291c37"
|
||||
integrity sha512-NhvVuQYgG7eO/vXxRaoJfkWUNkjvIpmCIS9UWU9Bbhb4V+wCOyX/MVOXqD0Yizhs4eyIkD7x90OXb79q+vi+oA==
|
||||
|
||||
neo4j-driver@^4.0.1, neo4j-driver@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/neo4j-driver/-/neo4j-driver-4.0.2.tgz#78de3b91e91572bcbd9d2e02554322fe1ab399ea"
|
||||
integrity sha512-xQN4BZZsweaNNac7FDYAV6f/JybghwY3lk4fwblS8V5KQ+DBMPe4Pthh672mp+wEYZGyzPalq5CfpcBrWaZ4Gw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
rxjs "^6.5.2"
|
||||
text-encoding-utf-8 "^1.0.2"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
neo4j-driver@^4.2.2:
|
||||
neo4j-driver@^4.0.1, neo4j-driver@^4.0.2, neo4j-driver@^4.2.2:
|
||||
version "4.4.7"
|
||||
resolved "https://registry.yarnpkg.com/neo4j-driver/-/neo4j-driver-4.4.7.tgz#51b3fb48241e66eb3be94e90032cc494c44e59f3"
|
||||
integrity sha512-N7GddPhp12gVJe4eB84u5ik5SmrtRv8nH3rK47Qy7IUKnJkVEos/F1QjOJN6zt1jLnDXwDcGzCKK8XklYpzogw==
|
||||
@ -8067,10 +7944,10 @@ neo4j-graphql-js@^2.11.5:
|
||||
lodash "^4.17.15"
|
||||
neo4j-driver "^4.0.1"
|
||||
|
||||
neode@^0.4.8:
|
||||
version "0.4.8"
|
||||
resolved "https://registry.yarnpkg.com/neode/-/neode-0.4.8.tgz#0889b4fc7f1bf0b470b01fa5b8870373b5d47ad6"
|
||||
integrity sha512-pb91NfCOg4Fj5o+98H+S2XYC+ByQfbdhwcc1UVuzuUQ0Ezzj+jWz8NmKWU8ZfCH6l4plk71yDAPd2eTwpt+Xvg==
|
||||
neode@^0.4.9:
|
||||
version "0.4.9"
|
||||
resolved "https://registry.yarnpkg.com/neode/-/neode-0.4.9.tgz#11c46c50d0348751dc6db828f3ca59252e7c247f"
|
||||
integrity sha512-3RufLD2cmTXrPpvsgkEAqS3maRjXJ89vjbyYmyqJMJl5uv6gcUa32N6hRwHcqfjWRWUaFKaoGLEJLaOQobs4eA==
|
||||
dependencies:
|
||||
"@hapi/joi" "^15.1.1"
|
||||
dotenv "^4.0.0"
|
||||
@ -8102,11 +7979,6 @@ no-case@^3.0.3:
|
||||
lower-case "^2.0.1"
|
||||
tslib "^1.10.0"
|
||||
|
||||
nocache@2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f"
|
||||
integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==
|
||||
|
||||
node-environment-flags@^1.0.5:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088"
|
||||
@ -8669,7 +8541,7 @@ parse5@^3.0.1:
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
parse5@^7.0.0, parse5@^7.1.1:
|
||||
parse5@^7.0.0, parse5@^7.1.2:
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32"
|
||||
integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==
|
||||
@ -8977,7 +8849,7 @@ punycode@1.3.2:
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
|
||||
integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
|
||||
|
||||
punycode@^2.1.0, punycode@^2.1.1:
|
||||
punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
|
||||
integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
|
||||
@ -9178,11 +9050,6 @@ redis-parser@^3.0.0:
|
||||
dependencies:
|
||||
redis-errors "^1.0.0"
|
||||
|
||||
referrer-policy@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/referrer-policy/-/referrer-policy-1.2.0.tgz#b99cfb8b57090dc454895ef897a4cc35ef67a98e"
|
||||
integrity sha512-LgQJIuS6nAy1Jd88DCQRemyE3mS+ispwlqMk3b0yjZ257fI1v9c+/p6SD5gP5FGyXUIgrNOAfmyioHwZtYv2VA==
|
||||
|
||||
regenerate-unicode-properties@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e"
|
||||
@ -9479,6 +9346,11 @@ rosie@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/rosie/-/rosie-2.0.1.tgz#c250c4787ce450b72aa9eff26509f68589814fa2"
|
||||
integrity sha1-wlDEeHzkULcqqe/yZQn2hYmBT6I=
|
||||
|
||||
rrweb-cssom@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1"
|
||||
integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==
|
||||
|
||||
rsvp@^4.8.4:
|
||||
version "4.8.5"
|
||||
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
|
||||
@ -9496,7 +9368,7 @@ run-parallel@^1.1.9:
|
||||
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679"
|
||||
integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==
|
||||
|
||||
rxjs@^6.4.0, rxjs@^6.5.2, rxjs@^6.5.4:
|
||||
rxjs@^6.4.0, rxjs@^6.5.4:
|
||||
version "6.5.4"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c"
|
||||
integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==
|
||||
@ -10311,11 +10183,6 @@ test-exclude@^6.0.0:
|
||||
glob "^7.1.4"
|
||||
minimatch "^3.0.4"
|
||||
|
||||
text-encoding-utf-8@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13"
|
||||
integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==
|
||||
|
||||
text-table@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
||||
@ -10378,17 +10245,17 @@ tlds@^1.228.0:
|
||||
resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.236.0.tgz#a118eebe33261c577e3a3025144faeabb7dd813c"
|
||||
integrity sha512-oP2PZ3KeGlgpHgsEfrtva3/K9kzsJUNliQSbCfrJ7JMCWFoCdtG+9YMq/g2AnADQ1v5tVlbtvKJZ4KLpy/P6MA==
|
||||
|
||||
tldts-core@^5.7.104:
|
||||
version "5.7.104"
|
||||
resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-5.7.104.tgz#4313a9663e4085332750a5fb04bfa0b0d91b826d"
|
||||
integrity sha512-8vhSgc2nzPNT0J7XyCqcOtQ6+ySBn+gsPmj5h95YytIZ7L2Xl40paUmj0T6Uko42HegHGQxXieunHIQuABWSmQ==
|
||||
tldts-core@^6.0.3:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.0.3.tgz#b59d87507c51cd2488c322b92020b973b608b59a"
|
||||
integrity sha512-PLiEM2aCkfGifyr8npbd93eWIW4isFRB44vTiup5DRie6e2Qy3+9quwHb252v12JyoM+RmF1cxtDgwD2PVBOjA==
|
||||
|
||||
tldts@~5.7.103:
|
||||
version "5.7.104"
|
||||
resolved "https://registry.yarnpkg.com/tldts/-/tldts-5.7.104.tgz#c964cf8a194936b086fa4c72994e92d4cd4d2562"
|
||||
integrity sha512-PlziEIVPH/ogbqOhS35K6MOeD09rd9U5g2NHO5n9NZeMC1PGpXgsjQpoJ1KiRnjhZsWDkzN8EoX3xQZuz5ZyFQ==
|
||||
tldts@~6.0.1:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.0.3.tgz#6a4a8bb550f396d6d72818606150e0e0263ae826"
|
||||
integrity sha512-rcdUIwrcGuMWe5+fg5FFBrmWTYdbfpHwkk1AjBKoSDbpsdAsYqJYKoZOVOHn8MQCYatADKGAx/SU+jpSKxSYNw==
|
||||
dependencies:
|
||||
tldts-core "^5.7.104"
|
||||
tldts-core "^6.0.3"
|
||||
|
||||
tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
@ -10479,12 +10346,12 @@ tough-cookie@^4.1.2:
|
||||
universalify "^0.2.0"
|
||||
url-parse "^1.5.3"
|
||||
|
||||
tr46@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9"
|
||||
integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
|
||||
tr46@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469"
|
||||
integrity sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==
|
||||
dependencies:
|
||||
punycode "^2.1.1"
|
||||
punycode "^2.3.0"
|
||||
|
||||
trigram-utils@^1.0.0:
|
||||
version "1.0.2"
|
||||
@ -10946,12 +10813,12 @@ whatwg-mimetype@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7"
|
||||
integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==
|
||||
|
||||
whatwg-url@^11.0.0:
|
||||
version "11.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018"
|
||||
integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==
|
||||
whatwg-url@^12.0.0, whatwg-url@^12.0.1:
|
||||
version "12.0.1"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-12.0.1.tgz#fd7bcc71192e7c3a2a97b9a8d6b094853ed8773c"
|
||||
integrity sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==
|
||||
dependencies:
|
||||
tr46 "^3.0.0"
|
||||
tr46 "^4.1.1"
|
||||
webidl-conversions "^7.0.0"
|
||||
|
||||
which@^1.2.9:
|
||||
@ -11062,15 +10929,10 @@ ws@^6.0.0:
|
||||
dependencies:
|
||||
async-limiter "~1.0.0"
|
||||
|
||||
ws@^8.11.0:
|
||||
version "8.12.0"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.0.tgz#485074cc392689da78e1828a9ff23585e06cddd8"
|
||||
integrity sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==
|
||||
|
||||
x-xss-protection@1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/x-xss-protection/-/x-xss-protection-1.3.0.tgz#3e3a8dd638da80421b0e9fff11a2dbe168f6d52c"
|
||||
integrity sha512-kpyBI9TlVipZO4diReZMAHWtS0MMa/7Kgx8hwG/EuZLiA6sg4Ah/4TRdASHhRRN3boobzcYgFRUFSgHRge6Qhg==
|
||||
ws@^8.13.0:
|
||||
version "8.13.0"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0"
|
||||
integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
|
||||
|
||||
xdg-basedir@^3.0.0:
|
||||
version "3.0.0"
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 540bd503b9401c1608ccda77bdd23fe47ba008fb
|
||||
Subproject commit be3ac7ad29f37d6a00fb3203db302cd91cebb9fa
|
||||
@ -65,10 +65,10 @@ services:
|
||||
|
||||
backend:
|
||||
image: ocelotsocialnetwork/backend-branded:local-${CONFIGURATION}
|
||||
container_name: backend
|
||||
container_name: backend-branded
|
||||
build:
|
||||
dockerfile: src/docker/backend.Dockerfile
|
||||
target: branded-branded
|
||||
target: branded
|
||||
context: .
|
||||
args:
|
||||
- CONFIGURATION=$CONFIGURATION
|
||||
@ -143,7 +143,7 @@ services:
|
||||
|
||||
neo4j:
|
||||
image: ocelotsocialnetwork/neo4j-community:latest
|
||||
container_name: neo4j
|
||||
container_name: neo4j-branded
|
||||
networks:
|
||||
- test-network
|
||||
volumes:
|
||||
|
||||
@ -16,6 +16,13 @@ if [ -z ${CONFIGURATION} ]; then
|
||||
fi
|
||||
echo "Using CONFIGURATION=${CONFIGURATION}"
|
||||
|
||||
# check DOCKERHUB_BRAND_VARRIANT
|
||||
if [ -z ${DOCKERHUB_BRAND_VARRIANT} ]; then
|
||||
echo "You must provide a `DOCKERHUB_BRAND_VARRIANT` via environment variable"
|
||||
exit 1
|
||||
fi
|
||||
echo "Using DOCKERHUB_BRAND_VARRIANT=${DOCKERHUB_BRAND_VARRIANT}"
|
||||
|
||||
# configuration
|
||||
DOCKERHUB_ORGANISATION=${DOCKERHUB_ORGANISATION:-"ocelotsocialnetwork"}
|
||||
OCELOT_VERSION=${OCELOT_VERSION:-$(node -p -e "require('${SCRIPT_DIR}/../../package.json').version")}
|
||||
@ -29,11 +36,11 @@ BUILD_COMMIT=${GITHUB_SHA:-"0000000"}
|
||||
|
||||
# backend
|
||||
docker build --target branded \
|
||||
-t "${DOCKERHUB_ORGANISATION}/backend-branded:latest" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/backend-branded:${OCELOT_VERSION}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/backend-branded:${OCELOT_VERSION_BUILD}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/backend-branded:${BUILD_VERSION_BASE}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/backend-branded:${BUILD_VERSION}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:latest" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION}" \
|
||||
-f "${SCRIPT_DIR}/../src/docker/backend.Dockerfile" \
|
||||
--build-arg "CONFIGURATION=${CONFIGURATION}" \
|
||||
--build-arg "APP_IMAGE_TAG_CODE=${OCELOT_VERSION}-code" \
|
||||
@ -42,11 +49,11 @@ docker build --target branded \
|
||||
|
||||
# webapp
|
||||
docker build --target branded \
|
||||
-t "${DOCKERHUB_ORGANISATION}/webapp-branded:latest" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/webapp-branded:${OCELOT_VERSION}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/webapp-branded:${OCELOT_VERSION_BUILD}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/webapp-branded:${BUILD_VERSION_BASE}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/webapp-branded:${BUILD_VERSION}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:latest" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION}" \
|
||||
-f "${SCRIPT_DIR}/../src/docker/webapp.Dockerfile" \
|
||||
--build-arg "CONFIGURATION=${CONFIGURATION}" \
|
||||
--build-arg "APP_IMAGE_TAG_CODE=${OCELOT_VERSION}-code" \
|
||||
@ -55,11 +62,11 @@ docker build --target branded \
|
||||
|
||||
# mainteance
|
||||
docker build --target branded \
|
||||
-t "${DOCKERHUB_ORGANISATION}/maintenance-branded:latest" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/maintenance-branded:${OCELOT_VERSION}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/maintenance-branded:${OCELOT_VERSION_BUILD}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/maintenance-branded:${BUILD_VERSION_BASE}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/maintenance-branded:${BUILD_VERSION}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:latest" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE}" \
|
||||
-t "${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION}" \
|
||||
-f "${SCRIPT_DIR}/../src/docker/maintenance.Dockerfile" \
|
||||
--build-arg "CONFIGURATION=${CONFIGURATION}" \
|
||||
--build-arg "APP_IMAGE_TAG_CODE=${OCELOT_VERSION}-code" \
|
||||
|
||||
@ -10,6 +10,13 @@
|
||||
SCRIPT_PATH=$(realpath $0)
|
||||
SCRIPT_DIR=$(dirname $SCRIPT_PATH)
|
||||
|
||||
# check DOCKERHUB_BRAND_VARRIANT
|
||||
if [ -z ${DOCKERHUB_BRAND_VARRIANT} ]; then
|
||||
echo "You must provide a `DOCKERHUB_BRAND_VARRIANT` via environment variable"
|
||||
exit 1
|
||||
fi
|
||||
echo "Using DOCKERHUB_BRAND_VARRIANT=${DOCKERHUB_BRAND_VARRIANT}"
|
||||
|
||||
# configuration
|
||||
DOCKERHUB_ORGANISATION=${DOCKERHUB_ORGANISATION:-"ocelotsocialnetwork"}
|
||||
OCELOT_VERSION=${OCELOT_VERSION:-$(node -p -e "require('${SCRIPT_DIR}/../../package.json').version")}
|
||||
@ -23,22 +30,22 @@ BUILD_VERSION=${BRANDED_VERSION}-ocelot.social${OCELOT_VERSION_BUILD}
|
||||
echo "${DOCKERHUB_TOKEN}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin
|
||||
|
||||
# push backend images
|
||||
docker push ${DOCKERHUB_ORGANISATION}/backend-branded:latest
|
||||
docker push ${DOCKERHUB_ORGANISATION}/backend-branded:${OCELOT_VERSION}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/backend-branded:${OCELOT_VERSION_BUILD}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/backend-branded:${BUILD_VERSION_BASE}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/backend-branded:${BUILD_VERSION}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:latest
|
||||
docker push ${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION}
|
||||
|
||||
# push webapp images
|
||||
docker push ${DOCKERHUB_ORGANISATION}/webapp-branded:latest
|
||||
docker push ${DOCKERHUB_ORGANISATION}/webapp-branded:${OCELOT_VERSION}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/webapp-branded:${OCELOT_VERSION_BUILD}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/webapp-branded:${BUILD_VERSION_BASE}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/webapp-branded:${BUILD_VERSION}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:latest
|
||||
docker push ${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION}
|
||||
|
||||
# push maintenance images
|
||||
docker push ${DOCKERHUB_ORGANISATION}/maintenance-branded:latest
|
||||
docker push ${DOCKERHUB_ORGANISATION}/maintenance-branded:${OCELOT_VERSION}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/maintenance-branded:${OCELOT_VERSION_BUILD}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/maintenance-branded:${BUILD_VERSION_BASE}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/maintenance-branded:${BUILD_VERSION}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:latest
|
||||
docker push ${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE}
|
||||
docker push ${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION}
|
||||
@ -39,6 +39,11 @@ spec:
|
||||
name: configmap-{{ .Release.Name }}-backend
|
||||
- secretRef:
|
||||
name: secret-{{ .Release.Name }}-backend
|
||||
resources:
|
||||
requests:
|
||||
memory: {{ .Values.BACKEND.RESOURCE_REQUESTS_MEMORY | default "500M" | quote }}
|
||||
limits:
|
||||
memory: {{ .Values.BACKEND.RESOURCE_LIMITS_MEMORY | default "1G" | quote }}
|
||||
ports:
|
||||
- containerPort: 4000
|
||||
protocol: TCP
|
||||
|
||||
@ -34,6 +34,11 @@ spec:
|
||||
name: configmap-{{ .Release.Name }}-webapp
|
||||
- secretRef:
|
||||
name: secret-{{ .Release.Name }}-webapp
|
||||
resources:
|
||||
requests:
|
||||
memory: {{ .Values.MAINTENANCE.RESOURCE_REQUESTS_MEMORY | default "500M" | quote }}
|
||||
limits:
|
||||
memory: {{ .Values.MAINTENANCE.RESOURCE_LIMITS_MEMORY | default "1G" | quote }}
|
||||
ports:
|
||||
- containerPort: 80
|
||||
restartPolicy: {{ .Values.MAINTENANCE.CONTAINER_RESTART_POLICY }}
|
||||
|
||||
@ -40,5 +40,10 @@ spec:
|
||||
name: configmap-{{ .Release.Name }}-webapp
|
||||
- secretRef:
|
||||
name: secret-{{ .Release.Name }}-webapp
|
||||
resources:
|
||||
requests:
|
||||
memory: {{ .Values.WEBAPP.RESOURCE_REQUESTS_MEMORY | default "500M" | quote }}
|
||||
limits:
|
||||
memory: {{ .Values.WEBAPP.RESOURCE_LIMITS_MEMORY | default "1G" | quote }}
|
||||
restartPolicy: {{ .Values.WEBAPP.CONTAINER_RESTART_POLICY }}
|
||||
terminationGracePeriodSeconds: {{ .Values.WEBAPP.CONTAINER_TERMINATION_GRACE_PERIOD_SECONDS }}
|
||||
10
package.json
10
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ocelot-social",
|
||||
"version": "2.5.1",
|
||||
"version": "2.6.0",
|
||||
"description": "Free and open source software program code available to run social networks.",
|
||||
"author": "ocelot.social Community",
|
||||
"license": "MIT",
|
||||
@ -22,8 +22,8 @@
|
||||
"release": "yarn version --no-git-tag-version --no-commit-hooks --no-commit && auto-changelog --latest-version $(node -p -e \"require('./package.json').version\") && cd backend && yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $(node -p -e \"require('./../package.json').version\") && cd ../webapp && yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $(node -p -e \"require('./../package.json').version\") && cd ../webapp/maintenance/source && yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $(node -p -e \"require('./../../../package.json').version\")"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/preset-env": "^7.12.7",
|
||||
"@babel/core": "^7.21.8",
|
||||
"@babel/preset-env": "^7.21.5",
|
||||
"@babel/register": "^7.12.10",
|
||||
"@faker-js/faker": "5.1.0",
|
||||
"auto-changelog": "^2.3.0",
|
||||
@ -34,9 +34,9 @@
|
||||
"cypress": "^7.0.1",
|
||||
"cypress-cucumber-preprocessor": "^2.2.1",
|
||||
"cypress-file-upload": "^3.5.3",
|
||||
"date-fns": "^2.25.0",
|
||||
"date-fns": "^2.30.0",
|
||||
"dotenv": "^8.2.0",
|
||||
"expect": "^25.3.0",
|
||||
"expect": "^29.5.0",
|
||||
"graphql-request": "^2.0.0",
|
||||
"import": "^0.0.6",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
##################################################################################
|
||||
# BASE (Is pushed to DockerHub for rebranding) ###################################
|
||||
##################################################################################
|
||||
FROM node:19.4.0-alpine3.17 as base
|
||||
FROM node:20.2.0-alpine3.17 as base
|
||||
|
||||
# ENVs
|
||||
## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
##################################################################################
|
||||
# BASE ###########################################################################
|
||||
##################################################################################
|
||||
FROM node:19.4.0-alpine3.17 as base
|
||||
FROM node:20.2.0-alpine3.17 as base
|
||||
|
||||
# ENVs
|
||||
## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame
|
||||
|
||||
5
webapp/assets/_new/icons/svgs/book.svg
Normal file
5
webapp/assets/_new/icons/svgs/book.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>book</title>
|
||||
<path d="M10 5c2.92 0 5.482 0.981 6 1.188 0.518-0.206 3.080-1.188 6-1.188 3.227 0 6.375 1.313 6.375 1.313l0.625 0.281v20.406h-11.281c-0.346 0.597-0.979 1-1.719 1s-1.373-0.403-1.719-1h-11.281v-20.406l0.625-0.281s3.148-1.313 6.375-1.313zM10 7c-2.199 0-4.232 0.69-5 0.969v16.125c1.188-0.392 2.897-0.875 5-0.875 2.057 0 3.888 0.506 5 0.875v-16.125c-1-0.343-3.067-0.969-5-0.969zM22 7c-1.933 0-4 0.626-5 0.969v16.125c1.112-0.369 2.943-0.875 5-0.875 2.103 0 3.813 0.483 5 0.875v-16.125c-0.768-0.279-2.801-0.969-5-0.969z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 680 B |
5
webapp/assets/_new/icons/svgs/calendar.svg
Normal file
5
webapp/assets/_new/icons/svgs/calendar.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>calendar</title>
|
||||
<path d="M9 4h2v1h10v-1h2v1h4v22h-22v-22h4v-1zM7 7v2h18v-2h-2v1h-2v-1h-10v1h-2v-1h-2zM7 11v14h18v-14h-18zM13 13h2v2h-2v-2zM17 13h2v2h-2v-2zM21 13h2v2h-2v-2zM9 17h2v2h-2v-2zM13 17h2v2h-2v-2zM17 17h2v2h-2v-2zM21 17h2v2h-2v-2zM9 21h2v2h-2v-2zM13 21h2v2h-2v-2zM17 21h2v2h-2v-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 445 B |
1
webapp/assets/_new/styles/mixins/buttonStates.css
Normal file
1
webapp/assets/_new/styles/mixins/buttonStates.css
Normal file
@ -0,0 +1 @@
|
||||
/*# sourceMappingURL=buttonStates.css.map */
|
||||
1
webapp/assets/_new/styles/mixins/buttonStates.css.map
Normal file
1
webapp/assets/_new/styles/mixins/buttonStates.css.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"sources":[],"names":[],"mappings":"","file":"buttonStates.css"}
|
||||
29
webapp/assets/_new/styles/resets.css
Normal file
29
webapp/assets/_new/styles/resets.css
Normal file
@ -0,0 +1,29 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
li {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}/*# sourceMappingURL=resets.css.map */
|
||||
1
webapp/assets/_new/styles/resets.css.map
Normal file
1
webapp/assets/_new/styles/resets.css.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"sources":["resets.scss","resets.css"],"names":[],"mappings":"AAAA;EACE,sBAAA;ACCF;;ADEA;EACE,UAAA;EACA,uBAAA;EACA,YAAA;EACA,oBAAA;EACA,kBAAA;ACCF;;ADEA;;;;;;;;EAQE,SAAA;ACCF;;ADEA;;EAEE,qBAAA;EACA,SAAA;EACA,UAAA;ACCF","file":"resets.css"}
|
||||
122
webapp/assets/_new/styles/tokens.css
Normal file
122
webapp/assets/_new/styles/tokens.css
Normal file
@ -0,0 +1,122 @@
|
||||
/**
|
||||
* @tokens Color Brand
|
||||
* @presenter Color
|
||||
*/
|
||||
/**
|
||||
* @tokens Color Neutral
|
||||
* @presenter Color
|
||||
*/
|
||||
/**
|
||||
* @tokens Color Text
|
||||
* @presenter Color
|
||||
*/
|
||||
/**
|
||||
* @tokens Color Background
|
||||
* @presenter Color
|
||||
*/
|
||||
/**
|
||||
* @tokens Color Border
|
||||
* @presenter Color
|
||||
*/
|
||||
/**
|
||||
* @tokens Border Size
|
||||
* @presenter Border
|
||||
*/
|
||||
/**
|
||||
* @tokens Border Radius
|
||||
* @presenter BorderRadius
|
||||
*/
|
||||
/**
|
||||
* @tokens Font Size
|
||||
* @presenter FontSize
|
||||
*/
|
||||
/**
|
||||
* @tokens Font Space
|
||||
* @presenter Spacing
|
||||
*/
|
||||
/**
|
||||
* @tokens Font Family
|
||||
* @presenter FontFamily
|
||||
*/
|
||||
/**
|
||||
* @tokens Font Weight
|
||||
* @presenter FontWeight
|
||||
*/
|
||||
/**
|
||||
* @tokens Line Height
|
||||
* @presenter LineHeight
|
||||
*/
|
||||
/**
|
||||
* @tokens Letter Spacing
|
||||
* @presenter Spacing
|
||||
*/
|
||||
/**
|
||||
* @tokens Opacity
|
||||
* @presenter Opacity
|
||||
*/
|
||||
/**
|
||||
* @tokens Space
|
||||
* @presenter Spacing
|
||||
*/
|
||||
/**
|
||||
* @tokens Size Height
|
||||
* @presenter Spacing
|
||||
*/
|
||||
/**
|
||||
* @tokens Size Width
|
||||
* @presenter Spacing
|
||||
*/
|
||||
/**
|
||||
* @tokens Size Avatar
|
||||
* @presenter Spacing
|
||||
*/
|
||||
/**
|
||||
* @tokens Size Buttons
|
||||
* @presenter Spacing
|
||||
*/
|
||||
/**
|
||||
* @tokens Size Images
|
||||
* @presenter Spacing
|
||||
*/
|
||||
/**
|
||||
* @tokens Size Icons
|
||||
* @presenter Spacing
|
||||
*/
|
||||
/**
|
||||
* @tokens Shadow
|
||||
* @presenter Shadow
|
||||
*/
|
||||
/**
|
||||
* @tokens Effects
|
||||
*/
|
||||
/**
|
||||
* @tokens Animation Duration
|
||||
*/
|
||||
/**
|
||||
* @tokens Animation Ease
|
||||
* @presenter Easing
|
||||
*/
|
||||
/**
|
||||
* @tokens Z-Index
|
||||
*/
|
||||
/**
|
||||
* @tokens Media Query
|
||||
*/
|
||||
/**
|
||||
* @tokens Background Images
|
||||
*/
|
||||
/**
|
||||
* @tokens Header Color
|
||||
*/
|
||||
/**
|
||||
* @tokens Footer Color
|
||||
*/
|
||||
/**
|
||||
* @tokens Locale Menu Color
|
||||
*/
|
||||
/**
|
||||
* @tokens Donation Bar Color
|
||||
*/
|
||||
/**
|
||||
* @tokens Toast Color
|
||||
*//*# sourceMappingURL=tokens.css.map */
|
||||
1
webapp/assets/_new/styles/tokens.css.map
Normal file
1
webapp/assets/_new/styles/tokens.css.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"sources":["tokens.scss"],"names":[],"mappings":"AAAA;;;EAAA;AA4BA;;;EAAA;AAmBA;;;EAAA;AAuBA;;;EAAA;AAgCA;;;EAAA;AAeA;;;EAAA;AASA;;;EAAA;AAYA;;;EAAA;AAgBA;;;EAAA;AAgBA;;;EAAA;AAUA;;;EAAA;AAQA;;;EAAA;AAUA;;;EAAA;AAWA;;;EAAA;AASA;;;EAAA;AAgBA;;;EAAA;AAaA;;;EAAA;AASA;;;EAAA;AASA;;;EAAA;AASA;;;EAAA;AAUA;;;EAAA;AAQA;;;EAAA;AAcA;;EAAA;AAMA;;EAAA;AAUA;;;EAAA;AAWA;;EAAA;AAaA;;EAAA;AAUA;;EAAA;AAIA;;EAAA;AAMA;;EAAA;AAOA;;EAAA;AAMA;;EAAA;AAOA;;EAAA","file":"tokens.css"}
|
||||
@ -336,10 +336,12 @@ $ease-in-sharp: cubic-bezier(0.895, 0.03, 0.685, 0.22);
|
||||
*/
|
||||
|
||||
$z-index-modal: 9999;
|
||||
$z-index-overlay: 9000;
|
||||
$z-index-dropdown: 8888;
|
||||
$z-index-page-submenu: 2500;
|
||||
$z-index-page-header: 2000;
|
||||
$z-index-page-sidebar: 1500;
|
||||
$z-index-sticky-float: 150;
|
||||
$z-index-sticky: 100;
|
||||
$z-index-post-teaser-link: 5;
|
||||
$z-index-surface: 1;
|
||||
|
||||
@ -95,7 +95,7 @@ blockquote {
|
||||
box-shadow: $box-shadow-base;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
z-index: $z-index-page-submenu;
|
||||
|
||||
a {
|
||||
outline: none;
|
||||
@ -131,7 +131,7 @@ hr {
|
||||
left: 0;
|
||||
position: fixed;
|
||||
background: rgba(0, 0, 0, 0.15);
|
||||
z-index: 99;
|
||||
z-index: $z-index-overlay;
|
||||
pointer-events: none;
|
||||
transition: opacity 150ms ease-out;
|
||||
transition-delay: 50ms;
|
||||
|
||||
@ -43,6 +43,14 @@ export default {
|
||||
selectedCategoryIds: this.existingCategoryIds,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
existingCategoryIds() {
|
||||
if (!this.selectedCategoryIds.length && this.existingCategoryIds.length) {
|
||||
this.selectedCategoryIds = this.existingCategoryIds
|
||||
this.$forceUpdate()
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
selectedCount() {
|
||||
return this.selectedCategoryIds.length
|
||||
|
||||
@ -80,7 +80,7 @@ export default {
|
||||
})
|
||||
}
|
||||
|
||||
if (this.isAdmin) {
|
||||
if (this.isAdmin && !this.resource.group) {
|
||||
if (!this.resource.pinnedBy) {
|
||||
routes.push({
|
||||
label: this.$t(`post.menu.pin`),
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
<base-icon name="question-circle" />
|
||||
</page-params-link>
|
||||
</div>
|
||||
<ds-space margin-top="base" />
|
||||
<ds-input
|
||||
model="title"
|
||||
:placeholder="$t('contribution.title')"
|
||||
@ -51,6 +52,90 @@
|
||||
{{ contentLength }}
|
||||
<base-icon v-if="errors && errors.content" name="warning" />
|
||||
</ds-chip>
|
||||
|
||||
<!-- Eventdata -->
|
||||
<div v-if="creatEvent" class="eventDatas">
|
||||
<hr />
|
||||
<ds-space margin-top="x-small" />
|
||||
<ds-grid>
|
||||
<ds-grid-item style="grid-row-end: span 3">
|
||||
<!-- <label>Beginn</label> -->
|
||||
<div style="z-index: 20">
|
||||
<date-picker
|
||||
name="eventStart"
|
||||
v-model="formData.eventStart"
|
||||
type="datetime"
|
||||
value-type="format"
|
||||
:minute-step="15"
|
||||
formmat="DD-MM-YYYY HH:mm"
|
||||
style="z-index: 20"
|
||||
:placeholder="$t('post.viewEvent.eventStart')"
|
||||
:disabled-date="notBeforeToday"
|
||||
:show-second="false"
|
||||
></date-picker>
|
||||
</div>
|
||||
<div class="chipbox" style="margin-top: 10px">
|
||||
<ds-chip size="base" :color="errors && errors.eventStart && 'danger'">
|
||||
<base-icon v-if="errors && errors.eventStart" name="warning" />
|
||||
</ds-chip>
|
||||
</div>
|
||||
</ds-grid-item>
|
||||
<ds-grid-item style="grid-row-end: span 3">
|
||||
<!-- <label>Ende (optional)</label> -->
|
||||
|
||||
<date-picker
|
||||
v-model="formData.eventEnd"
|
||||
type="datetime"
|
||||
:minute-step="15"
|
||||
:seconds-step="0"
|
||||
formmat="DD MM YYYY HH:mm"
|
||||
:placeholder="$t('post.viewEvent.eventEnd')"
|
||||
style="font-size: larger"
|
||||
:disabled-date="notBeforeToday"
|
||||
:show-second="false"
|
||||
></date-picker>
|
||||
</ds-grid-item>
|
||||
</ds-grid>
|
||||
<ds-grid>
|
||||
<ds-grid-item style="grid-row-end: span 3">
|
||||
<ds-input
|
||||
model="eventVenue"
|
||||
name="location"
|
||||
:placeholder="$t('post.viewEvent.eventVenue')"
|
||||
/>
|
||||
<div class="chipbox">
|
||||
<ds-chip size="base" :color="errors && errors.eventVenue && 'danger'">
|
||||
{{ formData.eventVenue.length }}/{{ formSchema.eventVenue.max }}
|
||||
<base-icon v-if="errors && errors.eventVenue" name="warning" />
|
||||
</ds-chip>
|
||||
</div>
|
||||
</ds-grid-item>
|
||||
<ds-grid-item style="grid-row-end: span 3">
|
||||
<ds-input
|
||||
model="eventLocationName"
|
||||
name="venue"
|
||||
:placeholder="$t('post.viewEvent.eventLocationName')"
|
||||
/>
|
||||
<div class="chipbox">
|
||||
<ds-chip size="base" :color="errors && errors.eventLocationName && 'danger'">
|
||||
{{ formData.eventLocationName.length }}/{{ formSchema.eventLocationName.max }}
|
||||
<base-icon v-if="errors && errors.eventLocationName" name="warning" />
|
||||
</ds-chip>
|
||||
</div>
|
||||
</ds-grid-item>
|
||||
</ds-grid>
|
||||
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
model="formData.eventIsOnline"
|
||||
name="eventIsOnline"
|
||||
style="font-size: larger"
|
||||
/>
|
||||
{{ $t('post.viewEvent.eventIsOnline') }}
|
||||
</div>
|
||||
</div>
|
||||
<ds-space margin-top="x-small" />
|
||||
<categories-select
|
||||
v-if="categoriesActive"
|
||||
model="categoryIds"
|
||||
@ -67,6 +152,7 @@
|
||||
<ds-flex class="buttons-footer" gutter="xxx-small">
|
||||
<ds-flex-item width="3.5" style="margin-right: 16px; margin-bottom: 6px">
|
||||
<!-- eslint-disable vue/no-v-text-v-html-on-component -->
|
||||
<!-- TODO => remove v-html! only text ! no html! secrurity first! -->
|
||||
<ds-text
|
||||
v-if="showGroupHint"
|
||||
v-html="$t('contribution.visibleOnlyForMembersOfGroup', { name: groupName })"
|
||||
@ -92,7 +178,6 @@
|
||||
</template>
|
||||
</ds-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import gql from 'graphql-tag'
|
||||
import { mapGetters } from 'vuex'
|
||||
@ -102,6 +187,8 @@ import CategoriesSelect from '~/components/CategoriesSelect/CategoriesSelect'
|
||||
import ImageUploader from '~/components/Uploader/ImageUploader'
|
||||
import links from '~/constants/links.js'
|
||||
import PageParamsLink from '~/components/_new/features/PageParamsLink/PageParamsLink.vue'
|
||||
import DatePicker from 'vue2-datepicker'
|
||||
import 'vue2-datepicker/scss/index.scss'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -109,6 +196,7 @@ export default {
|
||||
ImageUploader,
|
||||
PageParamsLink,
|
||||
CategoriesSelect,
|
||||
DatePicker,
|
||||
},
|
||||
props: {
|
||||
contribution: {
|
||||
@ -119,15 +207,30 @@ export default {
|
||||
type: Object,
|
||||
default: () => null,
|
||||
},
|
||||
creatEvent: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
const { title, content, image, categories } = this.contribution
|
||||
const {
|
||||
title,
|
||||
content,
|
||||
image,
|
||||
categories,
|
||||
eventStart,
|
||||
eventEnd,
|
||||
eventLocationName,
|
||||
eventVenue,
|
||||
eventIsOnline,
|
||||
eventLocation,
|
||||
} = this.contribution
|
||||
const {
|
||||
sensitive: imageBlurred = false,
|
||||
aspectRatio: imageAspectRatio = null,
|
||||
type: imageType = null,
|
||||
} = image || {}
|
||||
|
||||
return {
|
||||
categoriesActive: this.$env.CATEGORIES_ACTIVE,
|
||||
links,
|
||||
@ -139,6 +242,12 @@ export default {
|
||||
imageType,
|
||||
imageBlurred,
|
||||
categoryIds: categories ? categories.map((category) => category.id) : [],
|
||||
eventStart: eventStart || null,
|
||||
eventEnd: eventEnd || null,
|
||||
eventLocation: eventLocation || '',
|
||||
eventLocationName: eventLocationName || '',
|
||||
eventVenue: eventVenue || '',
|
||||
eventIsOnline: eventIsOnline || false,
|
||||
},
|
||||
formSchema: {
|
||||
title: { required: true, min: 3, max: 100 },
|
||||
@ -154,6 +263,9 @@ export default {
|
||||
return []
|
||||
},
|
||||
},
|
||||
eventStart: { required: !!this.creatEvent },
|
||||
eventVenue: { required: !!this.creatEvent, min: 3, max: 100 },
|
||||
eventLocationName: { required: !!this.creatEvent, min: 3, max: 100 },
|
||||
},
|
||||
loading: false,
|
||||
users: [],
|
||||
@ -161,10 +273,25 @@ export default {
|
||||
imageUpload: null,
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
await import(`vue2-datepicker/locale/${this.currentUser.locale}`)
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
currentUser: 'auth/user',
|
||||
}),
|
||||
eventInput() {
|
||||
if (this.creatEvent) {
|
||||
return {
|
||||
eventStart: this.formData.eventStart,
|
||||
eventVenue: this.formData.eventVenue,
|
||||
eventEnd: this.formData.eventEnd,
|
||||
eventIsOnline: this.formData.eventIsOnline,
|
||||
eventLocationName: this.formData.eventLocationName
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
},
|
||||
contentLength() {
|
||||
return this.$filters.removeHtml(this.formData.content).length
|
||||
},
|
||||
@ -177,10 +304,23 @@ export default {
|
||||
groupName() {
|
||||
return this.group && this.group.name
|
||||
},
|
||||
groupCategories() {
|
||||
return this.group && this.group.categories
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
groupCategories() {
|
||||
if (!this.formData.categoryIds.length && this.groupCategories)
|
||||
this.formData.categoryIds = this.groupCategories.map((cat) => cat.id)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
notBeforeToday(date) {
|
||||
return date < new Date(new Date().setHours(0, 0, 0, 0))
|
||||
},
|
||||
submit() {
|
||||
let image = null
|
||||
|
||||
const { title, content, categoryIds } = this.formData
|
||||
if (this.formData.image) {
|
||||
image = {
|
||||
@ -193,6 +333,7 @@ export default {
|
||||
}
|
||||
}
|
||||
this.loading = true
|
||||
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: this.contribution.id ? PostMutations().UpdatePost : PostMutations().CreatePost,
|
||||
@ -203,6 +344,8 @@ export default {
|
||||
id: this.contribution.id || null,
|
||||
image,
|
||||
groupId: this.groupId,
|
||||
postType: !this.creatEvent ? 'Article' : 'Event',
|
||||
eventInput: this.eventInput,
|
||||
},
|
||||
})
|
||||
.then(({ data }) => {
|
||||
@ -279,6 +422,17 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.eventDatas {
|
||||
.chipbox {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
> .ds-chip {
|
||||
margin-top: -10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.contribution-form > .base-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -360,5 +514,27 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx-datepicker {
|
||||
width: 100%;
|
||||
}
|
||||
.mx-datepicker input {
|
||||
font-size: 1rem;
|
||||
height: calc(1.625rem + 18px);
|
||||
padding: 8px 8px;
|
||||
background-color: #faf9fa;
|
||||
border-color: #c8c8c8;
|
||||
color: #4b4554;
|
||||
}
|
||||
.mx-datepicker input:hover {
|
||||
border-color: #c8c8c8;
|
||||
}
|
||||
.mx-datepicker input:focus {
|
||||
border-color: #17b53f;
|
||||
background-color: #fff;
|
||||
}
|
||||
.mx-datepicker-error {
|
||||
border-color: #cf2619;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -17,18 +17,22 @@
|
||||
</template>
|
||||
|
||||
<template #filter-list>
|
||||
<li v-for="category in categories" :key="category.id" class="item item-category">
|
||||
<labeled-button
|
||||
:icon="category.icon"
|
||||
:filled="filteredCategoryIds.includes(category.id)"
|
||||
:label="$t(`contribution.category.name.${category.slug}`)"
|
||||
<div class="category-filter-list">
|
||||
<base-button
|
||||
v-for="category in categories"
|
||||
:key="category.id"
|
||||
@click="toggleCategory(category.id)"
|
||||
:filled="filteredCategoryIds.includes(category.id)"
|
||||
:icon="category.icon"
|
||||
size="small"
|
||||
v-tooltip="{
|
||||
content: $t(`contribution.category.description.${category.slug}`),
|
||||
placement: 'bottom-start',
|
||||
}"
|
||||
/>
|
||||
</li>
|
||||
>
|
||||
{{ $t(`contribution.category.name.${category.slug}`) }}
|
||||
</base-button>
|
||||
</div>
|
||||
</template>
|
||||
</filter-menu-section>
|
||||
</template>
|
||||
@ -95,3 +99,13 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.category-filter-list {
|
||||
margin-left: $space-xx-large;
|
||||
|
||||
> .base-button {
|
||||
margin-right: $space-xx-small;
|
||||
margin-bottom: $space-xx-small;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
<div class="filter-menu-options">
|
||||
<h2 class="title">{{ $t('filter-menu.filter-by') }}</h2>
|
||||
<following-filter />
|
||||
<post-type-filter />
|
||||
<categories-filter v-if="categoriesActive" @showFilterMenu="$emit('showFilterMenu')" />
|
||||
</div>
|
||||
<div class="filter-menu-options">
|
||||
@ -13,6 +14,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PostTypeFilter from './PostTypeFilter'
|
||||
import FollowingFilter from './FollowingFilter'
|
||||
import OrderByFilter from './OrderByFilter'
|
||||
import CategoriesFilter from './CategoriesFilter'
|
||||
@ -22,6 +24,7 @@ export default {
|
||||
FollowingFilter,
|
||||
OrderByFilter,
|
||||
CategoriesFilter,
|
||||
PostTypeFilter,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
59
webapp/components/FilterMenu/PostTypeFilter.vue
Normal file
59
webapp/components/FilterMenu/PostTypeFilter.vue
Normal file
@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<filter-menu-section
|
||||
:title="$t('filter-menu.post-type')"
|
||||
:divider="false"
|
||||
class="following-filter"
|
||||
>
|
||||
<template #filter-follower>
|
||||
<li class="item article-item">
|
||||
<labeled-button
|
||||
icon="book"
|
||||
:label="$t('filter-menu.article')"
|
||||
:filled="articleSet"
|
||||
:title="$t('filter-menu.article')"
|
||||
@click="toggleFilterPostType('Article')"
|
||||
/>
|
||||
</li>
|
||||
<li class="item event-item">
|
||||
<labeled-button
|
||||
icon="calendar"
|
||||
:label="$t('filter-menu.events')"
|
||||
:filled="eventSet"
|
||||
:title="$t('filter-menu.events')"
|
||||
@click="toggleFilterPostType('Event')"
|
||||
/>
|
||||
</li>
|
||||
</template>
|
||||
</filter-menu-section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapMutations } from 'vuex'
|
||||
import FilterMenuSection from '~/components/FilterMenu/FilterMenuSection'
|
||||
import LabeledButton from '~/components/_new/generic/LabeledButton/LabeledButton'
|
||||
|
||||
export default {
|
||||
name: 'PostTypeFilter',
|
||||
components: {
|
||||
FilterMenuSection,
|
||||
LabeledButton,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
filteredPostTypes: 'posts/filteredPostTypes',
|
||||
currentUser: 'auth/user',
|
||||
}),
|
||||
articleSet() {
|
||||
return this.filteredPostTypes.includes('Article')
|
||||
},
|
||||
eventSet() {
|
||||
return this.filteredPostTypes.includes('Event')
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapMutations({
|
||||
toggleFilterPostType: 'posts/TOGGLE_POST_TYPE',
|
||||
}),
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -37,8 +37,6 @@
|
||||
<ds-text class="select-label">
|
||||
{{ $t('group.type') }}
|
||||
</ds-text>
|
||||
<!-- TODO: change it has to be implemented later -->
|
||||
<!-- TODO: move 'ds-select' from style guide to main code and implement missing translation etc. functionality -->
|
||||
<select
|
||||
class="select ds-input appearance--auto"
|
||||
name="groupType"
|
||||
@ -94,21 +92,10 @@
|
||||
<ds-text class="select-label">
|
||||
{{ $t('group.actionRadius') }}
|
||||
</ds-text>
|
||||
<!-- TODO: move 'ds-select' from styleguide to main code and implement missing translation etc. functionality -->
|
||||
<select
|
||||
class="select ds-input appearance--auto"
|
||||
name="actionRadius"
|
||||
:value="formData.actionRadius"
|
||||
@change="changeActionRadius($event)"
|
||||
>
|
||||
<option
|
||||
v-for="actionRadius in actionRadiusOptions"
|
||||
:key="actionRadius"
|
||||
:value="actionRadius"
|
||||
>
|
||||
{{ $t(`group.actionRadii.${actionRadius}`) }}
|
||||
</option>
|
||||
</select>
|
||||
<action-radius-select
|
||||
v-model="formData.actionRadius"
|
||||
@change.native="changeActionRadius($event)"
|
||||
/>
|
||||
<ds-chip
|
||||
size="base"
|
||||
:color="
|
||||
@ -123,6 +110,7 @@
|
||||
</ds-chip>
|
||||
|
||||
<!-- location -->
|
||||
<!-- TODO: move 'ds-select' from styleguide to main code and implement missing translation etc. functionality -->
|
||||
<ds-select
|
||||
id="city"
|
||||
:label="$t('settings.data.labelCity') + locationNameLabelAddOnOldName"
|
||||
@ -187,6 +175,7 @@ import {
|
||||
DESCRIPTION_WITHOUT_HTML_LENGTH_MIN,
|
||||
} from '~/constants/groups.js'
|
||||
import Editor from '~/components/Editor/Editor'
|
||||
import ActionRadiusSelect from '~/components/Select/ActionRadiusSelect'
|
||||
import { queryLocations } from '~/graphql/location'
|
||||
|
||||
let timeout
|
||||
@ -196,6 +185,7 @@ export default {
|
||||
components: {
|
||||
CategoriesSelect,
|
||||
Editor,
|
||||
ActionRadiusSelect,
|
||||
},
|
||||
props: {
|
||||
update: {
|
||||
@ -216,7 +206,6 @@ export default {
|
||||
categoriesActive: this.$env.CATEGORIES_ACTIVE,
|
||||
disabled: false,
|
||||
groupTypeOptions: ['public', 'closed', 'hidden'],
|
||||
actionRadiusOptions: ['regional', 'national', 'continental', 'global'],
|
||||
loadingGeo: false,
|
||||
cities: [],
|
||||
formData: {
|
||||
@ -327,10 +316,10 @@ export default {
|
||||
return false
|
||||
},
|
||||
changeGroupType(event) {
|
||||
this.formData.groupType = event.target.value
|
||||
this.$refs.groupForm.update('groupType', event.target.value)
|
||||
},
|
||||
changeActionRadius(event) {
|
||||
this.formData.actionRadius = event.target.value
|
||||
this.$refs.groupForm.update('actionRadius', event.target.value)
|
||||
},
|
||||
updateEditorDescription(value) {
|
||||
this.$refs.groupForm.update('description', value)
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
params: { id: scope.row.id, slug: scope.row.slug },
|
||||
}"
|
||||
>
|
||||
<ds-avatar online size="small" :name="scope.row.name"></ds-avatar>
|
||||
<profile-avatar :profile="scope.row" size="small" />
|
||||
</nuxt-link>
|
||||
</template>
|
||||
<template #name="scope">
|
||||
@ -80,9 +80,13 @@
|
||||
</template>
|
||||
<script>
|
||||
import { changeGroupMemberRoleMutation, removeUserFromGroupMutation } from '~/graphql/groups.js'
|
||||
import ProfileAvatar from '~/components/_new/generic/ProfileAvatar/ProfileAvatar'
|
||||
|
||||
export default {
|
||||
name: 'GroupMember',
|
||||
components: {
|
||||
ProfileAvatar,
|
||||
},
|
||||
props: {
|
||||
groupId: {
|
||||
type: String,
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
}"
|
||||
:highlight="isPinned"
|
||||
>
|
||||
<!-- {{ post }} -->
|
||||
<template v-if="post.image" #heroImage>
|
||||
<img :src="post.image | proxyApiUrl" class="image" />
|
||||
</template>
|
||||
@ -19,11 +20,37 @@
|
||||
<user-teaser :user="post.author" :group="post.group" :date-time="post.createdAt" />
|
||||
<hc-ribbon
|
||||
:class="[isPinned ? '--pinned' : '', post.image ? 'post-ribbon-w-img' : 'post-ribbon']"
|
||||
:text="isPinned ? $t('post.pinned') : $t('post.name')"
|
||||
:text="ribbonText"
|
||||
:typ="post.postType[0]"
|
||||
/>
|
||||
</div>
|
||||
</client-only>
|
||||
<h2 class="title hyphenate-text">{{ post.title }}</h2>
|
||||
<ds-space
|
||||
v-if="post && post.postType[0] === 'Event'"
|
||||
margin-bottom="small"
|
||||
style="padding: 5px"
|
||||
>
|
||||
<ds-flex>
|
||||
<ds-flex-item>
|
||||
<ds-text align="left" size="small" color="soft" class="event-info">
|
||||
<base-icon name="map-marker" data-test="map-marker" />
|
||||
<span v-if="post.eventIsOnline">
|
||||
{{ $t('post.viewEvent.eventIsOnline') }}
|
||||
</span>
|
||||
<span v-else-if="post.eventLocationName">
|
||||
{{ post.eventLocationName }}
|
||||
</span>
|
||||
</ds-text>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item>
|
||||
<ds-text align="left" color="soft" size="small" class="event-info">
|
||||
<base-icon name="calendar" data-test="calendar" />
|
||||
<span>{{ getEventDateString }}</span>
|
||||
</ds-text>
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
</ds-space>
|
||||
<!-- TODO: replace editor content with tiptap render view -->
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<div class="content hyphenate-text" v-html="excerpt" />
|
||||
@ -91,6 +118,7 @@ import UserTeaser from '~/components/UserTeaser/UserTeaser'
|
||||
import { mapGetters } from 'vuex'
|
||||
import PostMutations from '~/graphql/PostMutations'
|
||||
import { postMenuModalsData, deletePostMutation } from '~/components/utils/PostHelpers'
|
||||
import { format } from 'date-fns'
|
||||
|
||||
export default {
|
||||
name: 'PostTeaser',
|
||||
@ -152,6 +180,20 @@ export default {
|
||||
isPinned() {
|
||||
return this.post && this.post.pinned
|
||||
},
|
||||
ribbonText() {
|
||||
if (this.post.pinned) return this.$t('post.pinned')
|
||||
if (this.post.postType[0] === 'Event') return this.$t('post.event')
|
||||
return this.$t('post.name')
|
||||
},
|
||||
getEventDateString() {
|
||||
if (this.post.eventEnd) {
|
||||
const eventStart = format(new Date(this.post.eventStart), 'dd.MM.')
|
||||
const eventEnd = format(new Date(this.post.eventEnd), 'dd.MM.yyyy')
|
||||
return `${eventStart} - ${eventEnd}`
|
||||
} else {
|
||||
return format(new Date(this.post.eventStart), 'dd.MM.yyyy')
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async deletePostCallback() {
|
||||
@ -235,6 +277,12 @@ export default {
|
||||
margin-bottom: $space-small;
|
||||
}
|
||||
|
||||
& .event-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
> .footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<aside class="ribbon">
|
||||
<aside class="ribbon" :class="typ === 'Event' ? 'eventBg' : ''">
|
||||
<p>{{ text }}</p>
|
||||
</aside>
|
||||
</template>
|
||||
@ -12,6 +12,10 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
typ: {
|
||||
type: String,
|
||||
default: 'blue',
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -43,4 +47,11 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
.eventBg {
|
||||
background-color: $color-success-active;
|
||||
|
||||
&::before {
|
||||
border-color: $color-success-active transparent transparent $color-success-active;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
37
webapp/components/Select/ActionRadiusSelect.spec.js
Normal file
37
webapp/components/Select/ActionRadiusSelect.spec.js
Normal file
@ -0,0 +1,37 @@
|
||||
import { shallowMount } from '@vue/test-utils'
|
||||
import ActionRadiusSelect from './ActionRadiusSelect'
|
||||
|
||||
const localVue = global.localVue
|
||||
const propsData = { value: 'regional' }
|
||||
|
||||
describe('ActionRadiusSelect.', () => {
|
||||
let wrapper
|
||||
let mocks
|
||||
|
||||
beforeEach(() => {
|
||||
mocks = {
|
||||
$t: jest.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
describe('mount', () => {
|
||||
const Wrapper = () => {
|
||||
return shallowMount(ActionRadiusSelect, { propsData, mocks, localVue })
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
it('renders the select', () => {
|
||||
expect(wrapper.findComponent(ActionRadiusSelect).exists()).toBe(true)
|
||||
})
|
||||
|
||||
describe('when an option is selected', () => {
|
||||
it('emits a change event with the new value', () => {
|
||||
const select = wrapper.find('select')
|
||||
select.trigger('change')
|
||||
expect(wrapper.emitted().change[0]).toEqual(['regional'])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
33
webapp/components/Select/ActionRadiusSelect.vue
Normal file
33
webapp/components/Select/ActionRadiusSelect.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<select
|
||||
class="select ds-input appearance--auto"
|
||||
name="actionRadius"
|
||||
:value="value"
|
||||
@change="onActionRadiusChange"
|
||||
>
|
||||
<option v-for="actionRadius in actionRadiusOptions" :key="actionRadius" :value="actionRadius">
|
||||
{{ $t(`group.actionRadii.${actionRadius}`) }}
|
||||
</option>
|
||||
</select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ActionRadiusSelect',
|
||||
props: {
|
||||
value: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
actionRadiusOptions: ['regional', 'national', 'continental', 'global'],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onActionRadiusChange(event) {
|
||||
this.$emit('change', event.target.value)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -63,13 +63,6 @@ describe('SearchableInput.vue', () => {
|
||||
expect(select.element.value).toBe('abcd')
|
||||
})
|
||||
|
||||
it('calls onDelete when the delete key is pressed', () => {
|
||||
const spy = jest.spyOn(wrapper.vm, 'onDelete')
|
||||
select.trigger('input')
|
||||
select.trigger('keyup.delete')
|
||||
expect(spy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
describe('navigating to resource', () => {
|
||||
beforeEach(() => {
|
||||
propsData = { options: searchResults }
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
<template>
|
||||
<div class="searchable-input" aria-label="search" role="search">
|
||||
<ds-select
|
||||
ref="select"
|
||||
type="search"
|
||||
icon="search"
|
||||
v-model="searchValue"
|
||||
v-model="value"
|
||||
:id="id"
|
||||
label-prop="id"
|
||||
:icon-right="null"
|
||||
@ -11,12 +12,11 @@
|
||||
:loading="loading"
|
||||
:filter="(item) => item"
|
||||
:no-options-available="emptyText"
|
||||
:auto-reset-search="!searchValue"
|
||||
:auto-reset-search="!value"
|
||||
:placeholder="$t('search.placeholder')"
|
||||
@focus.capture.native="onFocus"
|
||||
@input.native="handleInput"
|
||||
@input.native="onInput"
|
||||
@keyup.enter.native="onEnter"
|
||||
@keyup.delete.native="onDelete"
|
||||
@keyup.esc.native="clear"
|
||||
@blur.capture.native="onBlur"
|
||||
@input.exact="onSelect"
|
||||
@ -77,23 +77,29 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchValue: '',
|
||||
value: '',
|
||||
unprocessedSearchInput: '',
|
||||
searchProcess: null,
|
||||
previousSearchTerm: '',
|
||||
delay: 300,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
emptyText() {
|
||||
return this.isActive && !this.loading ? this.$t('search.failed') : this.$t('search.hint')
|
||||
return !this.loading && this.isSearchable()
|
||||
? this.$t('search.failed')
|
||||
: this.$t('search.hint')
|
||||
},
|
||||
isActive() {
|
||||
return !isEmpty(this.previousSearchTerm)
|
||||
return !isEmpty(this.value)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isSearchable() {
|
||||
return (
|
||||
!isEmpty(this.value) &&
|
||||
typeof this.value === 'string' &&
|
||||
this.value.replace(/\s+/g, '').length >= 3
|
||||
)
|
||||
},
|
||||
isFirstOfType(option) {
|
||||
return (
|
||||
this.options.findIndex((o) => o === option) ===
|
||||
@ -103,49 +109,36 @@ export default {
|
||||
onFocus(event) {
|
||||
clearTimeout(this.searchProcess)
|
||||
},
|
||||
handleInput(event) {
|
||||
onInput(event) {
|
||||
clearTimeout(this.searchProcess)
|
||||
this.value = event.target ? event.target.value.replace(/\s+/g, ' ').trim() : ''
|
||||
this.unprocessedSearchInput = this.value
|
||||
if (isEmpty(this.value) || this.value.replace(/\s+/g, '').length < 3) {
|
||||
if (!this.isSearchable()) {
|
||||
this.$emit('clearSearch')
|
||||
return
|
||||
}
|
||||
this.searchProcess = setTimeout(() => {
|
||||
this.previousSearchTerm = this.value
|
||||
this.$emit('query', this.value)
|
||||
}, this.delay)
|
||||
},
|
||||
onEnter(event) {
|
||||
this.$router.push({
|
||||
path: '/search/search-results',
|
||||
query: { search: this.unprocessedSearchInput },
|
||||
query: { search: this.value },
|
||||
})
|
||||
this.$emit('clearSearch')
|
||||
},
|
||||
onDelete(event) {
|
||||
clearTimeout(this.searchProcess)
|
||||
const value = event.target ? event.target.value.trim() : ''
|
||||
if (isEmpty(value)) {
|
||||
this.clear()
|
||||
} else {
|
||||
this.handleInput(event)
|
||||
}
|
||||
this.$refs.select.close()
|
||||
},
|
||||
clear() {
|
||||
this.unprocessedSearchInput = ''
|
||||
this.previousSearchTerm = ''
|
||||
this.searchValue = ''
|
||||
this.value = ''
|
||||
this.$emit('clearSearch')
|
||||
clearTimeout(this.searchProcess)
|
||||
},
|
||||
onBlur(event) {
|
||||
this.searchValue = this.previousSearchTerm
|
||||
clearTimeout(this.searchProcess)
|
||||
},
|
||||
onSelect(item) {
|
||||
this.goToResource(item)
|
||||
this.$nextTick(() => {
|
||||
this.searchValue = this.previousSearchTerm
|
||||
this.value = this.$refs.select.$data.searchString
|
||||
})
|
||||
},
|
||||
getRouteName(item) {
|
||||
|
||||
@ -4,21 +4,30 @@ export default () => {
|
||||
return {
|
||||
CreatePost: gql`
|
||||
mutation (
|
||||
$id: ID
|
||||
$title: String!
|
||||
$slug: String
|
||||
$content: String!
|
||||
$categoryIds: [ID]
|
||||
$image: ImageInput
|
||||
$groupId: ID
|
||||
$postType: PostType
|
||||
$eventInput: _EventInput
|
||||
) {
|
||||
CreatePost(
|
||||
id: $id
|
||||
title: $title
|
||||
slug: $slug
|
||||
content: $content
|
||||
categoryIds: $categoryIds
|
||||
image: $image
|
||||
groupId: $groupId
|
||||
postType: $postType
|
||||
eventInput: $eventInput
|
||||
) {
|
||||
title
|
||||
id
|
||||
slug
|
||||
title
|
||||
content
|
||||
contentExcerpt
|
||||
language
|
||||
@ -26,6 +35,22 @@ export default () => {
|
||||
url
|
||||
sensitive
|
||||
}
|
||||
disabled
|
||||
deleted
|
||||
postType
|
||||
author {
|
||||
name
|
||||
}
|
||||
categories {
|
||||
id
|
||||
}
|
||||
eventStart
|
||||
eventVenue
|
||||
eventLocationName
|
||||
eventLocation {
|
||||
lng
|
||||
lat
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
||||
@ -24,6 +24,12 @@ export default (i18n) => {
|
||||
|
||||
query Post($id: ID!) {
|
||||
Post(id: $id) {
|
||||
postType
|
||||
eventStart
|
||||
eventEnd
|
||||
eventVenue
|
||||
eventLocationName
|
||||
eventIsOnline
|
||||
...post
|
||||
...postCounts
|
||||
...tagsCategoriesAndPinned
|
||||
@ -66,6 +72,12 @@ export const filterPosts = (i18n) => {
|
||||
|
||||
query Post($filter: _PostFilter, $first: Int, $offset: Int, $orderBy: [_PostOrdering]) {
|
||||
Post(filter: $filter, first: $first, offset: $offset, orderBy: $orderBy) {
|
||||
postType
|
||||
eventStart
|
||||
eventEnd
|
||||
eventVenue
|
||||
eventLocationName
|
||||
eventIsOnline
|
||||
...post
|
||||
...postCounts
|
||||
...tagsCategoriesAndPinned
|
||||
@ -103,6 +115,10 @@ export const profilePagePosts = (i18n) => {
|
||||
$orderBy: [_PostOrdering]
|
||||
) {
|
||||
profilePagePosts(filter: $filter, first: $first, offset: $offset, orderBy: $orderBy) {
|
||||
postType
|
||||
eventStart
|
||||
eventVenue
|
||||
eventLocationName
|
||||
...post
|
||||
...postCounts
|
||||
...tagsCategoriesAndPinned
|
||||
|
||||
@ -207,6 +207,9 @@ export const groupMembersQuery = () => {
|
||||
name
|
||||
slug
|
||||
myRoleInGroup
|
||||
avatar {
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@ -15,7 +15,11 @@ module.exports = {
|
||||
'!**/.prettierrc.js',
|
||||
'!**/nuxt.config.js',
|
||||
],
|
||||
coverageReporters: ['lcov', 'text'],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
lines: 83,
|
||||
},
|
||||
},
|
||||
setupFiles: ['<rootDir>/test/registerContext.js', '<rootDir>/test/testSetup.js'],
|
||||
transform: {
|
||||
'.*\\.(vue)$': 'vue-jest',
|
||||
|
||||
@ -278,6 +278,7 @@
|
||||
"inappropriatePicture": "Dieses Bild kann für einige Menschen unangemessen sein.",
|
||||
"languageSelectLabel": "Sprache Deines Beitrags",
|
||||
"languageSelectText": "Sprache wählen",
|
||||
"newEvent": "Erstelle einen neue Veranstaltung",
|
||||
"newPost": "Erstelle einen neuen Beitrag",
|
||||
"success": "Gespeichert!",
|
||||
"teaserImage": {
|
||||
@ -377,9 +378,11 @@
|
||||
},
|
||||
"filter-menu": {
|
||||
"all": "Alle",
|
||||
"article": "Artikel",
|
||||
"categories": "Themen",
|
||||
"deleteFilter": "Filter löschen",
|
||||
"emotions": "Emotionen",
|
||||
"events": "Veranstaltungen",
|
||||
"filter-by": "Filtern nach ...",
|
||||
"following": "Nutzer denen ich folge",
|
||||
"languages": "Sprachen",
|
||||
@ -395,6 +398,7 @@
|
||||
}
|
||||
},
|
||||
"order-by": "Sortieren nach ...",
|
||||
"post-type": "Beitrags-Typ",
|
||||
"save": {
|
||||
"error": "Themen konnten nicht gespeichert werden!",
|
||||
"success": "Themen gespeichert!"
|
||||
@ -684,6 +688,12 @@
|
||||
"submitted": "Kommentar gesendet",
|
||||
"updated": "Änderungen gespeichert"
|
||||
},
|
||||
"createNewEvent": {
|
||||
"forGroup": {
|
||||
"title": "Für die Gruppe „{name}“"
|
||||
},
|
||||
"title": "Erstelle ein neues Event"
|
||||
},
|
||||
"createNewPost": {
|
||||
"forGroup": {
|
||||
"title": "Für die Gruppe „{name}“"
|
||||
@ -697,6 +707,7 @@
|
||||
},
|
||||
"title": "Bearbeite deinen Beitrag"
|
||||
},
|
||||
"event": "Veranstaltung",
|
||||
"menu": {
|
||||
"delete": "Beitrag löschen",
|
||||
"edit": "Beitrag bearbeiten",
|
||||
@ -710,6 +721,14 @@
|
||||
"takeAction": {
|
||||
"name": "Aktiv werden"
|
||||
},
|
||||
"viewEvent": {
|
||||
"eventEnd": "Ende",
|
||||
"eventIsOnline": "Online Veranstaltung",
|
||||
"eventLocationName": "Stadt",
|
||||
"eventStart": "Beginn",
|
||||
"eventVenue": "Veranstaltungsort",
|
||||
"title": "Veranstaltung"
|
||||
},
|
||||
"viewPost": {
|
||||
"forGroup": {
|
||||
"title": "In der Gruppe „{name}“"
|
||||
|
||||
@ -278,6 +278,7 @@
|
||||
"inappropriatePicture": "This image may be inappropriate for some people.",
|
||||
"languageSelectLabel": "Language of your contribution",
|
||||
"languageSelectText": "Select Language",
|
||||
"newEvent": "Create a new Event",
|
||||
"newPost": "Create a new Post",
|
||||
"success": "Saved!",
|
||||
"teaserImage": {
|
||||
@ -377,9 +378,11 @@
|
||||
},
|
||||
"filter-menu": {
|
||||
"all": "All",
|
||||
"article": "Article",
|
||||
"categories": "Topics",
|
||||
"deleteFilter": "Delete filter",
|
||||
"emotions": "Emotions",
|
||||
"events": "Events",
|
||||
"filter-by": "Filter by ...",
|
||||
"following": "Users I follow",
|
||||
"languages": "Languages",
|
||||
@ -395,6 +398,7 @@
|
||||
}
|
||||
},
|
||||
"order-by": "Order by ...",
|
||||
"post-type": "Post type",
|
||||
"save": {
|
||||
"error": "Failed saving topic settings!",
|
||||
"success": "Topics saved!"
|
||||
@ -684,6 +688,12 @@
|
||||
"submitted": "Comment submitted!",
|
||||
"updated": "Changes saved!"
|
||||
},
|
||||
"createNewEvent": {
|
||||
"forGroup": {
|
||||
"title": "For The Group “{name}”"
|
||||
},
|
||||
"title": "Create A New Event"
|
||||
},
|
||||
"createNewPost": {
|
||||
"forGroup": {
|
||||
"title": "For The Group “{name}”"
|
||||
@ -697,6 +707,7 @@
|
||||
},
|
||||
"title": "Edit Your Post"
|
||||
},
|
||||
"event": "Event",
|
||||
"menu": {
|
||||
"delete": "Delete post",
|
||||
"edit": "Edit post",
|
||||
@ -710,6 +721,14 @@
|
||||
"takeAction": {
|
||||
"name": "Take action"
|
||||
},
|
||||
"viewEvent": {
|
||||
"eventEnd": "End",
|
||||
"eventIsOnline": "Online Event",
|
||||
"eventLocationName": "City",
|
||||
"eventStart": "Start",
|
||||
"eventVenue": "Venue",
|
||||
"title": "Event"
|
||||
},
|
||||
"viewPost": {
|
||||
"forGroup": {
|
||||
"title": "In The Group “{name}”"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ocelot-social/maintenance",
|
||||
"version": "2.5.1",
|
||||
"version": "2.6.0",
|
||||
"description": "Maintenance page for ocelot.social",
|
||||
"repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social",
|
||||
"author": "ocelot.social Community",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ocelot-social-webapp",
|
||||
"version": "2.5.1",
|
||||
"version": "2.6.0",
|
||||
"description": "ocelot.social Frontend",
|
||||
"repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social",
|
||||
"author": "ocelot.social Community",
|
||||
@ -38,7 +38,7 @@
|
||||
"express": "~4.17.1",
|
||||
"graphql": "~14.7.0",
|
||||
"intersection-observer": "^0.12.0",
|
||||
"jsonwebtoken": "~8.5.1",
|
||||
"jsonwebtoken": "~9.0.0",
|
||||
"linkify-it": "~3.0.2",
|
||||
"mapbox-gl": "1.13.2",
|
||||
"node-fetch": "^2.6.1",
|
||||
@ -60,12 +60,13 @@
|
||||
"vue-observe-visibility": "^1.0.0",
|
||||
"vue-scrollto": "^2.20.0",
|
||||
"vue-sweetalert-icons": "~4.3.1",
|
||||
"vue2-datepicker": "^3.11.1",
|
||||
"vuex-i18n": "~1.13.1",
|
||||
"xregexp": "^4.3.0",
|
||||
"zxcvbn": "^4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "~7.12.3",
|
||||
"@babel/core": "~7.21.4",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@faker-js/faker": "5.1.0",
|
||||
@ -81,7 +82,7 @@
|
||||
"async-validator": "^3.2.4",
|
||||
"babel-core": "~7.0.0-bridge.0",
|
||||
"babel-eslint": "~10.1.0",
|
||||
"babel-jest": "29.4",
|
||||
"babel-jest": "29.5",
|
||||
"babel-loader": "~8.1.0",
|
||||
"babel-plugin-require-context-hook": "^1.0.0",
|
||||
"babel-preset-vue": "~2.0.2",
|
||||
@ -100,7 +101,7 @@
|
||||
"eslint-plugin-vue": "~6.2.2",
|
||||
"flush-promises": "^1.0.2",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "29.4",
|
||||
"jest": "29.5",
|
||||
"jest-environment-jsdom": "^29.4.2",
|
||||
"mutation-observer": "^1.0.3",
|
||||
"prettier": "~2.7.1",
|
||||
|
||||
@ -318,7 +318,7 @@ export default {
|
||||
height: 54px;
|
||||
width: 54px;
|
||||
font-size: 26px;
|
||||
z-index: 100;
|
||||
z-index: $z-index-sticky-float;
|
||||
position: fixed;
|
||||
bottom: -5px;
|
||||
left: 98vw;
|
||||
@ -330,7 +330,7 @@ export default {
|
||||
height: 54px;
|
||||
width: 54px;
|
||||
font-size: 26px;
|
||||
z-index: 100;
|
||||
z-index: $z-index-sticky-float;
|
||||
position: fixed;
|
||||
top: 80px;
|
||||
box-shadow: $box-shadow-x-large;
|
||||
@ -344,7 +344,7 @@ export default {
|
||||
.filterButtonMenu {
|
||||
width: 95%;
|
||||
position: fixed;
|
||||
z-index: 6;
|
||||
z-index: $z-index-sticky;
|
||||
margin-top: -35px;
|
||||
padding: 20px 10px 20px 10px;
|
||||
background-color: #f5f4f6;
|
||||
@ -361,6 +361,7 @@ export default {
|
||||
max-height: 950px;
|
||||
overflow: auto;
|
||||
padding-bottom: 0px;
|
||||
z-index: $z-index-page-submenu;
|
||||
}
|
||||
@media screen and (min-height: 401px) {
|
||||
#my-filter {
|
||||
@ -410,7 +411,6 @@ export default {
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
font-size: 23px;
|
||||
z-index: 10;
|
||||
}
|
||||
.ds-grid {
|
||||
padding-top: 1em;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<transition name="fade" appear>
|
||||
<div>
|
||||
<ds-space margin="small">
|
||||
<ds-heading tag="h1">{{ $t('post.viewPost.title') }}</ds-heading>
|
||||
<ds-heading tag="h1">{{ heading }}</ds-heading>
|
||||
<ds-heading v-if="post && post.group" tag="h2">
|
||||
{{ $t('post.viewPost.forGroup.title', { name: post.group.name }) }}
|
||||
</ds-heading>
|
||||
@ -54,6 +54,33 @@
|
||||
</section>
|
||||
<ds-space margin-bottom="small" />
|
||||
<h2 class="title hyphenate-text">{{ post.title }}</h2>
|
||||
<!-- Eventdata -->
|
||||
<ds-space
|
||||
v-if="post && post.postType[0] === 'Event'"
|
||||
margin-bottom="small"
|
||||
style="padding: 10px"
|
||||
>
|
||||
<ds-text align="left" color="soft">
|
||||
<base-icon name="map-marker" data-test="map-marker" />
|
||||
<span v-if="post.eventVenue">{{ post.eventVenue }}</span>
|
||||
<span v-if="!post.eventIsOnline">
|
||||
<span v-if="post.eventVenue">-</span>
|
||||
{{ post.eventLocationName }}
|
||||
</span>
|
||||
<span v-else>
|
||||
<span v-if="post.eventVenue">-</span>
|
||||
{{ $t('post.viewEvent.eventIsOnline') }}
|
||||
</span>
|
||||
</ds-text>
|
||||
<ds-text align="left" color="soft" class="event-info">
|
||||
<base-icon name="calendar" data-test="calendar" />
|
||||
<span>{{ getEventDateString }}</span>
|
||||
</ds-text>
|
||||
<ds-text v-if="getEventTimeString" align="left" color="soft" class="event-info">
|
||||
<base-icon name="clock" data-test="calendar" />
|
||||
<span>{{ getEventTimeString }}</span>
|
||||
</ds-text>
|
||||
</ds-space>
|
||||
<ds-space margin-bottom="small" />
|
||||
<content-viewer class="content hyphenate-text" :content="post.content" />
|
||||
<!-- Categories -->
|
||||
@ -147,6 +174,7 @@ import { groupQuery } from '~/graphql/groups'
|
||||
import PostMutations from '~/graphql/PostMutations'
|
||||
import links from '~/constants/links.js'
|
||||
import SortCategories from '~/mixins/sortCategoriesMixin.js'
|
||||
import { format } from 'date-fns'
|
||||
|
||||
export default {
|
||||
name: 'PostSlug',
|
||||
@ -218,6 +246,10 @@ export default {
|
||||
},
|
||||
]
|
||||
},
|
||||
heading() {
|
||||
if (this.post?.postType[0] === 'Event') return this.$t('post.viewEvent.title')
|
||||
return this.$t('post.viewPost.title')
|
||||
},
|
||||
menuModalsData() {
|
||||
return postMenuModalsData(
|
||||
// "this.post" may not always be defined at the beginning …
|
||||
@ -256,6 +288,27 @@ export default {
|
||||
!this.post.group || (this.group && ['usual', 'admin', 'owner'].includes(this.group.myRole))
|
||||
)
|
||||
},
|
||||
getEventDateString() {
|
||||
if (this.post.eventEnd) {
|
||||
const eventStart = format(new Date(this.post.eventStart), 'dd.MM.')
|
||||
const eventEnd = format(new Date(this.post.eventEnd), 'dd.MM.yyyy')
|
||||
return `${eventStart} - ${eventEnd}`
|
||||
} else {
|
||||
return format(new Date(this.post.eventStart), 'dd.MM.yyyy')
|
||||
}
|
||||
},
|
||||
getEventTimeString() {
|
||||
if (this.post.eventEnd) {
|
||||
const eventStartTime = format(new Date(this.post.eventStart), 'HH:mm')
|
||||
const eventEndTime = format(new Date(this.post.eventEnd), 'HH:mm')
|
||||
/* assumption that if e.g. 00:00 == 00:00 is saved,
|
||||
it's not realistic because they are the default values, so don't show the time info.
|
||||
*/
|
||||
return eventStartTime !== eventEndTime ? `${eventStartTime} - ${eventEndTime}` : ''
|
||||
} else {
|
||||
return format(new Date(this.post.eventStart), 'HH:mm')
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
reply(message) {
|
||||
@ -374,6 +427,12 @@ export default {
|
||||
filter: blur($blur-radius);
|
||||
}
|
||||
|
||||
& .event-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.blur-toggle {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
|
||||
@ -1,15 +1,56 @@
|
||||
<template>
|
||||
<div>
|
||||
<ds-space margin="small">
|
||||
<ds-heading tag="h1">{{ $t('post.createNewPost.title') }}</ds-heading>
|
||||
<ds-heading v-if="group" tag="h2">
|
||||
{{ $t('post.createNewPost.forGroup.title', { name: group.name }) }}
|
||||
</ds-heading>
|
||||
</ds-space>
|
||||
<ds-space margin="large" />
|
||||
<ds-flex :width="{ base: '100%' }">
|
||||
<ds-flex-item :width="{ base: '100%', md: 5 }">
|
||||
<ds-flex gutter="base" :width="{ base: '100%', sm: 1 }">
|
||||
<ds-flex-item>
|
||||
<ds-card :primary="!creatEvent" centered>
|
||||
<div>
|
||||
<ds-button
|
||||
v-if="!creatEvent"
|
||||
ghost
|
||||
fullwidth
|
||||
size="x-large"
|
||||
style="background-color: #ff000000; color: whitesmoke"
|
||||
>
|
||||
{{ $t('post.createNewPost.title') }}
|
||||
</ds-button>
|
||||
<ds-button v-else ghost fullwidth size="x-large" @click="creatEvent = !creatEvent">
|
||||
{{ $t('post.createNewPost.title') }}
|
||||
</ds-button>
|
||||
</div>
|
||||
</ds-card>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item>
|
||||
<ds-card :primary="!!creatEvent" centered>
|
||||
<div>
|
||||
<ds-button
|
||||
ghost
|
||||
fullwidth
|
||||
size="x-large"
|
||||
v-if="creatEvent"
|
||||
hover
|
||||
style="background-color: #ff000000; color: whitesmoke"
|
||||
>
|
||||
{{ $t('post.createNewEvent.title') }}
|
||||
</ds-button>
|
||||
<ds-button ghost fullwidth size="x-large" v-else @click="creatEvent = !creatEvent">
|
||||
{{ $t('post.createNewEvent.title') }}
|
||||
</ds-button>
|
||||
</div>
|
||||
</ds-card>
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
<div v-if="group" style="font-size: 30px; text-align: center">
|
||||
{{ $t('post.createNewPost.forGroup.title', { name: group.name }) }}
|
||||
</div>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '100%', md: 1 }"> </ds-flex-item>
|
||||
</ds-flex>
|
||||
|
||||
<ds-flex :width="{ base: '100%' }" gutter="base">
|
||||
<ds-flex-item :width="{ base: '100%', md: 5 }">
|
||||
<contribution-form :group="group" />
|
||||
<contribution-form :group="group" :creatEvent="creatEvent" />
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '100%', md: 1 }"> </ds-flex-item>
|
||||
</ds-flex>
|
||||
@ -28,6 +69,7 @@ export default {
|
||||
const { groupId = null } = this.$route.query
|
||||
return {
|
||||
groupId,
|
||||
creatEvent: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
@ -63,6 +63,12 @@ export const mutations = {
|
||||
if (isEmpty(get(filter, 'categories_some.id_in'))) delete filter.categories_some
|
||||
state.filter = filter
|
||||
},
|
||||
TOGGLE_POST_TYPE(state, postType) {
|
||||
const filter = clone(state.filter)
|
||||
update(filter, 'postType_in', (postTypes) => xor(postTypes, [postType]))
|
||||
if (isEmpty(get(filter, 'postType_in'))) delete filter.postType_in
|
||||
state.filter = filter
|
||||
},
|
||||
TOGGLE_LANGUAGE(state, languageCode) {
|
||||
const filter = clone(state.filter)
|
||||
update(filter, 'language_in', (languageCodes) => xor(languageCodes, [languageCode]))
|
||||
@ -90,6 +96,9 @@ export const getters = {
|
||||
filteredCategoryIds(state) {
|
||||
return get(state.filter, 'categories_some.id_in') || []
|
||||
},
|
||||
filteredPostTypes(state) {
|
||||
return get(state.filter, 'postType_in') || []
|
||||
},
|
||||
filteredLanguageCodes(state) {
|
||||
return get(state.filter, 'language_in') || []
|
||||
},
|
||||
|
||||
@ -25,6 +25,18 @@ describe('getters', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('filteredPostTypes', () => {
|
||||
it('returns post types if filter is set', () => {
|
||||
state = { filter: { postType_in: ['Article', 'Event'] } }
|
||||
expect(getters.filteredPostTypes(state)).toEqual(['Article', 'Event'])
|
||||
})
|
||||
|
||||
it('returns empty array if post type filter is not set', () => {
|
||||
state = { filter: { author: { followedBy_some: { id: 7 } } } }
|
||||
expect(getters.filteredPostTypes(state)).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('filteredLanguageCodes', () => {
|
||||
it('returns category ids if filter is set', () => {
|
||||
state = { filter: { language_in: ['en', 'de', 'pt'] } }
|
||||
@ -213,6 +225,46 @@ describe('mutations', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('TOGGLE_POST_TYPE', () => {
|
||||
beforeEach(() => {
|
||||
testMutation = (postType) => {
|
||||
mutations.TOGGLE_POST_TYPE(state, postType)
|
||||
return getters.filter(state)
|
||||
}
|
||||
})
|
||||
|
||||
it('creates post type filter if empty', () => {
|
||||
state = { filter: {} }
|
||||
expect(testMutation('Event')).toEqual({ postType_in: ['Event'] })
|
||||
})
|
||||
|
||||
it('adds post type not present', () => {
|
||||
state = { filter: { postType_in: ['Event'] } }
|
||||
expect(testMutation('Article')).toEqual({ postType_in: ['Event', 'Article'] })
|
||||
})
|
||||
|
||||
it('removes category id if present', () => {
|
||||
state = { filter: { postType_in: ['Event', 'Article'] } }
|
||||
const result = testMutation('Event')
|
||||
expect(result).toEqual({ postType_in: ['Article'] })
|
||||
})
|
||||
|
||||
it('removes category filter if empty', () => {
|
||||
state = { filter: { postType_in: ['Event'] } }
|
||||
expect(testMutation('Event')).toEqual({})
|
||||
})
|
||||
|
||||
it('does not get in the way of other filters', () => {
|
||||
state = {
|
||||
filter: {
|
||||
author: { followedBy_some: { id: 7 } },
|
||||
postType_in: ['Event'],
|
||||
},
|
||||
}
|
||||
expect(testMutation('Event')).toEqual({ author: { followedBy_some: { id: 7 } } })
|
||||
})
|
||||
})
|
||||
|
||||
describe('TOGGLE_FILTER_BY_FOLLOWED', () => {
|
||||
beforeEach(() => {
|
||||
testMutation = (userId) => {
|
||||
|
||||
1420
webapp/yarn.lock
1420
webapp/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user