mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2026-01-14 08:54:38 +00:00
Merge branch 'master' into chat-notify-via-email
This commit is contained in:
commit
15a669ecb1
2
.github/workflows/deploy-documentation.yml
vendored
2
.github/workflows/deploy-documentation.yml
vendored
@ -38,7 +38,7 @@ jobs:
|
||||
run: npm install && npm run docs:build
|
||||
|
||||
- name: Deploy Vuepress to Github Pages
|
||||
uses: crazy-max/ghaction-github-pages@fbf0a4fa4e00f45accd6cf3232368436ec06ed59 # v4.0.0
|
||||
uses: crazy-max/ghaction-github-pages@df5cc2bfa78282ded844b354faee141f06b41865 # v4.0.0
|
||||
with:
|
||||
target_branch: gh-pages
|
||||
build_dir: .vuepress/dist
|
||||
|
||||
8
.github/workflows/docker-push.yml
vendored
8
.github/workflows/docker-push.yml
vendored
@ -59,16 +59,16 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.1.7
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@70b2cdc6480c1a8b86edf1777157f8f437de2166
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
@ -81,7 +81,7 @@ jobs:
|
||||
type=sha
|
||||
- name: Build and push Docker images
|
||||
id: push
|
||||
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75
|
||||
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4
|
||||
with:
|
||||
context: ${{ matrix.app.context }}
|
||||
target: ${{ matrix.app.target }}
|
||||
|
||||
6
.github/workflows/publish.yml
vendored
6
.github/workflows/publish.yml
vendored
@ -64,7 +64,7 @@ jobs:
|
||||
echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV
|
||||
- run: echo "BUILD_VERSION=${VERSION}-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
|
||||
#- name: Repository Dispatch
|
||||
# uses: peter-evans/repository-dispatch@b0b38f73c8333be75d585a92b2c630a10d2a78f5 # v3.0.0
|
||||
# uses: peter-evans/repository-dispatch@7d980a9b9f8ecf8955ea90507b3ed89122f53215 # v3.0.0
|
||||
# with:
|
||||
# token: ${{ github.token }}
|
||||
# event-type: trigger-ocelot-build-success
|
||||
@ -72,7 +72,7 @@ jobs:
|
||||
# client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "VERSION": "${VERSION}", "BUILD_DATE": "${BUILD_DATE}", "BUILD_COMMIT": "${BUILD_COMMIT}", "BUILD_VERSION": "${BUILD_VERSION}"}'
|
||||
|
||||
- name: Repository Dispatch stage.ocelot.social
|
||||
uses: peter-evans/repository-dispatch@b0b38f73c8333be75d585a92b2c630a10d2a78f5 # v3.0.0
|
||||
uses: peter-evans/repository-dispatch@7d980a9b9f8ecf8955ea90507b3ed89122f53215 # v3.0.0
|
||||
with:
|
||||
token: ${{ secrets.OCELOT_PUBLISH_EVENT_PAT }} # this token is required to access the other repository
|
||||
event-type: trigger-ocelot-build-success
|
||||
@ -80,7 +80,7 @@ jobs:
|
||||
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@b0b38f73c8333be75d585a92b2c630a10d2a78f5 # v3.0.0
|
||||
uses: peter-evans/repository-dispatch@7d980a9b9f8ecf8955ea90507b3ed89122f53215 # v3.0.0
|
||||
with:
|
||||
token: ${{ secrets.OCELOT_PUBLISH_EVENT_PAT }} # this token is required to access the other repository
|
||||
event-type: trigger-ocelot-build-success
|
||||
|
||||
8
.github/workflows/test-backend.yml
vendored
8
.github/workflows/test-backend.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
||||
|
||||
- name: Cache docker images
|
||||
id: cache-neo4j
|
||||
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/neo4j.tar
|
||||
key: ${{ github.run_id }}-backend-neo4j-cache
|
||||
@ -58,7 +58,7 @@ jobs:
|
||||
|
||||
- name: Cache docker images
|
||||
id: cache-backend
|
||||
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/backend.tar
|
||||
key: ${{ github.run_id }}-backend-cache
|
||||
@ -87,14 +87,14 @@ jobs:
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Restore Neo4J cache
|
||||
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/neo4j.tar
|
||||
key: ${{ github.run_id }}-backend-neo4j-cache
|
||||
fail-on-cache-miss: true
|
||||
|
||||
- name: Restore Backend cache
|
||||
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/backend.tar
|
||||
key: ${{ github.run_id }}-backend-cache
|
||||
|
||||
4
.github/workflows/test-e2e.yml
vendored
4
.github/workflows/test-e2e.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
||||
|
||||
- name: Cache docker images
|
||||
id: cache
|
||||
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: |
|
||||
/opt/cucumber-json-formatter
|
||||
@ -59,7 +59,7 @@ jobs:
|
||||
job: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
steps:
|
||||
- name: Restore cache
|
||||
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
id: cache
|
||||
with:
|
||||
path: |
|
||||
|
||||
4
.github/workflows/test-webapp.yml
vendored
4
.github/workflows/test-webapp.yml
vendored
@ -50,7 +50,7 @@ jobs:
|
||||
docker save "ocelotsocialnetwork/webapp:test" > /tmp/webapp.tar
|
||||
|
||||
- name: Cache docker image
|
||||
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/webapp.tar
|
||||
key: ${{ github.run_id }}-webapp-cache
|
||||
@ -79,7 +79,7 @@ jobs:
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Restore webapp cache
|
||||
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/webapp.tar
|
||||
key: ${{ github.run_id }}-webapp-cache
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,6 +15,7 @@ node_modules/
|
||||
cypress/videos
|
||||
cypress/screenshots/
|
||||
cypress.env.json
|
||||
deployment/configurations/
|
||||
|
||||
.vuepress/.cache/
|
||||
.vuepress/.temp/
|
||||
|
||||
@ -23,8 +23,8 @@
|
||||
"db:migrate:create": "yarn run __migrate --template-file ./src/db/migrate/template.ts --date-format 'yyyymmddHHmmss' create"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/cli": "~7.26.4",
|
||||
"@babel/core": "^7.26.9",
|
||||
"@babel/cli": "~7.27.0",
|
||||
"@babel/core": "^7.26.10",
|
||||
"@babel/node": "~7.26.0",
|
||||
"@babel/plugin-proposal-throw-expressions": "^7.25.9",
|
||||
"@babel/preset-env": "~7.26.9",
|
||||
@ -53,7 +53,7 @@
|
||||
"graphql-redis-subscriptions": "^2.7.0",
|
||||
"graphql-shield": "~7.2.2",
|
||||
"graphql-tag": "~2.10.3",
|
||||
"helmet": "~8.0.0",
|
||||
"helmet": "~8.1.0",
|
||||
"ioredis": "^4.16.1",
|
||||
"jsonwebtoken": "~8.5.1",
|
||||
"languagedetect": "^2.0.0",
|
||||
@ -61,20 +61,20 @@
|
||||
"linkifyjs": "^4.2.0",
|
||||
"lodash": "~4.17.21",
|
||||
"merge-graphql-schemas": "^1.7.8",
|
||||
"metascraper": "^5.46.7",
|
||||
"metascraper-author": "^5.46.5",
|
||||
"metascraper-date": "^5.46.5",
|
||||
"metascraper-description": "^5.46.5",
|
||||
"metascraper-image": "^5.46.5",
|
||||
"metascraper-lang": "^5.46.5",
|
||||
"metascraper": "^5.46.11",
|
||||
"metascraper-author": "^5.46.11",
|
||||
"metascraper-date": "^5.46.11",
|
||||
"metascraper-description": "^5.46.11",
|
||||
"metascraper-image": "^5.46.11",
|
||||
"metascraper-lang": "^5.46.11",
|
||||
"metascraper-lang-detector": "^4.10.2",
|
||||
"metascraper-logo": "^5.46.5",
|
||||
"metascraper-publisher": "^5.46.5",
|
||||
"metascraper-logo": "^5.46.11",
|
||||
"metascraper-publisher": "^5.46.11",
|
||||
"metascraper-soundcloud": "^5.34.4",
|
||||
"metascraper-title": "^5.46.5",
|
||||
"metascraper-url": "^5.46.5",
|
||||
"metascraper-video": "^5.46.5",
|
||||
"metascraper-youtube": "^5.46.5",
|
||||
"metascraper-title": "^5.46.11",
|
||||
"metascraper-url": "^5.46.11",
|
||||
"metascraper-video": "^5.46.11",
|
||||
"metascraper-youtube": "^5.46.11",
|
||||
"migrate": "^2.1.0",
|
||||
"mime-types": "^2.1.35",
|
||||
"minimatch": "^9.0.4",
|
||||
@ -86,25 +86,25 @@
|
||||
"nodemailer": "^6.10.0",
|
||||
"nodemailer-html-to-text": "^3.2.0",
|
||||
"request": "~2.88.2",
|
||||
"sanitize-html": "~2.14.0",
|
||||
"sanitize-html": "~2.15.0",
|
||||
"slug": "~9.1.0",
|
||||
"subscriptions-transport-ws": "^0.9.19",
|
||||
"trunc-html": "~1.1.2",
|
||||
"uuid": "~9.0.1",
|
||||
"validator": "^13.12.0",
|
||||
"validator": "^13.15.0",
|
||||
"xregexp": "^5.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "9.5.0",
|
||||
"@faker-js/faker": "9.6.0",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^22.13.5",
|
||||
"@types/node": "^22.14.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
"apollo-server-testing": "~2.11.0",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-prettier": "^10.1.1",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-import-resolver-typescript": "^3.8.3",
|
||||
"eslint-import-resolver-typescript": "^4.3.1",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-jest": "^28.11.0",
|
||||
"eslint-plugin-n": "^16.6.2",
|
||||
@ -113,11 +113,11 @@
|
||||
"eslint-plugin-security": "^3.0.1",
|
||||
"jest": "^29.7.0",
|
||||
"nodemon": "~3.1.9",
|
||||
"prettier": "^3.5.2",
|
||||
"prettier": "^3.5.3",
|
||||
"rosie": "^2.1.1",
|
||||
"ts-jest": "^29.2.5",
|
||||
"ts-jest": "^29.3.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.7.3"
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"**/**/fs-capacitor": "^6.2.0",
|
||||
|
||||
@ -255,7 +255,6 @@ describe('notifications', () => {
|
||||
})
|
||||
|
||||
it('sends me no notification', async () => {
|
||||
await notifiedUser.relateTo(commentAuthor, 'blocked')
|
||||
await createCommentOnPostAction()
|
||||
const expected = expect.objectContaining({
|
||||
data: { notifications: [] },
|
||||
|
||||
@ -109,13 +109,19 @@ const handleContentDataOfPost = async (resolve, root, args, context, resolveInfo
|
||||
|
||||
const handleContentDataOfComment = async (resolve, root, args, context, resolveInfo) => {
|
||||
const { content } = args
|
||||
let idsOfUsers = extractMentionedUsers(content)
|
||||
let idsOfMentionedUsers = extractMentionedUsers(content)
|
||||
const comment = await resolve(root, args, context, resolveInfo)
|
||||
const [postAuthor] = await postAuthorOfComment(comment.id, { context })
|
||||
idsOfUsers = idsOfUsers.filter((id) => id !== postAuthor.id)
|
||||
idsOfMentionedUsers = idsOfMentionedUsers.filter((id) => id !== postAuthor.id)
|
||||
await publishNotifications(context, [
|
||||
notifyUsersOfMention('Comment', comment.id, idsOfUsers, 'mentioned_in_comment', context),
|
||||
notifyUsersOfComment('Comment', comment.id, postAuthor.id, 'commented_on_post', context),
|
||||
notifyUsersOfMention(
|
||||
'Comment',
|
||||
comment.id,
|
||||
idsOfMentionedUsers,
|
||||
'mentioned_in_comment',
|
||||
context,
|
||||
),
|
||||
notifyUsersOfComment('Comment', comment.id, 'commented_on_post', context),
|
||||
])
|
||||
return comment
|
||||
}
|
||||
@ -270,29 +276,34 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
|
||||
}
|
||||
}
|
||||
|
||||
const notifyUsersOfComment = async (label, commentId, postAuthorId, reason, context) => {
|
||||
if (context.user.id === postAuthorId) return []
|
||||
const notifyUsersOfComment = async (label, commentId, reason, context) => {
|
||||
await validateNotifyUsers(label, reason)
|
||||
const session = context.driver.session()
|
||||
const writeTxResultPromise = await session.writeTransaction(async (transaction) => {
|
||||
const notificationTransactionResponse = await transaction.run(
|
||||
`
|
||||
MATCH (postAuthor:User {id: $postAuthorId})-[:WROTE]->(post:Post)<-[:COMMENTS]-(comment:Comment { id: $commentId })<-[:WROTE]-(commenter:User)
|
||||
WHERE NOT (postAuthor)-[:BLOCKED]-(commenter)
|
||||
MERGE (comment)-[notification:NOTIFIED {reason: $reason}]->(postAuthor)
|
||||
MATCH (observingUser:User)-[:OBSERVES { active: true }]->(post:Post)<-[:COMMENTS]-(comment:Comment { id: $commentId })<-[:WROTE]-(commenter:User)
|
||||
WHERE NOT (observingUser)-[:BLOCKED]-(commenter) AND NOT observingUser.id = $userId
|
||||
WITH observingUser, post, comment, commenter
|
||||
MATCH (postAuthor:User)-[:WROTE]->(post)
|
||||
MERGE (comment)-[notification:NOTIFIED {reason: $reason}]->(observingUser)
|
||||
SET notification.read = FALSE
|
||||
SET notification.createdAt = COALESCE(notification.createdAt, toString(datetime()))
|
||||
SET notification.updatedAt = toString(datetime())
|
||||
WITH notification, postAuthor, post, commenter,
|
||||
WITH notification, observingUser, post, commenter, postAuthor,
|
||||
comment {.*, __typename: labels(comment)[0], author: properties(commenter), post: post {.*, author: properties(postAuthor) } } AS finalResource
|
||||
RETURN notification {
|
||||
.*,
|
||||
from: finalResource,
|
||||
to: properties(postAuthor),
|
||||
to: properties(observingUser),
|
||||
relatedUser: properties(commenter)
|
||||
}
|
||||
`,
|
||||
{ commentId, postAuthorId, reason },
|
||||
{
|
||||
commentId,
|
||||
reason,
|
||||
userId: context.user.id,
|
||||
},
|
||||
)
|
||||
return notificationTransactionResponse.records.map((record) => record.get('notification'))
|
||||
})
|
||||
|
||||
377
backend/src/middleware/notifications/observing-posts.spec.ts
Normal file
377
backend/src/middleware/notifications/observing-posts.spec.ts
Normal file
@ -0,0 +1,377 @@
|
||||
import gql from 'graphql-tag'
|
||||
import { cleanDatabase } from '../../db/factories'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import createServer from '../../server'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
|
||||
import CONFIG from '../../config'
|
||||
|
||||
CONFIG.CATEGORIES_ACTIVE = false
|
||||
|
||||
let server, query, mutate, authenticatedUser
|
||||
|
||||
let postAuthor, firstCommenter, secondCommenter
|
||||
|
||||
const driver = getDriver()
|
||||
const neode = getNeode()
|
||||
|
||||
const createPostMutation = gql`
|
||||
mutation ($id: ID, $title: String!, $content: String!) {
|
||||
CreatePost(id: $id, title: $title, content: $content) {
|
||||
id
|
||||
title
|
||||
content
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const createCommentMutation = gql`
|
||||
mutation ($id: ID, $postId: ID!, $content: String!) {
|
||||
CreateComment(id: $id, postId: $postId, content: $content) {
|
||||
id
|
||||
content
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const notificationQuery = gql`
|
||||
query ($read: Boolean) {
|
||||
notifications(read: $read, orderBy: updatedAt_desc) {
|
||||
read
|
||||
reason
|
||||
createdAt
|
||||
relatedUser {
|
||||
id
|
||||
}
|
||||
from {
|
||||
__typename
|
||||
... on Post {
|
||||
id
|
||||
content
|
||||
}
|
||||
... on Comment {
|
||||
id
|
||||
content
|
||||
}
|
||||
... on Group {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const toggleObservePostMutation = gql`
|
||||
mutation ($id: ID!, $value: Boolean!) {
|
||||
toggleObservePost(id: $id, value: $value) {
|
||||
isObservedByMe
|
||||
observingUsersCount
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
beforeAll(async () => {
|
||||
await cleanDatabase()
|
||||
|
||||
const createServerResult = createServer({
|
||||
context: () => {
|
||||
return {
|
||||
user: authenticatedUser,
|
||||
neode,
|
||||
driver,
|
||||
cypherParams: {
|
||||
currentUserId: authenticatedUser ? authenticatedUser.id : null,
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
server = createServerResult.server
|
||||
const createTestClientResult = createTestClient(server)
|
||||
query = createTestClientResult.query
|
||||
mutate = createTestClientResult.mutate
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDatabase()
|
||||
driver.close()
|
||||
})
|
||||
|
||||
describe('notifications for users that observe a post', () => {
|
||||
beforeAll(async () => {
|
||||
postAuthor = await neode.create(
|
||||
'User',
|
||||
{
|
||||
id: 'post-author',
|
||||
name: 'Post Author',
|
||||
slug: 'post-author',
|
||||
},
|
||||
{
|
||||
email: 'test@example.org',
|
||||
password: '1234',
|
||||
},
|
||||
)
|
||||
firstCommenter = await neode.create(
|
||||
'User',
|
||||
{
|
||||
id: 'first-commenter',
|
||||
name: 'First Commenter',
|
||||
slug: 'first-commenter',
|
||||
},
|
||||
{
|
||||
email: 'test2@example.org',
|
||||
password: '1234',
|
||||
},
|
||||
)
|
||||
secondCommenter = await neode.create(
|
||||
'User',
|
||||
{
|
||||
id: 'second-commenter',
|
||||
name: 'Second Commenter',
|
||||
slug: 'second-commenter',
|
||||
},
|
||||
{
|
||||
email: 'test3@example.org',
|
||||
password: '1234',
|
||||
},
|
||||
)
|
||||
authenticatedUser = await postAuthor.toJson()
|
||||
await mutate({
|
||||
mutation: createPostMutation,
|
||||
variables: {
|
||||
id: 'post',
|
||||
title: 'This is the post',
|
||||
content: 'This is the content of the post',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('first comment on the post', () => {
|
||||
beforeAll(async () => {
|
||||
authenticatedUser = await firstCommenter.toJson()
|
||||
await mutate({
|
||||
mutation: createCommentMutation,
|
||||
variables: {
|
||||
postId: 'post',
|
||||
id: 'c-1',
|
||||
content: 'first comment of first commenter',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('sends NO notification to the commenter', async () => {
|
||||
await expect(
|
||||
query({
|
||||
query: notificationQuery,
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
notifications: [],
|
||||
},
|
||||
errors: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it('sends notification to the author', async () => {
|
||||
authenticatedUser = await postAuthor.toJson()
|
||||
await expect(
|
||||
query({
|
||||
query: notificationQuery,
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
notifications: [
|
||||
{
|
||||
from: {
|
||||
__typename: 'Comment',
|
||||
id: 'c-1',
|
||||
},
|
||||
read: false,
|
||||
reason: 'commented_on_post',
|
||||
},
|
||||
],
|
||||
},
|
||||
errors: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
describe('second comment on post', () => {
|
||||
beforeAll(async () => {
|
||||
authenticatedUser = await secondCommenter.toJson()
|
||||
await mutate({
|
||||
mutation: createCommentMutation,
|
||||
variables: {
|
||||
postId: 'post',
|
||||
id: 'c-2',
|
||||
content: 'first comment of second commenter',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('sends NO notification to the commenter', async () => {
|
||||
await expect(
|
||||
query({
|
||||
query: notificationQuery,
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
notifications: [],
|
||||
},
|
||||
errors: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it('sends notification to the author', async () => {
|
||||
authenticatedUser = await postAuthor.toJson()
|
||||
await expect(
|
||||
query({
|
||||
query: notificationQuery,
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
notifications: [
|
||||
{
|
||||
from: {
|
||||
__typename: 'Comment',
|
||||
id: 'c-2',
|
||||
},
|
||||
read: false,
|
||||
reason: 'commented_on_post',
|
||||
},
|
||||
{
|
||||
from: {
|
||||
__typename: 'Comment',
|
||||
id: 'c-1',
|
||||
},
|
||||
read: false,
|
||||
reason: 'commented_on_post',
|
||||
},
|
||||
],
|
||||
},
|
||||
errors: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it('sends notification to first commenter', async () => {
|
||||
authenticatedUser = await firstCommenter.toJson()
|
||||
await expect(
|
||||
query({
|
||||
query: notificationQuery,
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
notifications: [
|
||||
{
|
||||
from: {
|
||||
__typename: 'Comment',
|
||||
id: 'c-2',
|
||||
},
|
||||
read: false,
|
||||
reason: 'commented_on_post',
|
||||
},
|
||||
],
|
||||
},
|
||||
errors: undefined,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('first commenter unfollows the post and post author comments post', () => {
|
||||
beforeAll(async () => {
|
||||
authenticatedUser = await firstCommenter.toJson()
|
||||
await mutate({
|
||||
mutation: toggleObservePostMutation,
|
||||
variables: {
|
||||
id: 'post',
|
||||
value: false,
|
||||
},
|
||||
})
|
||||
|
||||
authenticatedUser = await postAuthor.toJson()
|
||||
await mutate({
|
||||
mutation: createCommentMutation,
|
||||
variables: {
|
||||
postId: 'post',
|
||||
id: 'c-3',
|
||||
content: 'first comment of post author',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('sends no new notification to the post author', async () => {
|
||||
await expect(
|
||||
query({
|
||||
query: notificationQuery,
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
notifications: [
|
||||
{
|
||||
from: {
|
||||
__typename: 'Comment',
|
||||
id: 'c-2',
|
||||
},
|
||||
read: false,
|
||||
reason: 'commented_on_post',
|
||||
},
|
||||
{
|
||||
from: {
|
||||
__typename: 'Comment',
|
||||
id: 'c-1',
|
||||
},
|
||||
read: false,
|
||||
reason: 'commented_on_post',
|
||||
},
|
||||
],
|
||||
},
|
||||
errors: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it('sends no new notification to first commenter', async () => {
|
||||
authenticatedUser = await firstCommenter.toJson()
|
||||
await expect(
|
||||
query({
|
||||
query: notificationQuery,
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
notifications: [
|
||||
{
|
||||
from: {
|
||||
__typename: 'Comment',
|
||||
id: 'c-2',
|
||||
},
|
||||
read: false,
|
||||
reason: 'commented_on_post',
|
||||
},
|
||||
],
|
||||
},
|
||||
errors: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it('sends notification to second commenter', async () => {
|
||||
authenticatedUser = await secondCommenter.toJson()
|
||||
await expect(
|
||||
query({
|
||||
query: notificationQuery,
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
notifications: [
|
||||
{
|
||||
from: {
|
||||
__typename: 'Comment',
|
||||
id: 'c-3',
|
||||
},
|
||||
read: false,
|
||||
reason: 'commented_on_post',
|
||||
},
|
||||
],
|
||||
},
|
||||
errors: undefined,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
File diff suppressed because it is too large
Load Diff
73
deployment/deployment-values.md
Normal file
73
deployment/deployment-values.md
Normal file
@ -0,0 +1,73 @@
|
||||
# Deployment Values
|
||||
|
||||
For each deployment, you need to set the environment variables and configurations.
|
||||
Here is some specific information on how to set the values.
|
||||
|
||||
## Webapp
|
||||
|
||||
We have several configuration possibilities just in the frontend.
|
||||
|
||||
### Date Time
|
||||
|
||||
In file `branding/constants/dateTime.js`.
|
||||
|
||||
- `RELATIVE_DATETIME`
|
||||
- `true` (default) or `false`
|
||||
- `ABSOLUT_DATETIME_FORMAT`
|
||||
- definition see [date-fns, format](https://date-fns.org/v3.3.1/docs/format):
|
||||
- `P`: just localized date
|
||||
- `Pp`: just localized date and time
|
||||
|
||||
## E-Mails
|
||||
|
||||
You need to set environment variables to send registration and invitation information or notifications to users, for example.
|
||||
|
||||
### SPF and DKIM
|
||||
|
||||
More and more e-mail providers require settings for authorization and verification of e-mail senders.
|
||||
|
||||
### SPF
|
||||
|
||||
Sometimes it is enough to create an SPF record in your DNS.
|
||||
|
||||
### DKIM
|
||||
|
||||
However, if you need DKIM authorization and verification, you must set the appropriate environment variables in: `.env`, `docker-compose.yml` or Helm script `values.yaml`:
|
||||
|
||||
```bash
|
||||
SMTP_DKIM_DOMAINNAME=<your e-mail sender domain>
|
||||
SMTP_DKIM_KEYSELECTOR=ocelot # "free" name used in DNS as selector. we recommend this
|
||||
SMTP_DKIM_PRIVATKEY="-----BEGIN RSA PRIVATE KEY-----\\n<your base64 encoded private key data>\\n-----END RSA PRIVATE KEY-----\\n"
|
||||
```
|
||||
|
||||
You can find out how DKIM works here:
|
||||
|
||||
<https://www.ionos.com/digitalguide/e-mail/e-mail-security/dkim-domainkeys/>
|
||||
|
||||
To create the private and public DKIM key as DNS records with selector, see here:
|
||||
|
||||
<https://knowledge.ondmarc.redsift.com/en/articles/2141592-generating-2048-bits-dkim-public-and-private-keys-using-openssl-on-a-mac>
|
||||
|
||||
Information about the required PEM format can be found here:
|
||||
|
||||
<https://docs.progress.com/bundle/datadirect-hybrid-data-pipeline-installation-46/page/PEM-file-format.html>
|
||||
|
||||
## Neo4j Database
|
||||
|
||||
We have several configuration options for our Neo4j database.
|
||||
|
||||
### DBMS_DEFAULT_DATABASE – Default Database Name to be Used
|
||||
|
||||
If you need to set the default database name in Neo4j to be used for all operations and terminal commands like our backup scripts, you must set the appropriate environment variable in: `.env`, `docker-compose.yml` or Helm script `values.yaml`:
|
||||
|
||||
```yaml
|
||||
DBMS_DEFAULT_DATABASE: "graph.db"
|
||||
```
|
||||
|
||||
The default value is `neo4j` if it is not set.
|
||||
|
||||
As example see files:
|
||||
|
||||
- `neo4j/.env.template`
|
||||
- `deployment/docker-compose.yml`
|
||||
- `deployment/configurations/stage.ocelot.social/kubernetes/values.yaml.template`
|
||||
@ -43,3 +43,4 @@ services:
|
||||
container_name: mailserver
|
||||
ports:
|
||||
- 1080:80
|
||||
- 25:25
|
||||
|
||||
@ -40,3 +40,4 @@ services:
|
||||
image: djfarrelly/maildev
|
||||
ports:
|
||||
- 1080:80
|
||||
- 25:25
|
||||
|
||||
84
package-lock.json
generated
84
package-lock.json
generated
@ -9,13 +9,13 @@
|
||||
"version": "3.2.1",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.26.9",
|
||||
"@babel/core": "^7.26.10",
|
||||
"@babel/preset-env": "^7.26.9",
|
||||
"@babel/register": "^7.25.9",
|
||||
"@badeball/cypress-cucumber-preprocessor": "^22.0.1",
|
||||
"@cucumber/cucumber": "11.2.0",
|
||||
"@cypress/browserify-preprocessor": "^3.0.2",
|
||||
"@faker-js/faker": "9.5.0",
|
||||
"@faker-js/faker": "9.6.0",
|
||||
"auto-changelog": "^2.5.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"cross-env": "^7.0.3",
|
||||
@ -127,22 +127,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/core": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz",
|
||||
"integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==",
|
||||
"version": "7.26.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz",
|
||||
"integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.26.2",
|
||||
"@babel/generator": "^7.26.9",
|
||||
"@babel/generator": "^7.26.10",
|
||||
"@babel/helper-compilation-targets": "^7.26.5",
|
||||
"@babel/helper-module-transforms": "^7.26.0",
|
||||
"@babel/helpers": "^7.26.9",
|
||||
"@babel/parser": "^7.26.9",
|
||||
"@babel/helpers": "^7.26.10",
|
||||
"@babel/parser": "^7.26.10",
|
||||
"@babel/template": "^7.26.9",
|
||||
"@babel/traverse": "^7.26.9",
|
||||
"@babel/types": "^7.26.9",
|
||||
"@babel/traverse": "^7.26.10",
|
||||
"@babel/types": "^7.26.10",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"debug": "^4.1.0",
|
||||
"gensync": "^1.0.0-beta.2",
|
||||
@ -158,14 +158,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz",
|
||||
"integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz",
|
||||
"integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.26.9",
|
||||
"@babel/types": "^7.26.9",
|
||||
"@babel/parser": "^7.27.0",
|
||||
"@babel/types": "^7.27.0",
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25",
|
||||
"jsesc": "^3.0.2"
|
||||
@ -411,27 +411,27 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helpers": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz",
|
||||
"integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz",
|
||||
"integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.26.9",
|
||||
"@babel/types": "^7.26.9"
|
||||
"@babel/template": "^7.27.0",
|
||||
"@babel/types": "^7.27.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz",
|
||||
"integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
|
||||
"integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.26.9"
|
||||
"@babel/types": "^7.27.0"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@ -1714,32 +1714,32 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz",
|
||||
"integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
|
||||
"integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.26.2",
|
||||
"@babel/parser": "^7.26.9",
|
||||
"@babel/types": "^7.26.9"
|
||||
"@babel/parser": "^7.27.0",
|
||||
"@babel/types": "^7.27.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz",
|
||||
"integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz",
|
||||
"integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.26.2",
|
||||
"@babel/generator": "^7.26.9",
|
||||
"@babel/parser": "^7.26.9",
|
||||
"@babel/template": "^7.26.9",
|
||||
"@babel/types": "^7.26.9",
|
||||
"@babel/generator": "^7.27.0",
|
||||
"@babel/parser": "^7.27.0",
|
||||
"@babel/template": "^7.27.0",
|
||||
"@babel/types": "^7.27.0",
|
||||
"debug": "^4.3.1",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
@ -1748,9 +1748,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz",
|
||||
"integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
|
||||
"integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -2786,9 +2786,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@faker-js/faker": {
|
||||
"version": "9.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.5.0.tgz",
|
||||
"integrity": "sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw==",
|
||||
"version": "9.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.6.0.tgz",
|
||||
"integrity": "sha512-3vm4by+B5lvsFPSyep3ELWmZfE3kicDtmemVpuwl1yH7tqtnHdsA6hG8fbXedMVdkzgtvzWoRgjSB4Q+FHnZiw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
||||
@ -33,13 +33,13 @@
|
||||
"release": "./scripts/release.sh"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.26.9",
|
||||
"@babel/core": "^7.26.10",
|
||||
"@babel/preset-env": "^7.26.9",
|
||||
"@babel/register": "^7.25.9",
|
||||
"@badeball/cypress-cucumber-preprocessor": "^22.0.1",
|
||||
"@cucumber/cucumber": "11.2.0",
|
||||
"@cypress/browserify-preprocessor": "^3.0.2",
|
||||
"@faker-js/faker": "9.5.0",
|
||||
"@faker-js/faker": "9.6.0",
|
||||
"auto-changelog": "^2.5.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"cross-env": "^7.0.3",
|
||||
|
||||
1
webapp/assets/_new/icons/svgs/bell-slashed.svg
Normal file
1
webapp/assets/_new/icons/svgs/bell-slashed.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="1080" height="1080"><rect width="100%" height="100%" fill="transparent"/><path d="M15 3a2 2 0 0 1 2 2c0 .085-.021.168-.031.25C20.49 6.174 23 9.523 23 13.281V22c0 .565.435 1 1 1h1v2h-7.188c.114.316.188.647.188 1 0 1.645-1.355 3-3 3s-3-1.355-3-3c0-.353.073-.684.188-1H5v-2h1c.565 0 1-.435 1-1v-9c0-3.726 2.574-6.866 6.031-7.75C13.021 5.168 13 5.085 13 5a2 2 0 0 1 2-2zm-.437 4A6.004 6.004 0 0 0 9 13v9c0 .353-.073.684-.188 1h12.375a2.925 2.925 0 0 1-.188-1v-8.719c0-3.319-2.546-6.183-5.813-6.281-.064-.002-.124 0-.188 0-.148 0-.292-.011-.438 0zM15 25c-.564 0-1 .436-1 1 0 .564.436 1 1 1 .564 0 1-.436 1-1 0-.564-.436-1-1-1z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#000;fill-rule:nonzero;opacity:1" transform="translate(33.75) scale(33.75)"/><rect width="74.334" height="74.334" x="-37.167" y="-37.167" rx="0" ry="0" style="stroke:#000;stroke-width:0;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#000;fill-rule:nonzero;opacity:1" transform="matrix(9.42 -12.59 .8 .6 538.54 541.95)" vector-effect="non-scaling-stroke"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@ -500,6 +500,44 @@ describe('ContentMenu.vue', () => {
|
||||
],
|
||||
])
|
||||
})
|
||||
|
||||
it('can observe posts', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
isObservedByMe: false,
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.observe')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('toggleObservePost')).toEqual([
|
||||
['d23a4265-f5f7-4e17-9f86-85f714b4b9f8', true],
|
||||
])
|
||||
})
|
||||
|
||||
it('can unobserve posts', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
isObservedByMe: true,
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.unobserve')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('toggleObservePost')).toEqual([
|
||||
['d23a4265-f5f7-4e17-9f86-85f714b4b9f8', false],
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -99,6 +99,24 @@ export default {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (this.resource.isObservedByMe) {
|
||||
routes.push({
|
||||
label: this.$t(`post.menu.unobserve`),
|
||||
callback: () => {
|
||||
this.$emit('toggleObservePost', this.resource.id, false)
|
||||
},
|
||||
icon: 'bell-slashed',
|
||||
})
|
||||
} else {
|
||||
routes.push({
|
||||
label: this.$t(`post.menu.observe`),
|
||||
callback: () => {
|
||||
this.$emit('toggleObservePost', this.resource.id, true)
|
||||
},
|
||||
icon: 'bell',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isOwner && this.resourceType === 'comment') {
|
||||
|
||||
60
webapp/components/ObserveButton.spec.js
Normal file
60
webapp/components/ObserveButton.spec.js
Normal file
@ -0,0 +1,60 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import ObserveButton from './ObserveButton.vue'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
describe('ObserveButton', () => {
|
||||
let mocks
|
||||
|
||||
const Wrapper = (count = 1, postId = '123', isObserved = true) => {
|
||||
return mount(ObserveButton, {
|
||||
mocks,
|
||||
localVue,
|
||||
propsData: {
|
||||
count,
|
||||
postId,
|
||||
isObserved,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
let wrapper
|
||||
|
||||
beforeEach(() => {
|
||||
mocks = {
|
||||
$t: jest.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
describe('observed', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper(1, '123', true)
|
||||
})
|
||||
|
||||
it('renders', () => {
|
||||
expect(wrapper.element).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('emits toggleObservePost with false when clicked', () => {
|
||||
const button = wrapper.find('.base-button')
|
||||
button.trigger('click')
|
||||
expect(wrapper.emitted('toggleObservePost')).toEqual([['123', false]])
|
||||
})
|
||||
})
|
||||
|
||||
describe('unobserved', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper(1, '123', false)
|
||||
})
|
||||
|
||||
it('renders', () => {
|
||||
expect(wrapper.element).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('emits toggleObservePost with true when clicked', () => {
|
||||
const button = wrapper.find('.base-button')
|
||||
button.trigger('click')
|
||||
expect(wrapper.emitted('toggleObservePost')).toEqual([['123', true]])
|
||||
})
|
||||
})
|
||||
})
|
||||
39
webapp/components/ObserveButton.vue
Normal file
39
webapp/components/ObserveButton.vue
Normal file
@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<ds-space margin="xx-small" class="text-align-center">
|
||||
<base-button :loading="loading" :filled="isObserved" icon="bell" circle @click="toggle" />
|
||||
<ds-space margin-bottom="xx-small" />
|
||||
<ds-text color="soft" class="observe-button-text">
|
||||
<ds-heading style="display: inline" tag="h3">{{ count }}x</ds-heading>
|
||||
{{ $t('observeButton.observed') }}
|
||||
</ds-text>
|
||||
</ds-space>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
count: { type: Number, default: 0 },
|
||||
postId: { type: String, default: null },
|
||||
isObserved: { type: Boolean, default: false },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
this.$emit('toggleObservePost', this.postId, !this.isObserved)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.observe-button-text {
|
||||
user-select: none;
|
||||
}
|
||||
.text-align-center {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@ -59,7 +59,7 @@
|
||||
:key="category.id"
|
||||
v-tooltip="{
|
||||
content: `
|
||||
${$t(`contribution.category.name.${category.slug}`)}:
|
||||
${$t(`contribution.category.name.${category.slug}`)}:
|
||||
${$t(`contribution.category.description.${category.slug}`)}
|
||||
`,
|
||||
placement: 'bottom-start',
|
||||
@ -97,6 +97,7 @@
|
||||
:is-owner="isAuthor"
|
||||
@pinPost="pinPost"
|
||||
@unpinPost="unpinPost"
|
||||
@toggleObservePost="toggleObservePost"
|
||||
/>
|
||||
</client-only>
|
||||
</footer>
|
||||
@ -212,6 +213,9 @@ export default {
|
||||
unpinPost(post) {
|
||||
this.$emit('unpinPost', post)
|
||||
},
|
||||
toggleObservePost(postId, value) {
|
||||
this.$emit('toggleObservePost', postId, value)
|
||||
},
|
||||
visibilityChanged(isVisible, entry, id) {
|
||||
if (!this.post.viewedTeaserByCurrentUser && isVisible) {
|
||||
this.$apollo
|
||||
|
||||
81
webapp/components/__snapshots__/ObserveButton.spec.js.snap
Normal file
81
webapp/components/__snapshots__/ObserveButton.spec.js.snap
Normal file
@ -0,0 +1,81 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ObserveButton observed renders 1`] = `
|
||||
<div
|
||||
class="ds-space text-align-center"
|
||||
style="margin-top: 4px; margin-bottom: 4px;"
|
||||
>
|
||||
<button
|
||||
class="base-button --icon-only --circle --filled"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="base-icon"
|
||||
>
|
||||
<!---->
|
||||
</span>
|
||||
|
||||
<!---->
|
||||
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="ds-space"
|
||||
style="margin-bottom: 4px;"
|
||||
/>
|
||||
|
||||
<p
|
||||
class="ds-text observe-button-text ds-text-soft"
|
||||
>
|
||||
<h3
|
||||
class="ds-heading ds-heading-h3"
|
||||
style="display: inline;"
|
||||
>
|
||||
1x
|
||||
</h3>
|
||||
|
||||
|
||||
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ObserveButton unobserved renders 1`] = `
|
||||
<div
|
||||
class="ds-space text-align-center"
|
||||
style="margin-top: 4px; margin-bottom: 4px;"
|
||||
>
|
||||
<button
|
||||
class="base-button --icon-only --circle"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="base-icon"
|
||||
>
|
||||
<!---->
|
||||
</span>
|
||||
|
||||
<!---->
|
||||
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="ds-space"
|
||||
style="margin-bottom: 4px;"
|
||||
/>
|
||||
|
||||
<p
|
||||
class="ds-text observe-button-text ds-text-soft"
|
||||
>
|
||||
<h3
|
||||
class="ds-heading ds-heading-h3"
|
||||
style="display: inline;"
|
||||
>
|
||||
1x
|
||||
</h3>
|
||||
|
||||
|
||||
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
@ -48,6 +48,9 @@
|
||||
@removePostFromList="posts = removePostFromList(post, posts)"
|
||||
@pinPost="pinPost(post, refetchPostList)"
|
||||
@unpinPost="unpinPost(post, refetchPostList)"
|
||||
@toggleObservePost="
|
||||
(postId, value) => toggleObservePost(postId, value, refetchPostList)
|
||||
"
|
||||
/>
|
||||
</masonry-grid-item>
|
||||
</template>
|
||||
|
||||
@ -13,6 +13,8 @@ export default (i18n) => {
|
||||
updatedAt
|
||||
disabled
|
||||
deleted
|
||||
isPostObservedByMe
|
||||
postObservingUsersCount
|
||||
author {
|
||||
id
|
||||
slug
|
||||
|
||||
@ -38,4 +38,5 @@ module.exports = {
|
||||
},
|
||||
moduleFileExtensions: ['js', 'json', 'vue'],
|
||||
testEnvironment: 'jest-environment-jsdom',
|
||||
snapshotSerializers: ['jest-serializer-vue'],
|
||||
}
|
||||
|
||||
@ -747,6 +747,9 @@
|
||||
"title": "Benachrichtigungen",
|
||||
"user": "Nutzer"
|
||||
},
|
||||
"observeButton": {
|
||||
"observed": "beobachtet"
|
||||
},
|
||||
"post": {
|
||||
"comment": {
|
||||
"reply": "Antworten",
|
||||
@ -778,8 +781,12 @@
|
||||
"menu": {
|
||||
"delete": "Beitrag löschen",
|
||||
"edit": "Beitrag bearbeiten",
|
||||
"observe": "Beitrag beobachten",
|
||||
"observedSuccessfully": "Du beobachtest diesen Beitrag!",
|
||||
"pin": "Beitrag anheften",
|
||||
"pinnedSuccessfully": "Beitrag erfolgreich angeheftet!",
|
||||
"unobserve": "Beitrag nicht mehr beobachten",
|
||||
"unobservedSuccessfully": "Du beobachtest diesen Beitrag nicht mehr!",
|
||||
"unpin": "Beitrag loslösen",
|
||||
"unpinnedSuccessfully": "Angehefteten Beitrag erfolgreich losgelöst!"
|
||||
},
|
||||
|
||||
@ -747,6 +747,9 @@
|
||||
"title": "Notifications",
|
||||
"user": "User"
|
||||
},
|
||||
"observeButton": {
|
||||
"observed": "observed"
|
||||
},
|
||||
"post": {
|
||||
"comment": {
|
||||
"reply": "Reply",
|
||||
@ -778,8 +781,12 @@
|
||||
"menu": {
|
||||
"delete": "Delete post",
|
||||
"edit": "Edit post",
|
||||
"observe": "Observe post",
|
||||
"observedSuccessfully": "You are now observing this post!",
|
||||
"pin": "Pin post",
|
||||
"pinnedSuccessfully": "Post pinned successfully!",
|
||||
"unobserve": "Stop to observe post",
|
||||
"unobservedSuccessfully": "You are no longer observing this post!",
|
||||
"unpin": "Unpin post",
|
||||
"unpinnedSuccessfully": "Post unpinned successfully!"
|
||||
},
|
||||
|
||||
@ -747,6 +747,9 @@
|
||||
"title": "Notificaciones",
|
||||
"user": "Usuario"
|
||||
},
|
||||
"observeButton": {
|
||||
"observed": null
|
||||
},
|
||||
"post": {
|
||||
"comment": {
|
||||
"reply": "Contestar",
|
||||
@ -778,8 +781,12 @@
|
||||
"menu": {
|
||||
"delete": "Borrar contribución",
|
||||
"edit": "Editar contribución",
|
||||
"observe": "Observar contribución",
|
||||
"observedSuccessfully": null,
|
||||
"pin": "Anclar contribución",
|
||||
"pinnedSuccessfully": "¡Contribución anclado con éxito!",
|
||||
"unobserve": "Dejar de observar contribución",
|
||||
"unobservedSuccessfully": null,
|
||||
"unpin": "Desanclar contribución",
|
||||
"unpinnedSuccessfully": "¡Contribución desanclado con éxito!"
|
||||
},
|
||||
|
||||
@ -747,6 +747,9 @@
|
||||
"title": "Notifications",
|
||||
"user": "Utilisateur"
|
||||
},
|
||||
"observeButton": {
|
||||
"observed": null
|
||||
},
|
||||
"post": {
|
||||
"comment": {
|
||||
"reply": null,
|
||||
@ -778,8 +781,12 @@
|
||||
"menu": {
|
||||
"delete": "Supprimer le Post",
|
||||
"edit": "Modifier le Post",
|
||||
"observe": "Observer le Post",
|
||||
"observedSuccessfully": null,
|
||||
"pin": "Épingler le Post",
|
||||
"pinnedSuccessfully": "Poste épinglé avec succès!",
|
||||
"unobserve": "Ne plus observer le Post",
|
||||
"unobservedSuccessfully": null,
|
||||
"unpin": "Retirer l'épingle du poste",
|
||||
"unpinnedSuccessfully": "Épingle retirer du Post avec succès!"
|
||||
},
|
||||
|
||||
@ -747,6 +747,9 @@
|
||||
"title": null,
|
||||
"user": null
|
||||
},
|
||||
"observeButton": {
|
||||
"observed": null
|
||||
},
|
||||
"post": {
|
||||
"comment": {
|
||||
"reply": null,
|
||||
@ -778,8 +781,12 @@
|
||||
"menu": {
|
||||
"delete": null,
|
||||
"edit": null,
|
||||
"observe": null,
|
||||
"observedSuccessfully": null,
|
||||
"pin": null,
|
||||
"pinnedSuccessfully": null,
|
||||
"unobserve": null,
|
||||
"unobservedSuccessfully": null,
|
||||
"unpin": null,
|
||||
"unpinnedSuccessfully": null
|
||||
},
|
||||
|
||||
@ -747,6 +747,9 @@
|
||||
"title": null,
|
||||
"user": null
|
||||
},
|
||||
"observeButton": {
|
||||
"observed": null
|
||||
},
|
||||
"post": {
|
||||
"comment": {
|
||||
"reply": null,
|
||||
@ -778,8 +781,12 @@
|
||||
"menu": {
|
||||
"delete": null,
|
||||
"edit": null,
|
||||
"observe": null,
|
||||
"observedSuccessfully": null,
|
||||
"pin": null,
|
||||
"pinnedSuccessfully": null,
|
||||
"unobserve": null,
|
||||
"unobservedSuccessfully": null,
|
||||
"unpin": null,
|
||||
"unpinnedSuccessfully": null
|
||||
},
|
||||
|
||||
@ -747,6 +747,9 @@
|
||||
"title": null,
|
||||
"user": null
|
||||
},
|
||||
"observeButton": {
|
||||
"observed": null
|
||||
},
|
||||
"post": {
|
||||
"comment": {
|
||||
"reply": null,
|
||||
@ -778,8 +781,12 @@
|
||||
"menu": {
|
||||
"delete": "Usuń wpis",
|
||||
"edit": "Edytuj wpis",
|
||||
"observe": null,
|
||||
"observedSuccessfully": null,
|
||||
"pin": null,
|
||||
"pinnedSuccessfully": null,
|
||||
"unobserve": null,
|
||||
"unobservedSuccessfully": null,
|
||||
"unpin": null,
|
||||
"unpinnedSuccessfully": null
|
||||
},
|
||||
|
||||
@ -747,6 +747,9 @@
|
||||
"title": "Notificações",
|
||||
"user": "Usuário"
|
||||
},
|
||||
"observeButton": {
|
||||
"observed": null
|
||||
},
|
||||
"post": {
|
||||
"comment": {
|
||||
"reply": null,
|
||||
@ -778,8 +781,12 @@
|
||||
"menu": {
|
||||
"delete": "Excluir publicação",
|
||||
"edit": "Editar publicação",
|
||||
"observe": "Observar publicação",
|
||||
"observedSuccessfully": null,
|
||||
"pin": "Fixar publicação",
|
||||
"pinnedSuccessfully": "Publicação fixada com sucesso!",
|
||||
"unobserve": "Deixar de observar publicação",
|
||||
"unobservedSuccessfully": null,
|
||||
"unpin": "Desafixar publicação",
|
||||
"unpinnedSuccessfully": "Publicação desafixada com sucesso!"
|
||||
},
|
||||
|
||||
@ -747,6 +747,9 @@
|
||||
"title": "Уведомления",
|
||||
"user": "Пользователь"
|
||||
},
|
||||
"observeButton": {
|
||||
"observed": null
|
||||
},
|
||||
"post": {
|
||||
"comment": {
|
||||
"reply": "Ответ",
|
||||
@ -778,8 +781,12 @@
|
||||
"menu": {
|
||||
"delete": "Удалить пост",
|
||||
"edit": "Редактировать пост",
|
||||
"observe": null,
|
||||
"observedSuccessfully": null,
|
||||
"pin": "Закрепить пост",
|
||||
"pinnedSuccessfully": "Пост больше не закреплен!",
|
||||
"unobserve": null,
|
||||
"unobservedSuccessfully": null,
|
||||
"unpin": "Открепить пост",
|
||||
"unpinnedSuccessfully": "Пост успешно не закреплено!"
|
||||
},
|
||||
|
||||
@ -35,5 +35,23 @@ export default {
|
||||
})
|
||||
.catch((error) => this.$toast.error(error.message))
|
||||
},
|
||||
toggleObservePost(postId, value, refetchPostList = () => {}) {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: PostMutations().toggleObservePost,
|
||||
variables: {
|
||||
value,
|
||||
id: postId,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
const message = this.$t(
|
||||
`post.menu.${value ? 'observedSuccessfully' : 'unobservedSuccessfully'}`,
|
||||
)
|
||||
this.$toast.success(message)
|
||||
refetchPostList()
|
||||
})
|
||||
.catch((error) => this.$toast.error(error.message))
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -40,6 +40,7 @@
|
||||
"express": "~4.21.2",
|
||||
"graphql": "~14.7.0",
|
||||
"intersection-observer": "^0.12.0",
|
||||
"jest-serializer-vue": "^3.1.0",
|
||||
"jsonwebtoken": "~9.0.2",
|
||||
"linkify-it": "~5.0.0",
|
||||
"mapbox-gl": "1.13.2",
|
||||
@ -47,7 +48,7 @@
|
||||
"nuxt": "~2.12.1",
|
||||
"nuxt-dropzone": "^1.0.4",
|
||||
"nuxt-env": "~0.1.0",
|
||||
"sass": "^1.85.0",
|
||||
"sass": "^1.86.3",
|
||||
"stack-utils": "^2.0.3",
|
||||
"tippy.js": "^4.3.5",
|
||||
"tiptap": "~1.26.6",
|
||||
@ -55,7 +56,7 @@
|
||||
"trunc-html": "^1.1.2",
|
||||
"v-mapbox": "^1.11.2",
|
||||
"v-tooltip": "~2.1.3",
|
||||
"validator": "^13.12.0",
|
||||
"validator": "^13.15.0",
|
||||
"vue-advanced-chat": "^2.0.11",
|
||||
"vue-count-to": "~1.0.13",
|
||||
"vue-infinite-loading": "^2.4.5",
|
||||
@ -72,7 +73,7 @@
|
||||
"@babel/core": "^7.25.8",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/preset-env": "^7.25.8",
|
||||
"@faker-js/faker": "9.5.0",
|
||||
"@faker-js/faker": "9.6.0",
|
||||
"@storybook/addon-a11y": "^8.0.8",
|
||||
"@storybook/addon-actions": "^5.3.21",
|
||||
"@storybook/addon-notes": "^5.3.18",
|
||||
@ -92,7 +93,7 @@
|
||||
"core-js": "~2.6.10",
|
||||
"css-loader": "~3.5.2",
|
||||
"eslint": "^7.28.0",
|
||||
"eslint-config-prettier": "~10.0.1",
|
||||
"eslint-config-prettier": "~10.1.1",
|
||||
"eslint-config-standard": "~15.0.1",
|
||||
"eslint-loader": "~4.0.0",
|
||||
"eslint-plugin-import": "~2.31.0",
|
||||
@ -101,13 +102,13 @@
|
||||
"eslint-plugin-prettier": "^5.2.3",
|
||||
"eslint-plugin-promise": "~7.2.1",
|
||||
"eslint-plugin-standard": "~5.0.0",
|
||||
"eslint-plugin-vue": "~9.32.0",
|
||||
"eslint-plugin-vue": "~9.33.0",
|
||||
"flush-promises": "^1.0.2",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "29.7",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"mutation-observer": "^1.0.3",
|
||||
"prettier": "~3.5.2",
|
||||
"prettier": "~3.5.3",
|
||||
"sass-loader": "~10.1.1",
|
||||
"storybook-design-token": "^0.8.1",
|
||||
"storybook-vue-router": "^1.0.7",
|
||||
|
||||
@ -283,6 +283,9 @@
|
||||
@removePostFromList="posts = removePostFromList(post, posts)"
|
||||
@pinPost="pinPost(post, refetchPostList)"
|
||||
@unpinPost="unpinPost(post, refetchPostList)"
|
||||
@toggleObservePost="
|
||||
(postId, value) => toggleObservePost(postId, value, refetchPostList)
|
||||
"
|
||||
/>
|
||||
</masonry-grid-item>
|
||||
</template>
|
||||
|
||||
@ -116,6 +116,9 @@
|
||||
@removePostFromList="posts = removePostFromList(post, posts)"
|
||||
@pinPost="pinPost(post, refetchPostList)"
|
||||
@unpinPost="unpinPost(post, refetchPostList)"
|
||||
@toggleObservePost="
|
||||
(postId, value) => toggleObservePost(postId, value, refetchPostList)
|
||||
"
|
||||
/>
|
||||
</masonry-grid-item>
|
||||
</template>
|
||||
|
||||
@ -49,6 +49,7 @@
|
||||
:is-owner="isAuthor"
|
||||
@pinPost="pinPost"
|
||||
@unpinPost="unpinPost"
|
||||
@toggleObservePost="toggleObservePost"
|
||||
/>
|
||||
</client-only>
|
||||
</section>
|
||||
@ -111,6 +112,18 @@
|
||||
:post-id="post.id"
|
||||
/>
|
||||
</ds-flex-item>
|
||||
<!-- Follow Button -->
|
||||
<ds-flex-item
|
||||
:width="{ lg: '15%', md: '22%', sm: '22%', base: '100%' }"
|
||||
class="shout-button"
|
||||
>
|
||||
<observe-button
|
||||
:is-observed="post.isObservedByMe"
|
||||
:count="post.observingUsersCount"
|
||||
:post-id="post.id"
|
||||
@toggleObservePost="toggleObservePost"
|
||||
/>
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
</ds-space>
|
||||
<!-- Comments -->
|
||||
@ -156,6 +169,7 @@ import ContentMenu from '~/components/ContentMenu/ContentMenu'
|
||||
import DateTimeRange from '~/components/DateTimeRange/DateTimeRange'
|
||||
import UserTeaser from '~/components/UserTeaser/UserTeaser'
|
||||
import HcShoutButton from '~/components/ShoutButton.vue'
|
||||
import ObserveButton from '~/components/ObserveButton.vue'
|
||||
import LocationTeaser from '~/components/LocationTeaser/LocationTeaser'
|
||||
import PageParamsLink from '~/components/_new/features/PageParamsLink/PageParamsLink.vue'
|
||||
import {
|
||||
@ -184,6 +198,7 @@ export default {
|
||||
HcCategory,
|
||||
HcHashtag,
|
||||
HcShoutButton,
|
||||
ObserveButton,
|
||||
LocationTeaser,
|
||||
PageParamsLink,
|
||||
UserTeaser,
|
||||
@ -302,6 +317,8 @@ export default {
|
||||
},
|
||||
async createComment(comment) {
|
||||
this.post.comments.push(comment)
|
||||
this.post.isObservedByMe = comment.isPostObservedByMe
|
||||
this.post.observingUsersCount = comment.postObservingUsersCount
|
||||
},
|
||||
pinPost(post) {
|
||||
this.$apollo
|
||||
@ -325,6 +342,24 @@ export default {
|
||||
})
|
||||
.catch((error) => this.$toast.error(error.message))
|
||||
},
|
||||
toggleObservePost(postId, value) {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: PostMutations().toggleObservePost,
|
||||
variables: {
|
||||
value,
|
||||
id: postId,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
const message = this.$t(
|
||||
`post.menu.${value ? 'observedSuccessfully' : 'unobservedSuccessfully'}`,
|
||||
)
|
||||
this.$toast.success(message)
|
||||
this.$apollo.queries.Post.refetch()
|
||||
})
|
||||
.catch((error) => this.$toast.error(error.message))
|
||||
},
|
||||
toggleNewCommentForm(showNewCommentForm) {
|
||||
this.showNewCommentForm = showNewCommentForm
|
||||
},
|
||||
@ -379,7 +414,7 @@ export default {
|
||||
position: relative;
|
||||
/* The padding top makes sure the correct height is set (according to the
|
||||
hero image aspect ratio) before the hero image loads so
|
||||
the autoscroll works correctly when following a comment link.
|
||||
the autoscroll works correctly when following a comment link.
|
||||
*/
|
||||
|
||||
padding-top: calc(var(--hero-image-aspect-ratio) * (100% + 48px));
|
||||
|
||||
@ -156,6 +156,9 @@
|
||||
@removePostFromList="posts = removePostFromList(post, posts)"
|
||||
@pinPost="pinPost(post, refetchPostList)"
|
||||
@unpinPost="unpinPost(post, refetchPostList)"
|
||||
@toggleObservePost="
|
||||
(postId, value) => toggleObservePost(postId, value, refetchPostList)
|
||||
"
|
||||
/>
|
||||
</masonry-grid-item>
|
||||
</template>
|
||||
|
||||
@ -2447,10 +2447,10 @@
|
||||
minimatch "^3.0.4"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
"@faker-js/faker@9.5.0":
|
||||
version "9.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.5.0.tgz#ce254c83706250ca8a5a0e05683608160610dd84"
|
||||
integrity sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw==
|
||||
"@faker-js/faker@9.6.0":
|
||||
version "9.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.6.0.tgz#64235d20330b142eef3d1d1638ba56c083b4bf1d"
|
||||
integrity sha512-3vm4by+B5lvsFPSyep3ELWmZfE3kicDtmemVpuwl1yH7tqtnHdsA6hG8fbXedMVdkzgtvzWoRgjSB4Q+FHnZiw==
|
||||
|
||||
"@hapi/address@2.x.x":
|
||||
version "2.0.0"
|
||||
@ -9425,10 +9425,10 @@ eslint-config-prettier@^6.0.0:
|
||||
dependencies:
|
||||
get-stdin "^6.0.0"
|
||||
|
||||
eslint-config-prettier@~10.0.1:
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz#fbb03bfc8db0651df9ce4e8b7150d11c5fe3addf"
|
||||
integrity sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==
|
||||
eslint-config-prettier@~10.1.1:
|
||||
version "10.1.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.1.1.tgz#cf0ff6e5c4e7e15f129f1f1ce2a5ecba92dec132"
|
||||
integrity sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw==
|
||||
|
||||
eslint-config-standard@~15.0.1:
|
||||
version "15.0.1"
|
||||
@ -9534,10 +9534,10 @@ eslint-plugin-standard@~5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-5.0.0.tgz#c43f6925d669f177db46f095ea30be95476b1ee4"
|
||||
integrity sha512-eSIXPc9wBM4BrniMzJRBm2uoVuXz2EPa+NXPk2+itrVt+r5SbKFERx/IgrK/HmfjddyKVz2f+j+7gBRvu19xLg==
|
||||
|
||||
eslint-plugin-vue@~9.32.0:
|
||||
version "9.32.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.32.0.tgz#2b558e827886b567dfaa156cc1cad0f596461fab"
|
||||
integrity sha512-b/Y05HYmnB/32wqVcjxjHZzNpwxj1onBOvqW89W+V+XNG1dRuaFbNd3vT9CLbr2LXjEoq+3vn8DanWf7XU22Ug==
|
||||
eslint-plugin-vue@~9.33.0:
|
||||
version "9.33.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.33.0.tgz#de33eba8f78e1d172c59c8ec7fbfd60c6ca35c39"
|
||||
integrity sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.4.0"
|
||||
globals "^13.24.0"
|
||||
@ -12609,6 +12609,13 @@ jest-runtime@^29.7.0:
|
||||
slash "^3.0.0"
|
||||
strip-bom "^4.0.0"
|
||||
|
||||
jest-serializer-vue@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-serializer-vue/-/jest-serializer-vue-3.1.0.tgz#af65817aa416d019f837b6cc53f121a3222846f4"
|
||||
integrity sha512-vXz9/3IgBbLhsaVANYLG4ROCQd+Wg3qbB6ICofzFL+fbhSFPlqb0/MMGXcueVsjaovdWlYiRaLQLpdi1PTcoRQ==
|
||||
dependencies:
|
||||
pretty "2.0.0"
|
||||
|
||||
jest-snapshot@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5"
|
||||
@ -16118,10 +16125,10 @@ prettier@^1.18.2:
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
|
||||
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
|
||||
|
||||
prettier@~3.5.2:
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.2.tgz#d066c6053200da0234bf8fa1ef45168abed8b914"
|
||||
integrity sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==
|
||||
prettier@~3.5.3:
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.3.tgz#4fc2ce0d657e7a02e602549f053b239cb7dfe1b5"
|
||||
integrity sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==
|
||||
|
||||
pretty-bytes@^5.3.0:
|
||||
version "5.3.0"
|
||||
@ -16155,7 +16162,7 @@ pretty-time@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e"
|
||||
integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==
|
||||
|
||||
pretty@^2.0.0:
|
||||
pretty@2.0.0, pretty@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty/-/pretty-2.0.0.tgz#adbc7960b7bbfe289a557dc5f737619a220d06a5"
|
||||
integrity sha512-G9xUchgTEiNpormdYBl+Pha50gOUovT18IvAe7EYMZ1/f9W/WWMPRn+xI68yXNMUk3QXHDwo/1wV/4NejVNe1w==
|
||||
@ -17419,10 +17426,10 @@ sass-resources-loader@^2.2.1:
|
||||
glob "^7.1.6"
|
||||
loader-utils "^2.0.0"
|
||||
|
||||
sass@^1.85.0:
|
||||
version "1.85.0"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.85.0.tgz#0127ef697d83144496401553f0a0e87be83df45d"
|
||||
integrity sha512-3ToiC1xZ1Y8aU7+CkgCI/tqyuPXEmYGJXO7H4uqp0xkLXUqp88rQQ4j1HmP37xSJLbCJPaIiv+cT1y+grssrww==
|
||||
sass@^1.86.3:
|
||||
version "1.86.3"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.86.3.tgz#0a0d9ea97cb6665e73f409639f8533ce057464c9"
|
||||
integrity sha512-iGtg8kus4GrsGLRDLRBRHY9dNVA78ZaS7xr01cWnS7PEMQyFtTqBiyCrfpTYTZXRWM94akzckYjh8oADfFNTzw==
|
||||
dependencies:
|
||||
chokidar "^4.0.0"
|
||||
immutable "^5.0.2"
|
||||
@ -17816,12 +17823,7 @@ source-list-map@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
|
||||
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
|
||||
|
||||
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
|
||||
integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
|
||||
|
||||
source-map-js@^1.0.2, source-map-js@^1.2.0:
|
||||
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2, source-map-js@^1.2.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||
@ -19659,10 +19661,10 @@ validate-npm-package-license@^3.0.1:
|
||||
spdx-correct "^3.0.0"
|
||||
spdx-expression-parse "^3.0.0"
|
||||
|
||||
validator@^13.12.0:
|
||||
version "13.12.0"
|
||||
resolved "https://registry.yarnpkg.com/validator/-/validator-13.12.0.tgz#7d78e76ba85504da3fee4fd1922b385914d4b35f"
|
||||
integrity sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==
|
||||
validator@^13.15.0:
|
||||
version "13.15.0"
|
||||
resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.0.tgz#2dc7ce057e7513a55585109eec29b2c8e8c1aefd"
|
||||
integrity sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA==
|
||||
|
||||
vary@^1, vary@^1.1.2, vary@~1.1.2:
|
||||
version "1.1.2"
|
||||
|
||||
96
yarn.lock
96
yarn.lock
@ -52,34 +52,34 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367"
|
||||
integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==
|
||||
|
||||
"@babel/core@^7.16.0", "@babel/core@^7.26.9":
|
||||
version "7.26.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.9.tgz#71838542a4b1e49dfed353d7acbc6eb89f4a76f2"
|
||||
integrity sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==
|
||||
"@babel/core@^7.16.0", "@babel/core@^7.26.10":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9"
|
||||
integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==
|
||||
dependencies:
|
||||
"@ampproject/remapping" "^2.2.0"
|
||||
"@babel/code-frame" "^7.26.2"
|
||||
"@babel/generator" "^7.26.9"
|
||||
"@babel/generator" "^7.26.10"
|
||||
"@babel/helper-compilation-targets" "^7.26.5"
|
||||
"@babel/helper-module-transforms" "^7.26.0"
|
||||
"@babel/helpers" "^7.26.9"
|
||||
"@babel/parser" "^7.26.9"
|
||||
"@babel/helpers" "^7.26.10"
|
||||
"@babel/parser" "^7.26.10"
|
||||
"@babel/template" "^7.26.9"
|
||||
"@babel/traverse" "^7.26.9"
|
||||
"@babel/types" "^7.26.9"
|
||||
"@babel/traverse" "^7.26.10"
|
||||
"@babel/types" "^7.26.10"
|
||||
convert-source-map "^2.0.0"
|
||||
debug "^4.1.0"
|
||||
gensync "^1.0.0-beta.2"
|
||||
json5 "^2.2.3"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/generator@^7.26.9":
|
||||
version "7.26.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.9.tgz#75a9482ad3d0cc7188a537aa4910bc59db67cbca"
|
||||
integrity sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==
|
||||
"@babel/generator@^7.26.10", "@babel/generator@^7.27.0":
|
||||
version "7.27.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.27.0.tgz#764382b5392e5b9aff93cadb190d0745866cbc2c"
|
||||
integrity sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.26.9"
|
||||
"@babel/types" "^7.26.9"
|
||||
"@babel/parser" "^7.27.0"
|
||||
"@babel/types" "^7.27.0"
|
||||
"@jridgewell/gen-mapping" "^0.3.5"
|
||||
"@jridgewell/trace-mapping" "^0.3.25"
|
||||
jsesc "^3.0.2"
|
||||
@ -343,20 +343,20 @@
|
||||
"@babel/traverse" "^7.25.9"
|
||||
"@babel/types" "^7.25.9"
|
||||
|
||||
"@babel/helpers@^7.26.9":
|
||||
version "7.26.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.9.tgz#28f3fb45252fc88ef2dc547c8a911c255fc9fef6"
|
||||
integrity sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==
|
||||
"@babel/helpers@^7.26.10":
|
||||
version "7.27.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.0.tgz#53d156098defa8243eab0f32fa17589075a1b808"
|
||||
integrity sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==
|
||||
dependencies:
|
||||
"@babel/template" "^7.26.9"
|
||||
"@babel/types" "^7.26.9"
|
||||
"@babel/template" "^7.27.0"
|
||||
"@babel/types" "^7.27.0"
|
||||
|
||||
"@babel/parser@^7.24.4", "@babel/parser@^7.24.7", "@babel/parser@^7.25.3", "@babel/parser@^7.26.9":
|
||||
version "7.26.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.9.tgz#d9e78bee6dc80f9efd8f2349dcfbbcdace280fd5"
|
||||
integrity sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==
|
||||
"@babel/parser@^7.24.4", "@babel/parser@^7.24.7", "@babel/parser@^7.25.3", "@babel/parser@^7.26.10", "@babel/parser@^7.27.0":
|
||||
version "7.27.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.0.tgz#3d7d6ee268e41d2600091cbd4e145ffee85a44ec"
|
||||
integrity sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==
|
||||
dependencies:
|
||||
"@babel/types" "^7.26.9"
|
||||
"@babel/types" "^7.27.0"
|
||||
|
||||
"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9":
|
||||
version "7.25.9"
|
||||
@ -1015,32 +1015,32 @@
|
||||
dependencies:
|
||||
regenerator-runtime "^0.14.0"
|
||||
|
||||
"@babel/template@^7.22.15", "@babel/template@^7.25.9", "@babel/template@^7.26.9":
|
||||
version "7.26.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2"
|
||||
integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==
|
||||
"@babel/template@^7.22.15", "@babel/template@^7.25.9", "@babel/template@^7.26.9", "@babel/template@^7.27.0":
|
||||
version "7.27.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.0.tgz#b253e5406cc1df1c57dcd18f11760c2dbf40c0b4"
|
||||
integrity sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.26.2"
|
||||
"@babel/parser" "^7.26.9"
|
||||
"@babel/types" "^7.26.9"
|
||||
"@babel/parser" "^7.27.0"
|
||||
"@babel/types" "^7.27.0"
|
||||
|
||||
"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.8", "@babel/traverse@^7.26.9":
|
||||
version "7.26.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.9.tgz#4398f2394ba66d05d988b2ad13c219a2c857461a"
|
||||
integrity sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==
|
||||
"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.8":
|
||||
version "7.27.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.0.tgz#11d7e644779e166c0442f9a07274d02cd91d4a70"
|
||||
integrity sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.26.2"
|
||||
"@babel/generator" "^7.26.9"
|
||||
"@babel/parser" "^7.26.9"
|
||||
"@babel/template" "^7.26.9"
|
||||
"@babel/types" "^7.26.9"
|
||||
"@babel/generator" "^7.27.0"
|
||||
"@babel/parser" "^7.27.0"
|
||||
"@babel/template" "^7.27.0"
|
||||
"@babel/types" "^7.27.0"
|
||||
debug "^4.3.1"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.25.9", "@babel/types@^7.26.9", "@babel/types@^7.4.4":
|
||||
version "7.26.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.9.tgz#08b43dec79ee8e682c2ac631c010bdcac54a21ce"
|
||||
integrity sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==
|
||||
"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.27.0", "@babel/types@^7.4.4":
|
||||
version "7.27.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.0.tgz#ef9acb6b06c3173f6632d993ecb6d4ae470b4559"
|
||||
integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==
|
||||
dependencies:
|
||||
"@babel/helper-string-parser" "^7.25.9"
|
||||
"@babel/helper-validator-identifier" "^7.25.9"
|
||||
@ -1543,10 +1543,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz#81fd50d11e2c32b2d6241470e3185b70c7b30699"
|
||||
integrity sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==
|
||||
|
||||
"@faker-js/faker@9.5.0":
|
||||
version "9.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.5.0.tgz#ce254c83706250ca8a5a0e05683608160610dd84"
|
||||
integrity sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw==
|
||||
"@faker-js/faker@9.6.0":
|
||||
version "9.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.6.0.tgz#64235d20330b142eef3d1d1638ba56c083b4bf1d"
|
||||
integrity sha512-3vm4by+B5lvsFPSyep3ELWmZfE3kicDtmemVpuwl1yH7tqtnHdsA6hG8fbXedMVdkzgtvzWoRgjSB4Q+FHnZiw==
|
||||
|
||||
"@fastify/busboy@^2.0.0":
|
||||
version "2.1.1"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user