Merge branch 'master' into 6443-refactor-create-post-page

This commit is contained in:
Markus 2023-07-13 18:50:04 +02:00
commit 9662e729aa
28 changed files with 1765 additions and 1686 deletions

View File

@ -24,22 +24,37 @@ jobs:
- 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
- name: Full stack tests | prepare
run: |
wget --no-verbose -O /opt/cucumber-json-formatter "https://github.com/cucumber/json-formatter/releases/download/v19.0.0/cucumber-json-formatter-linux-386"
chmod +x /opt/cucumber-json-formatter
sudo ln -fs /opt/cucumber-json-formatter /usr/bin/cucumber-json-formatter
cd backend
yarn install
yarn build
cd ..
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
- name: Full stack tests | run tests
id: e2e-tests
run: yarn run cypress:run --spec $(cypress/parallel-features.sh ${{ matrix.job }} ${{ env.jobs }} )
- name: Full stack tests | if tests failed, compile html report
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
run: |
cd cypress/
node create-cucumber-html-report.js
- name: End-to-end tests | if tests failed, get pr number
id: pr
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
uses: 8BitJonny/gh-get-current-pr@2.2.0
- name: End-to-end tests | if tests failed, upload report
id: e2e-report
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
uses: actions/upload-artifact@v3
with:
name: cypress-screenshots
path: cypress/screenshots/
name: ocelot-e2e-test-report-pr${{ steps.pr.outputs.number }}
path: /home/runner/work/Ocelot-Social/Ocelot-Social/cypress/reports/cucumber_html_report

View File

@ -11,7 +11,7 @@ module.exports = {
],
coverageThreshold: {
global: {
lines: 70,
lines: 67,
},
},
testMatch: ['**/src/**/?(*.)+(spec|test).ts?(x)'],

View File

@ -38,8 +38,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
})
const { mutate } = createTestClient(server)
const [Hamburg, Berlin, Germany, Paris, France] = await Promise.all([
Factory.build('location', {
// locations
const Hamburg = await Factory.build('location', {
id: 'region.5127278006398860',
name: 'Hamburg',
type: 'region',
@ -54,8 +54,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
nameNL: 'Hamburg',
namePL: 'Hamburg',
nameRU: 'Гамбург',
}),
Factory.build('location', {
})
const Berlin = await Factory.build('location', {
id: 'region.14880313158564380',
type: 'region',
name: 'Berlin',
@ -70,8 +70,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
nameNL: 'Berlijn',
namePL: 'Berlin',
nameRU: 'Берлин',
}),
Factory.build('location', {
})
const Germany = await Factory.build('location', {
id: 'country.10743216036480410',
name: 'Germany',
type: 'country',
@ -84,8 +84,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
nameIT: 'Germania',
nameEN: 'Germany',
nameRU: 'Германия',
}),
Factory.build('location', {
})
const Paris = await Factory.build('location', {
id: 'region.9397217726497330',
name: 'Paris',
type: 'region',
@ -100,8 +100,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
nameNL: 'Parijs',
namePL: 'Paryż',
nameRU: 'Париж',
}),
Factory.build('location', {
})
const France = await Factory.build('location', {
id: 'country.9759535382641660',
name: 'France',
type: 'country',
@ -114,44 +114,39 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
nameIT: 'Francia',
nameEN: 'France',
nameRU: 'Франция',
}),
])
await Promise.all([
Berlin.relateTo(Germany, 'isIn'),
Hamburg.relateTo(Germany, 'isIn'),
Paris.relateTo(France, 'isIn'),
])
})
await Berlin.relateTo(Germany, 'isIn')
await Hamburg.relateTo(Germany, 'isIn')
await Paris.relateTo(France, 'isIn')
const [racoon, rabbit, wolf, bear, turtle, rhino] = await Promise.all([
Factory.build('badge', {
// badges
const racoon = await Factory.build('badge', {
id: 'indiegogo_en_racoon',
icon: '/img/badges/indiegogo_en_racoon.svg',
}),
Factory.build('badge', {
})
const rabbit = await Factory.build('badge', {
id: 'indiegogo_en_rabbit',
icon: '/img/badges/indiegogo_en_rabbit.svg',
}),
Factory.build('badge', {
})
const wolf = await Factory.build('badge', {
id: 'indiegogo_en_wolf',
icon: '/img/badges/indiegogo_en_wolf.svg',
}),
Factory.build('badge', {
})
const bear = await Factory.build('badge', {
id: 'indiegogo_en_bear',
icon: '/img/badges/indiegogo_en_bear.svg',
}),
Factory.build('badge', {
})
const turtle = await Factory.build('badge', {
id: 'indiegogo_en_turtle',
icon: '/img/badges/indiegogo_en_turtle.svg',
}),
Factory.build('badge', {
})
const rhino = await Factory.build('badge', {
id: 'indiegogo_en_rhino',
icon: '/img/badges/indiegogo_en_rhino.svg',
}),
])
})
const [peterLustig, bobDerBaumeister, jennyRostock, huey, dewey, louie, dagobert] =
await Promise.all([
Factory.build(
// users
const peterLustig = await Factory.build(
'user',
{
id: 'u1',
@ -162,8 +157,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
{
email: 'admin@example.org',
},
),
Factory.build(
)
const bobDerBaumeister = await Factory.build(
'user',
{
id: 'u2',
@ -175,8 +170,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
email: 'moderator@example.org',
avatar: null,
},
),
Factory.build(
)
const jennyRostock = await Factory.build(
'user',
{
id: 'u3',
@ -187,8 +182,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
{
email: 'user@example.org',
},
),
Factory.build(
)
const huey = await Factory.build(
'user',
{
id: 'u4',
@ -199,8 +194,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
{
email: 'huey@example.org',
},
),
Factory.build(
)
const dewey = await Factory.build(
'user',
{
id: 'u5',
@ -212,8 +207,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
email: 'dewey@example.org',
avatar: null,
},
),
Factory.build(
)
const louie = await Factory.build(
'user',
{
id: 'u6',
@ -224,8 +219,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
{
email: 'louie@example.org',
},
),
Factory.build(
)
const dagobert = await Factory.build(
'user',
{
id: 'u7',
@ -236,46 +231,42 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
{
email: 'dagobert@example.org',
},
),
])
)
await Promise.all([
peterLustig.relateTo(Berlin, 'isIn'),
bobDerBaumeister.relateTo(Hamburg, 'isIn'),
jennyRostock.relateTo(Paris, 'isIn'),
huey.relateTo(Paris, 'isIn'),
])
await peterLustig.relateTo(Berlin, 'isIn')
await bobDerBaumeister.relateTo(Hamburg, 'isIn')
await jennyRostock.relateTo(Paris, 'isIn')
await huey.relateTo(Paris, 'isIn')
await Promise.all([
peterLustig.relateTo(racoon, 'rewarded'),
peterLustig.relateTo(rhino, 'rewarded'),
peterLustig.relateTo(wolf, 'rewarded'),
bobDerBaumeister.relateTo(racoon, 'rewarded'),
bobDerBaumeister.relateTo(turtle, 'rewarded'),
jennyRostock.relateTo(bear, 'rewarded'),
dagobert.relateTo(rabbit, 'rewarded'),
await peterLustig.relateTo(racoon, 'rewarded')
await peterLustig.relateTo(rhino, 'rewarded')
await peterLustig.relateTo(wolf, 'rewarded')
await bobDerBaumeister.relateTo(racoon, 'rewarded')
await bobDerBaumeister.relateTo(turtle, 'rewarded')
await jennyRostock.relateTo(bear, 'rewarded')
await dagobert.relateTo(rabbit, 'rewarded')
peterLustig.relateTo(bobDerBaumeister, 'friends'),
peterLustig.relateTo(jennyRostock, 'friends'),
bobDerBaumeister.relateTo(jennyRostock, 'friends'),
await peterLustig.relateTo(bobDerBaumeister, 'friends')
await peterLustig.relateTo(jennyRostock, 'friends')
await bobDerBaumeister.relateTo(jennyRostock, 'friends')
peterLustig.relateTo(jennyRostock, 'following'),
peterLustig.relateTo(huey, 'following'),
bobDerBaumeister.relateTo(huey, 'following'),
jennyRostock.relateTo(huey, 'following'),
huey.relateTo(dewey, 'following'),
dewey.relateTo(huey, 'following'),
louie.relateTo(jennyRostock, 'following'),
await peterLustig.relateTo(jennyRostock, 'following')
await peterLustig.relateTo(huey, 'following')
await bobDerBaumeister.relateTo(huey, 'following')
await jennyRostock.relateTo(huey, 'following')
await huey.relateTo(dewey, 'following')
await dewey.relateTo(huey, 'following')
await louie.relateTo(jennyRostock, 'following')
huey.relateTo(dagobert, 'muted'),
dewey.relateTo(dagobert, 'muted'),
louie.relateTo(dagobert, 'muted'),
await huey.relateTo(dagobert, 'muted')
await dewey.relateTo(dagobert, 'muted')
await louie.relateTo(dagobert, 'muted')
dagobert.relateTo(huey, 'blocked'),
dagobert.relateTo(dewey, 'blocked'),
dagobert.relateTo(louie, 'blocked'),
])
await dagobert.relateTo(huey, 'blocked')
await dagobert.relateTo(dewey, 'blocked')
await dagobert.relateTo(louie, 'blocked')
// categories
await Promise.all(
categories.map(({ icon, name }, index) => {
return Factory.build('category', {
@ -287,23 +278,20 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
}),
)
const [environment, nature, democracy, freedom] = await Promise.all([
Factory.build('tag', {
const environment = await Factory.build('tag', {
id: 'Environment',
}),
Factory.build('tag', {
})
const nature = await Factory.build('tag', {
id: 'Nature',
}),
Factory.build('tag', {
})
const democracy = await Factory.build('tag', {
id: 'Democracy',
}),
Factory.build('tag', {
})
const freedom = await Factory.build('tag', {
id: 'Freedom',
}),
])
// Create Groups
})
// groups
authenticatedUser = await peterLustig.toJson()
await Promise.all([
mutate({
@ -668,10 +656,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
eventStart: new Date(now.getFullYear(), now.getMonth(), now.getDate() - 3).toISOString(),
})
// Create Posts (Articles)
const [p0, p1, p3, p4, p5, p6, p9, p10, p11, p13, p14, p15] = await Promise.all([
Factory.build(
// posts (articles)
const p0 = await Factory.build(
'post',
{
id: 'p0',
@ -686,8 +672,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
aspectRatio: 300 / 169,
}),
},
),
Factory.build(
)
const p1 = await Factory.build(
'post',
{
id: 'p1',
@ -701,8 +687,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
aspectRatio: 300 / 1500,
}),
},
),
Factory.build(
)
const p3 = await Factory.build(
'post',
{
id: 'p3',
@ -712,8 +698,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
categoryIds: ['cat3'],
author: huey,
},
),
Factory.build(
)
const p4 = await Factory.build(
'post',
{
id: 'p4',
@ -723,8 +709,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
categoryIds: ['cat4'],
author: dewey,
},
),
Factory.build(
)
const p5 = await Factory.build(
'post',
{
id: 'p5',
@ -734,8 +720,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
categoryIds: ['cat5'],
author: louie,
},
),
Factory.build(
)
const p6 = await Factory.build(
'post',
{
id: 'p6',
@ -749,8 +735,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
aspectRatio: 300 / 857,
}),
},
),
Factory.build(
)
const p9 = await Factory.build(
'post',
{
id: 'p9',
@ -760,8 +746,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
categoryIds: ['cat9'],
author: huey,
},
),
Factory.build(
)
const p10 = await Factory.build(
'post',
{
id: 'p10',
@ -773,8 +759,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
sensitive: true,
}),
},
),
Factory.build(
)
const p11 = await Factory.build(
'post',
{
id: 'p11',
@ -788,8 +774,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
aspectRatio: 300 / 901,
}),
},
),
Factory.build(
)
const p13 = await Factory.build(
'post',
{
id: 'p13',
@ -799,8 +785,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
categoryIds: ['cat13'],
author: bobDerBaumeister,
},
),
Factory.build(
)
const p14 = await Factory.build(
'post',
{
id: 'p14',
@ -814,8 +800,8 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
aspectRatio: 300 / 450,
}),
},
),
Factory.build(
)
const p15 = await Factory.build(
'post',
{
id: 'p15',
@ -825,9 +811,9 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
categoryIds: ['cat15'],
author: huey,
},
),
])
)
// invite code
await Factory.build(
'inviteCode',
{
@ -924,8 +910,9 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
])
authenticatedUser = null
const comments = await Promise.all([
Factory.build(
const comments: any[] = []
comments.push(
await Factory.build(
'comment',
{
id: 'c1',
@ -935,7 +922,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
postId: 'p1',
},
),
Factory.build(
await Factory.build(
'comment',
{
id: 'c2',
@ -945,7 +932,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
postId: 'p1',
},
),
Factory.build(
await Factory.build(
'comment',
{
id: 'c3',
@ -955,7 +942,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
postId: 'p3',
},
),
Factory.build(
await Factory.build(
'comment',
{
id: 'c5',
@ -965,7 +952,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
postId: 'p3',
},
),
Factory.build(
await Factory.build(
'comment',
{
id: 'c6',
@ -975,7 +962,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
postId: 'p4',
},
),
Factory.build(
await Factory.build(
'comment',
{
id: 'c7',
@ -985,7 +972,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
postId: 'p2',
},
),
Factory.build(
await Factory.build(
'comment',
{
id: 'c8',
@ -995,7 +982,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
postId: 'p15',
},
),
Factory.build(
await Factory.build(
'comment',
{
id: 'c9',
@ -1005,7 +992,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
postId: 'p15',
},
),
Factory.build(
await Factory.build(
'comment',
{
id: 'c10',
@ -1015,7 +1002,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
postId: 'p15',
},
),
Factory.build(
await Factory.build(
'comment',
{
id: 'c11',
@ -1025,7 +1012,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
postId: 'p15',
},
),
Factory.build(
await Factory.build(
'comment',
{
id: 'c12',
@ -1035,84 +1022,81 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
postId: 'p15',
},
),
])
)
const trollingComment = comments[0]
await Promise.all([
democracy.relateTo(p3, 'post'),
democracy.relateTo(p11, 'post'),
democracy.relateTo(p15, 'post'),
democracy.relateTo(p7, 'post'),
environment.relateTo(p1, 'post'),
environment.relateTo(p5, 'post'),
environment.relateTo(p9, 'post'),
environment.relateTo(p13, 'post'),
freedom.relateTo(p0, 'post'),
freedom.relateTo(p4, 'post'),
freedom.relateTo(p8, 'post'),
freedom.relateTo(p12, 'post'),
nature.relateTo(p2, 'post'),
nature.relateTo(p6, 'post'),
nature.relateTo(p10, 'post'),
nature.relateTo(p14, 'post'),
peterLustig.relateTo(p15, 'emoted', { emotion: 'surprised' }),
bobDerBaumeister.relateTo(p15, 'emoted', { emotion: 'surprised' }),
jennyRostock.relateTo(p15, 'emoted', { emotion: 'surprised' }),
huey.relateTo(p15, 'emoted', { emotion: 'surprised' }),
dewey.relateTo(p15, 'emoted', { emotion: 'surprised' }),
louie.relateTo(p15, 'emoted', { emotion: 'surprised' }),
dagobert.relateTo(p15, 'emoted', { emotion: 'surprised' }),
bobDerBaumeister.relateTo(p14, 'emoted', { emotion: 'cry' }),
jennyRostock.relateTo(p13, 'emoted', { emotion: 'angry' }),
huey.relateTo(p12, 'emoted', { emotion: 'funny' }),
dewey.relateTo(p11, 'emoted', { emotion: 'surprised' }),
louie.relateTo(p10, 'emoted', { emotion: 'cry' }),
dewey.relateTo(p9, 'emoted', { emotion: 'happy' }),
huey.relateTo(p8, 'emoted', { emotion: 'angry' }),
jennyRostock.relateTo(p7, 'emoted', { emotion: 'funny' }),
bobDerBaumeister.relateTo(p6, 'emoted', { emotion: 'surprised' }),
peterLustig.relateTo(p5, 'emoted', { emotion: 'cry' }),
bobDerBaumeister.relateTo(p4, 'emoted', { emotion: 'happy' }),
jennyRostock.relateTo(p3, 'emoted', { emotion: 'angry' }),
huey.relateTo(p2, 'emoted', { emotion: 'funny' }),
dewey.relateTo(p1, 'emoted', { emotion: 'surprised' }),
louie.relateTo(p0, 'emoted', { emotion: 'cry' }),
])
await democracy.relateTo(p3, 'post')
await democracy.relateTo(p11, 'post')
await democracy.relateTo(p15, 'post')
await democracy.relateTo(p7, 'post')
await environment.relateTo(p1, 'post')
await environment.relateTo(p5, 'post')
await environment.relateTo(p9, 'post')
await environment.relateTo(p13, 'post')
await freedom.relateTo(p0, 'post')
await freedom.relateTo(p4, 'post')
await freedom.relateTo(p8, 'post')
await freedom.relateTo(p12, 'post')
await nature.relateTo(p2, 'post')
await nature.relateTo(p6, 'post')
await nature.relateTo(p10, 'post')
await nature.relateTo(p14, 'post')
await peterLustig.relateTo(p15, 'emoted', { emotion: 'surprised' })
await bobDerBaumeister.relateTo(p15, 'emoted', { emotion: 'surprised' })
await jennyRostock.relateTo(p15, 'emoted', { emotion: 'surprised' })
await huey.relateTo(p15, 'emoted', { emotion: 'surprised' })
await dewey.relateTo(p15, 'emoted', { emotion: 'surprised' })
await louie.relateTo(p15, 'emoted', { emotion: 'surprised' })
await dagobert.relateTo(p15, 'emoted', { emotion: 'surprised' })
await bobDerBaumeister.relateTo(p14, 'emoted', { emotion: 'cry' })
await jennyRostock.relateTo(p13, 'emoted', { emotion: 'angry' })
await huey.relateTo(p12, 'emoted', { emotion: 'funny' })
await dewey.relateTo(p11, 'emoted', { emotion: 'surprised' })
await louie.relateTo(p10, 'emoted', { emotion: 'cry' })
await dewey.relateTo(p9, 'emoted', { emotion: 'happy' })
await huey.relateTo(p8, 'emoted', { emotion: 'angry' })
await jennyRostock.relateTo(p7, 'emoted', { emotion: 'funny' })
await bobDerBaumeister.relateTo(p6, 'emoted', { emotion: 'surprised' })
await peterLustig.relateTo(p5, 'emoted', { emotion: 'cry' })
await bobDerBaumeister.relateTo(p4, 'emoted', { emotion: 'happy' })
await jennyRostock.relateTo(p3, 'emoted', { emotion: 'angry' })
await huey.relateTo(p2, 'emoted', { emotion: 'funny' })
await dewey.relateTo(p1, 'emoted', { emotion: 'surprised' })
await louie.relateTo(p0, 'emoted', { emotion: 'cry' })
await Promise.all([
peterLustig.relateTo(p1, 'shouted'),
peterLustig.relateTo(p6, 'shouted'),
bobDerBaumeister.relateTo(p0, 'shouted'),
bobDerBaumeister.relateTo(p6, 'shouted'),
jennyRostock.relateTo(p6, 'shouted'),
jennyRostock.relateTo(p7, 'shouted'),
huey.relateTo(p8, 'shouted'),
huey.relateTo(p9, 'shouted'),
dewey.relateTo(p10, 'shouted'),
peterLustig.relateTo(p2, 'shouted'),
peterLustig.relateTo(p6, 'shouted'),
bobDerBaumeister.relateTo(p0, 'shouted'),
bobDerBaumeister.relateTo(p6, 'shouted'),
jennyRostock.relateTo(p6, 'shouted'),
jennyRostock.relateTo(p7, 'shouted'),
huey.relateTo(p8, 'shouted'),
huey.relateTo(p9, 'shouted'),
louie.relateTo(p10, 'shouted'),
])
const reports = await Promise.all([
Factory.build('report'),
Factory.build('report'),
Factory.build('report'),
Factory.build('report'),
])
await peterLustig.relateTo(p1, 'shouted')
await peterLustig.relateTo(p6, 'shouted')
await bobDerBaumeister.relateTo(p0, 'shouted')
await bobDerBaumeister.relateTo(p6, 'shouted')
await jennyRostock.relateTo(p6, 'shouted')
await jennyRostock.relateTo(p7, 'shouted')
await huey.relateTo(p8, 'shouted')
await huey.relateTo(p9, 'shouted')
await dewey.relateTo(p10, 'shouted')
await peterLustig.relateTo(p2, 'shouted')
await peterLustig.relateTo(p6, 'shouted')
await bobDerBaumeister.relateTo(p0, 'shouted')
await bobDerBaumeister.relateTo(p6, 'shouted')
await jennyRostock.relateTo(p6, 'shouted')
await jennyRostock.relateTo(p7, 'shouted')
await huey.relateTo(p8, 'shouted')
await huey.relateTo(p9, 'shouted')
await louie.relateTo(p10, 'shouted')
const reports: any[] = []
reports.push(
await Factory.build('report'),
await Factory.build('report'),
await Factory.build('report'),
await Factory.build('report'),
)
const reportAgainstDagobert = reports[0]
const reportAgainstTrollingPost = reports[1]
const reportAgainstTrollingComment = reports[2]
const reportAgainstDewey = reports[3]
// report resource first time
await reportAgainstDagobert.relateTo(jennyRostock, 'filed', {
resourceId: 'u7',
reasonCategory: 'discrimination_etc',
@ -1139,27 +1123,25 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
await reportAgainstDewey.relateTo(dewey, 'belongsTo')
// report resource a second time
await Promise.all([
reportAgainstDagobert.relateTo(louie, 'filed', {
await reportAgainstDagobert.relateTo(louie, 'filed', {
resourceId: 'u7',
reasonCategory: 'discrimination_etc',
reasonDescription: 'this user is attacking me for who I am!',
}),
reportAgainstDagobert.relateTo(dagobert, 'belongsTo'),
reportAgainstTrollingPost.relateTo(peterLustig, 'filed', {
})
await reportAgainstDagobert.relateTo(dagobert, 'belongsTo')
await reportAgainstTrollingPost.relateTo(peterLustig, 'filed', {
resourceId: 'p2',
reasonCategory: 'discrimination_etc',
reasonDescription: 'This post is bigoted',
}),
reportAgainstTrollingPost.relateTo(p2, 'belongsTo'),
})
await reportAgainstTrollingPost.relateTo(p2, 'belongsTo')
reportAgainstTrollingComment.relateTo(bobDerBaumeister, 'filed', {
await reportAgainstTrollingComment.relateTo(bobDerBaumeister, 'filed', {
resourceId: 'c1',
reasonCategory: 'pornographic_content_links',
reasonDescription: 'This comment is porno!!!',
}),
reportAgainstTrollingComment.relateTo(trollingComment, 'belongsTo'),
])
})
await reportAgainstTrollingComment.relateTo(trollingComment, 'belongsTo')
const disableVariables = {
resourceId: 'undefined-resource',
@ -1168,46 +1150,46 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
}
// review resource first time
await Promise.all([
reportAgainstDagobert.relateTo(bobDerBaumeister, 'reviewed', {
await reportAgainstDagobert.relateTo(bobDerBaumeister, 'reviewed', {
...disableVariables,
resourceId: 'u7',
}),
dagobert.update({ disabled: true, updatedAt: new Date().toISOString() }),
reportAgainstTrollingPost.relateTo(peterLustig, 'reviewed', {
})
await dagobert.update({ disabled: true, updatedAt: new Date().toISOString() })
await reportAgainstTrollingPost.relateTo(peterLustig, 'reviewed', {
...disableVariables,
resourceId: 'p2',
}),
p2.update({ disabled: true, updatedAt: new Date().toISOString() }),
reportAgainstTrollingComment.relateTo(bobDerBaumeister, 'reviewed', {
})
await p2.update({ disabled: true, updatedAt: new Date().toISOString() })
await reportAgainstTrollingComment.relateTo(bobDerBaumeister, 'reviewed', {
...disableVariables,
resourceId: 'c1',
}),
trollingComment.update({ disabled: true, updatedAt: new Date().toISOString() }),
])
})
await trollingComment.update({ disabled: true, updatedAt: new Date().toISOString() })
// second review of resource and close report
await Promise.all([
reportAgainstDagobert.relateTo(peterLustig, 'reviewed', {
await reportAgainstDagobert.relateTo(peterLustig, 'reviewed', {
resourceId: 'u7',
disable: false,
closed: true,
}),
dagobert.update({ disabled: false, updatedAt: new Date().toISOString(), closed: true }),
reportAgainstTrollingPost.relateTo(bobDerBaumeister, 'reviewed', {
})
await dagobert.update({ disabled: false, updatedAt: new Date().toISOString(), closed: true })
await reportAgainstTrollingPost.relateTo(bobDerBaumeister, 'reviewed', {
resourceId: 'p2',
disable: true,
closed: true,
}),
p2.update({ disabled: true, updatedAt: new Date().toISOString(), closed: true }),
reportAgainstTrollingComment.relateTo(peterLustig, 'reviewed', {
})
await p2.update({ disabled: true, updatedAt: new Date().toISOString(), closed: true })
await reportAgainstTrollingComment.relateTo(peterLustig, 'reviewed', {
...disableVariables,
resourceId: 'c1',
disable: true,
closed: true,
}),
trollingComment.update({ disabled: true, updatedAt: new Date().toISOString(), closed: true }),
])
})
await trollingComment.update({
disabled: true,
updatedAt: new Date().toISOString(),
closed: true,
})
const additionalUsers = await Promise.all(
[...Array(30).keys()].map(() => Factory.build('user')),

View File

@ -6,6 +6,9 @@ export const createMessageMutation = () => {
CreateMessage(roomId: $roomId, content: $content) {
id
content
saved
distributed
seen
}
}
`
@ -22,7 +25,18 @@ export const messageQuery = () => {
username
avatar
date
saved
distributed
seen
}
}
`
}
export const markMessagesAsSeen = () => {
return gql`
mutation ($messageIds: [String!]) {
MarkMessagesAsSeen(messageIds: $messageIds)
}
`
}

View File

@ -463,6 +463,7 @@ export default shield(
saveCategorySettings: isAuthenticated,
CreateRoom: isAuthenticated,
CreateMessage: isAuthenticated,
MarkMessagesAsSeen: isAuthenticated,
},
User: {
email: or(isMyOwn, isAdmin),

View File

@ -2,7 +2,7 @@ import { createTestClient } from 'apollo-server-testing'
import Factory, { cleanDatabase } from '../../db/factories'
import { getNeode, getDriver } from '../../db/neo4j'
import { createRoomMutation } from '../../graphql/rooms'
import { createMessageMutation, messageQuery } from '../../graphql/messages'
import { createMessageMutation, messageQuery, markMessagesAsSeen } from '../../graphql/messages'
import createServer from '../../server'
const driver = getDriver()
@ -122,6 +122,9 @@ describe('Message', () => {
CreateMessage: {
id: expect.any(String),
content: 'Some nice message to other chatting user',
saved: true,
distributed: false,
seen: false,
},
},
})
@ -217,6 +220,9 @@ describe('Message', () => {
username: 'Chatting User',
avatar: expect.any(String),
date: expect.any(String),
saved: true,
distributed: true,
seen: false,
},
],
},
@ -261,6 +267,9 @@ describe('Message', () => {
username: 'Chatting User',
avatar: expect.any(String),
date: expect.any(String),
saved: true,
distributed: true,
seen: false,
}),
expect.objectContaining({
id: expect.any(String),
@ -269,6 +278,9 @@ describe('Message', () => {
username: 'Other Chatting User',
avatar: expect.any(String),
date: expect.any(String),
saved: true,
distributed: true,
seen: false,
}),
expect.objectContaining({
id: expect.any(String),
@ -277,6 +289,9 @@ describe('Message', () => {
username: 'Chatting User',
avatar: expect.any(String),
date: expect.any(String),
saved: true,
distributed: false,
seen: false,
}),
]),
},
@ -308,4 +323,74 @@ describe('Message', () => {
})
})
})
describe('marks massges as seen', () => {
describe('unauthenticated', () => {
beforeAll(() => {
authenticatedUser = null
})
it('throws authorization error', async () => {
await expect(
mutate({
mutation: markMessagesAsSeen(),
variables: {
messageIds: ['some-id'],
},
}),
).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
})
})
})
describe('authenticated', () => {
const messageIds: string[] = []
beforeAll(async () => {
authenticatedUser = await otherChattingUser.toJson()
const msgs = await query({
query: messageQuery(),
variables: {
roomId,
},
})
msgs.data.Message.forEach((m) => messageIds.push(m.id))
})
it('returns true', async () => {
await expect(
mutate({
mutation: markMessagesAsSeen(),
variables: {
messageIds,
},
}),
).resolves.toMatchObject({
errors: undefined,
data: {
MarkMessagesAsSeen: true,
},
})
})
it('has seen prop set to true', async () => {
await expect(
query({
query: messageQuery(),
variables: {
roomId,
},
}),
).resolves.toMatchObject({
data: {
Message: [
expect.objectContaining({ seen: true }),
expect.objectContaining({ seen: false }),
expect.objectContaining({ seen: true }),
],
},
})
})
})
})
})

View File

@ -14,9 +14,37 @@ export default {
},
}
const resolved = await neo4jgraphql(object, params, context, resolveInfo)
if (resolved) {
const undistributedMessagesIds = resolved
.filter((msg) => !msg.distributed && msg.senderId !== context.user.id)
.map((msg) => msg.id)
if (undistributedMessagesIds.length > 0) {
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
const setDistributedCypher = `
MATCH (m:Message) WHERE m.id IN $undistributedMessagesIds
SET m.distributed = true
RETURN m { .* }
`
const setDistributedTxResponse = await transaction.run(setDistributedCypher, {
undistributedMessagesIds,
})
const messages = await setDistributedTxResponse.records.map((record) => record.get('m'))
return messages
})
try {
await writeTxResultPromise
} finally {
session.close()
}
// send subscription to author to updated the messages
}
resolved.forEach((message) => {
message._id = message.id
if (message.senderId !== context.user.id) {
message.distributed = true
}
})
}
return resolved
@ -35,7 +63,10 @@ export default {
CREATE (currentUser)-[:CREATED]->(message:Message {
createdAt: toString(datetime()),
id: apoc.create.uuid(),
content: $content
content: $content,
saved: true,
distributed: false,
seen: false
})-[:INSIDE]->(room)
RETURN message { .* }
`
@ -58,6 +89,32 @@ export default {
session.close()
}
},
MarkMessagesAsSeen: async (_parent, params, context, _resolveInfo) => {
const { messageIds } = params
const currentUserId = context.user.id
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
const setSeenCypher = `
MATCH (m:Message)<-[:CREATED]-(user:User)
WHERE m.id IN $messageIds AND NOT user.id = $currentUserId
SET m.seen = true
RETURN m { .* }
`
const setSeenTxResponse = await transaction.run(setSeenCypher, {
messageIds,
currentUserId,
})
const messages = await setSeenTxResponse.records.map((record) => record.get('m'))
return messages
})
try {
await writeTxResultPromise
// send subscription to author to updated the messages
return true
} finally {
session.close()
}
},
},
Message: {
...Resolver('Message', {

View File

@ -25,4 +25,3 @@ type LocationMapBox {
type Query {
queryLocations(place: String!, lang: String!): [LocationMapBox]!
}

View File

@ -16,6 +16,10 @@ type Message {
username: String! @cypher(statement: "MATCH (this)<-[:CREATED]-(user:User) RETURN user.name")
avatar: String @cypher(statement: "MATCH (this)<-[:CREATED]-(:User)-[:AVATAR_IMAGE]->(image:Image) RETURN image.url")
date: String! @cypher(statement: "RETURN this.createdAt")
saved: Boolean
distributed: Boolean
seen: Boolean
}
type Mutation {
@ -23,6 +27,8 @@ type Mutation {
roomId: ID!
content: String!
): Message
MarkMessagesAsSeen(messageIds: [String!]): Boolean
}
type Query {

View File

@ -0,0 +1,12 @@
const report = require("multiple-cucumber-html-reporter");
const reportTitle = "Ocelot webapp end-to-end test report"
report.generate({
jsonDir: "reports/json_logs",
reportPath: "./reports/cucumber_html_report",
pageTitle: reportTitle,
reportName: reportTitle,
pageFooter: "<div></div>",
hideMetadata: true
});

View File

@ -22,12 +22,6 @@ async function setupNodeEvents(on, config) {
},
});
on("after:run", (results) => {
if (results) {
console.log(results.status);
}
});
return config;
}

View File

@ -13,7 +13,6 @@
// Cypress.Commands.add('login', (email, password) => { ... })
/* globals Cypress cy */
import "cypress-file-upload";
import { GraphQLClient, request } from 'graphql-request'
import CONFIG from '../../backend/build/src/config'

View File

@ -1,28 +1,27 @@
import { Then } from "@badeball/cypress-cucumber-preprocessor";
Then("I should be able to {string} a teaser image", condition => {
// cy.reload()
let postTeaserImage = ""
switch(condition){
case 'change':
cy.get('.delete-image-button')
case "change":
postTeaserImage = "humanconnection.png"
cy.get(".delete-image-button")
.click()
cy.fixture('humanconnection.png').as('postTeaserImage').then(function() {
cy.get("#postdropzone").upload(
{ fileContent: this.postTeaserImage, fileName: 'humanconnection.png', mimeType: "image/png" },
{ subjectType: "drag-n-drop", force: true }
cy.get("#postdropzone").selectFile(
{ contents: `cypress/fixtures/${postTeaserImage}`, fileName: postTeaserImage, mimeType: "image/png" },
{ action: "drag-drop", force: true }
).wait(750);
})
break;
case 'add':
cy.fixture('onourjourney.png').as('postTeaserImage').then(function() {
cy.get("#postdropzone").upload(
{ fileContent: this.postTeaserImage, fileName: 'onourjourney.png', mimeType: "image/png" },
{ subjectType: "drag-n-drop", force: true }
case "add":
postTeaserImage = "onourjourney.png"
cy.get("#postdropzone").selectFile(
{ contents: `cypress/fixtures/${postTeaserImage}`, fileName: postTeaserImage, mimeType: "image/png" },
{ action: "drag-drop", force: true }
).wait(750);
})
break;
case 'remove':
cy.get('.delete-image-button')
case "remove":
cy.get(".delete-image-button")
.click()
break;
}

View File

@ -3,12 +3,10 @@ import { Then } from "@badeball/cypress-cucumber-preprocessor";
Then("I should be able to change my profile picture", () => {
const avatarUpload = "onourjourney.png";
cy.fixture(avatarUpload, "base64").then(fileContent => {
cy.get("#customdropzone").upload(
{ fileContent, fileName: avatarUpload, mimeType: "image/png" },
{ subjectType: "drag-n-drop", force: true }
cy.get("#customdropzone").selectFile(
{ contents: `cypress/fixtures/${avatarUpload}`, fileName: avatarUpload, mimeType: "image/png" },
{ action: "drag-drop" }
);
});
cy.get(".profile-page-avatar img")
.should("have.attr", "src")
.and("contains", "onourjourney");

@ -1 +1 @@
Subproject commit 350237c62dcff1a5c34f1e8d718f89b05ce3d33f
Subproject commit fdc2e52fa444b300e1c4736600bc0e9ae3314222

View File

@ -10,13 +10,25 @@
"url": "https://github.com/Ocelot-Social-Community/Ocelot-Social.git"
},
"cypress-cucumber-preprocessor": {
"nonGlobalStepDefinitions": true
"stepDefinitions": "cypress/support/step_definitions/**/*.js",
"json": {
"enabled": true,
"output": "cypress/reports/json_logs/cucumber_log.json",
"formatter": "cucumber-json-formatter"
},
"messages": {
"enabled": true,
"output": "cypress/reports/json_logs/messages.ndjson"
},
"html": {
"enabled": false
}
},
"scripts": {
"db:seed": "cd backend && yarn run db:seed",
"db:reset": "cd backend && yarn run db:reset",
"cypress:run": "cypress run --browser electron --config-file ./cypress/cypress.config.js",
"cypress:open": "cypress open --browser electron --config-file ./cypress/cypress.config.js",
"cypress:run": "cypress run --e2e --browser electron --config-file ./cypress/cypress.config.js",
"cypress:open": "cypress open --e2e --browser electron --config-file ./cypress/cypress.config.js",
"cucumber:setup": "cd backend && yarn run dev",
"cucumber": "wait-on tcp:4000 && cucumber-js --require-module @babel/register --exit",
"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\")"
@ -33,7 +45,6 @@
"cross-env": "^7.0.3",
"cucumber": "^6.0.5",
"cypress": "^12.17.0",
"cypress-file-upload": "^3.5.3",
"cypress-network-idle": "^1.14.2",
"date-fns": "^2.25.0",
"dotenv": "^8.2.0",
@ -42,6 +53,7 @@
"import": "^0.0.6",
"jsonwebtoken": "^8.5.1",
"mock-socket": "^9.0.3",
"multiple-cucumber-html-reporter": "^3.4.0",
"neo4j-driver": "^4.3.4",
"neode": "^0.4.8",
"rosie": "^2.1.0",

View File

@ -0,0 +1,30 @@
:export {
colorPrimary: $color-primary;
colorPrimaryActive: $color-primary-active;
colorPrimaryLight: $color-primary-light;
borderColorSoft: $border-color-soft;
borderRadiusBase: $border-radius-base;
textColorBase: $text-color-base;
textColorSoft: $text-color-soft;
textColorInverse: $text-color-inverse;
boxShadowBase: $box-shadow-base;
backgroundColorBase: $background-color-base;
backgroundColorSoft: $background-color-soft;
backgroundColorSoftest: $background-color-softest;
backgroundColorPrimary: $background-color-primary;
colorNeutral30: $color-neutral-30;
chatMessageColor: $chat-message-color;
chatMessageBgMe: $chat-message-bg-me;
chatMessageBgOthers: $chat-message-bg-others;
chatNewMessageColor: $chat-new-message-color;
}

View File

@ -407,3 +407,13 @@ $color-ribbon-event: $background-color-third;
$color-ribbon-event-active: $background-color-third-active;
$color-ribbon-article: $background-color-secondary;
$color-ribbon-article-active: $background-color-secondary-active;
/**
* @tokens Chat Color
*/
$chat-message-bg-me: $color-primary-light;
$chat-message-color: $text-color-base;
$chat-message-bg-others: $color-neutral-80;
$chat-sidemenu-bg: $color-secondary-active;
$chat-new-message-color: $color-secondary-active;

View File

@ -15,13 +15,42 @@
:rooms-loaded="true"
show-files="false"
show-audio="false"
:styles="JSON.stringify(computedChatStyle)"
:show-footer="true"
@send-message="sendMessage($event.detail[0])"
@fetch-messages="fetchMessages($event.detail[0])"
:responsive-breakpoint="responsiveBreakpoint"
:single-room="singleRoom"
@show-demo-options="showDemoOptions = $event"
>
<div slot="menu-icon" @click.prevent.stop="$emit('close-single-room', true)">
<div v-if="singleRoom">
<ds-icon name="close"></ds-icon>
</div>
</div>
<div slot="room-header-avatar">
<div
v-if="selectedRoom && selectedRoom.avatar && selectedRoom.avatar !== 'default-avatar'"
class="vac-avatar"
:style="{ 'background-image': `url('${selectedRoom.avatar}')` }"
/>
<div v-else-if="selectedRoom" class="vac-avatar">
<span class="initials">{{ getInitialsName(selectedRoom.roomName) }}</span>
</div>
</div>
<div v-for="room in rooms" :slot="'room-list-avatar_' + room.id" :key="room.id">
<div
v-if="room.avatar && room.avatar !== 'default-avatar'"
class="vac-avatar"
:style="{ 'background-image': `url('${room.avatar}')` }"
/>
<div v-else class="vac-avatar">
<span class="initials">{{ getInitialsName(room.roomName) }}</span>
</div>
</div>
</vue-advanced-chat>
</client-only>
</div>
</template>
@ -29,6 +58,7 @@
<script>
import { roomQuery, createRoom } from '~/graphql/Rooms'
import { messageQuery, createMessageMutation } from '~/graphql/Messages'
import chatStyle from '~/constants/chat.js'
import { mapGetters } from 'vuex'
export default {
@ -45,6 +75,11 @@ export default {
data() {
return {
menuActions: [
// NOTE: if menuActions is empty, the related slot is not shown
{
name: 'dummyItem',
title: 'Just a dummy item',
},
/* {
name: 'inviteUser',
title: 'Invite User',
@ -79,18 +114,18 @@ export default {
},
],
textMessages: {
ROOMS_EMPTY: 'Aucune conversation',
ROOM_EMPTY: 'Aucune conversation sélectionnée',
NEW_MESSAGES: 'Nouveaux messages',
MESSAGE_DELETED: 'Ce message a été supprimé',
MESSAGES_EMPTY: 'Aucun message',
CONVERSATION_STARTED: 'La conversation a commencée le :',
TYPE_MESSAGE: 'Tapez votre message',
SEARCH: 'Rechercher',
IS_ONLINE: 'est en ligne',
LAST_SEEN: 'dernière connexion ',
IS_TYPING: 'est en train de taper...',
CANCEL_SELECT_MESSAGE: 'Annuler Sélection',
ROOMS_EMPTY: this.$t('chat.roomsEmpty'),
ROOM_EMPTY: this.$t('chat.roomEmpty'),
NEW_MESSAGES: this.$t('chat.newMessages'),
MESSAGE_DELETED: this.$t('chat.messageDeleted'),
MESSAGES_EMPTY: this.$t('chat.messagesEmpty'),
CONVERSATION_STARTED: this.$t('chat.conversationStarted'),
TYPE_MESSAGE: this.$t('chat.typeMessage'),
SEARCH: this.$t('chat.search'),
IS_ONLINE: this.$t('chat.isOnline'),
LAST_SEEN: this.$t('chat.lastSeen'),
IS_TYPING: this.$t('chat.isTyping'),
CANCEL_SELECT_MESSAGE: this.$t('chat.cancelSelectMessage'),
},
roomActions: [
/*
@ -109,6 +144,7 @@ export default {
showDemoOptions: true,
responsiveBreakpoint: 600,
singleRoom: !!this.singleRoomId || false,
selectedRoom: null,
}
},
mounted() {
@ -135,6 +171,11 @@ export default {
...mapGetters({
currentUser: 'auth/user',
}),
computedChatStyle() {
// TODO light/dark theme still needed?
// return this.theme === 'light' ? chatStyle.STYLE.light : chatStyle.STYLE.dark
return chatStyle.STYLE.light
},
},
methods: {
fetchMessages({ room, options = {} }) {
@ -156,6 +197,8 @@ export default {
this.$toast.error(error.message)
}
this.messagesLoaded = true
this.selectedRoom = room
})
},
@ -177,6 +220,11 @@ export default {
}
this.refetchMessage(message.roomId)
},
getInitialsName(fullname) {
if (!fullname) return
return fullname.match(/\b\w/g).join('').substring(0, 3).toUpperCase()
},
},
apollo: {
Rooms: {
@ -217,4 +265,25 @@ export default {
body {
font-family: 'Quicksand', sans-serif;
}
.vac-avatar {
background-size: cover;
background-position: center center;
background-repeat: no-repeat;
background-color: $color-primary-dark;
color: $text-color-primary-inverse;
height: 42px;
width: 42px;
min-height: 42px;
min-width: 42px;
margin-right: 15px;
border-radius: 50%;
position: relative;
> .initials {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
</style>

299
webapp/constants/chat.js Normal file
View File

@ -0,0 +1,299 @@
import tokens from './../assets/_new/styles/tokens.scss'
// import branding from './../assets/styles/imports/_branding.scss'
const styleData = tokens
const STYLE = {
light: {
general: {
color: styleData.textColorBase,
colorButtonClear: '#1976d2',
colorButton: '#fff',
backgroundColorButton: '#1976d2',
backgroundInput: '#fff',
colorPlaceholder: styleData.textColorSoft,
colorCaret: '#1976d2',
colorSpinner: styleData.colorPrimary,
borderStyle: '1px solid #e1e4e8',
backgroundScrollIcon: '#fff',
},
container: {
border: 'none',
borderRadius: styleData.borderRadiusBase,
boxShadow: styleData.boxShadowBase,
},
header: {
background: styleData.backgroundColorSoft,
colorRoomName: styleData.textColorBase,
colorRoomInfo: styleData.textColorSoft,
},
footer: {
background: styleData.backgroundColorSoft,
borderStyleInput: '1px solid #e1e4e8',
borderInputSelected: '#1976d2',
backgroundReply: styleData.backgroundColorSoft,
backgroundTagActive: styleData.backgroundColorSoft,
backgroundTag: styleData.backgroundColorBase,
},
content: {
background: styleData.backgroundColorBase,
},
sidemenu: {
background: '#fff',
backgroundHover: '#f6f6f6',
backgroundActive: styleData.colorPrimaryLight,
colorActive: '#1976d2',
borderColorSearch: '#e1e5e8',
},
dropdown: {
background: '#fff',
backgroundHover: '#f6f6f6',
},
message: {
background: styleData.chatMessageBgOthers,
backgroundMe: styleData.chatMessageBgMe,
color: styleData.chatMessageColor,
colorStarted: '#9ca6af',
backgroundDeleted: '#dadfe2',
backgroundSelected: '#c2dcf2',
colorDeleted: '#757e85',
colorUsername: '#9ca6af',
colorTimestamp: '#828c94',
backgroundDate: '#e5effa',
colorDate: '#505a62',
backgroundSystem: '#e5effa',
colorSystem: '#505a62',
backgroundMedia: 'rgba(0, 0, 0, 0.15)',
backgroundReply: 'rgba(0, 0, 0, 0.08)',
colorReplyUsername: '#0a0a0a',
colorReply: '#6e6e6e',
colorTag: '#0d579c',
backgroundImage: '#ddd',
colorNewMessages: styleData.chatNewMessageColor,
backgroundScrollCounter: '#0696c7',
colorScrollCounter: '#fff',
backgroundReaction: '#eee',
borderStyleReaction: '1px solid #eee',
backgroundReactionHover: '#fff',
borderStyleReactionHover: '1px solid #ddd',
colorReactionCounter: '#0a0a0a',
backgroundReactionMe: '#cfecf5',
borderStyleReactionMe: '1px solid #3b98b8',
backgroundReactionHoverMe: '#cfecf5',
borderStyleReactionHoverMe: '1px solid #3b98b8',
colorReactionCounterMe: '#0b59b3',
backgroundAudioRecord: '#eb4034',
backgroundAudioLine: 'rgba(0, 0, 0, 0.15)',
backgroundAudioProgress: '#455247',
backgroundAudioProgressSelector: '#455247',
colorFileExtension: '#757e85',
},
markdown: {
background: 'rgba(239, 239, 239, 0.7)',
border: 'rgba(212, 212, 212, 0.9)',
color: '#e01e5a',
colorMulti: '#0a0a0a',
},
room: {
colorUsername: '#0a0a0a',
colorMessage: '#67717a',
colorTimestamp: '#a2aeb8',
colorStateOnline: '#4caf50',
colorStateOffline: '#9ca6af',
backgroundCounterBadge: '#0696c7',
colorCounterBadge: '#fff',
},
emoji: {
background: '#fff',
},
icons: {
search: '#9ca6af',
add: styleData.colorPrimary,
toggle: styleData.colorNeutral30,
menu: styleData.colorNeutral30,
close: '#9ca6af',
closeImage: '#fff',
file: styleData.colorPrimary,
paperclip: styleData.colorPrimary,
closeOutline: '#000',
closePreview: '#fff',
send: styleData.colorPrimary,
sendDisabled: '#9ca6af',
emoji: styleData.colorPrimary,
emojiReaction: 'rgba(0, 0, 0, 0.3)',
document: styleData.colorPrimary,
pencil: '#9e9e9e',
checkmark: '#9e9e9e',
checkmarkSeen: '#0696c7',
eye: '#fff',
dropdownMessage: '#fff',
dropdownMessageBackground: 'rgba(0, 0, 0, 0.25)',
dropdownRoom: '#9e9e9e',
dropdownScroll: '#0a0a0a',
microphone: styleData.colorPrimary,
audioPlay: '#455247',
audioPause: '#455247',
audioCancel: '#eb4034',
audioConfirm: '#1ba65b',
},
},
dark: {
general: {
color: '#fff',
colorButtonClear: '#fff',
colorButton: '#fff',
backgroundColorButton: '#1976d2',
backgroundInput: '#202223',
colorPlaceholder: '#596269',
colorCaret: '#fff',
colorSpinner: '#fff',
borderStyle: 'none',
backgroundScrollIcon: '#fff',
},
container: {
border: 'none',
borderRadius: '4px',
boxShadow: '0px 1px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12)',
},
header: {
background: '#181a1b',
colorRoomName: '#fff',
colorRoomInfo: '#9ca6af',
},
footer: {
background: '#131415',
borderStyleInput: 'none',
borderInputSelected: '#1976d2',
backgroundReply: '#1b1c1c',
backgroundTagActive: '#1b1c1c',
backgroundTag: '#131415',
},
content: {
background: '#131415',
},
sidemenu: {
background: '#181a1b',
backgroundHover: '#202224',
backgroundActive: '#151617',
colorActive: '#fff',
borderColorSearch: '#181a1b',
},
dropdown: {
background: '#2a2c33',
backgroundHover: '#26282e',
},
message: {
background: '#22242a',
backgroundMe: '#1f7e80',
color: '#fff',
colorStarted: '#9ca6af',
backgroundDeleted: '#1b1c21',
backgroundSelected: '#c2dcf2',
colorDeleted: '#a2a5a8',
colorUsername: '#b3bac9',
colorTimestamp: '#ebedf2',
backgroundDate: 'rgba(0, 0, 0, 0.3)',
colorDate: '#bec5cc',
backgroundSystem: 'rgba(0, 0, 0, 0.3)',
colorSystem: '#bec5cc',
backgroundMedia: 'rgba(0, 0, 0, 0.18)',
backgroundReply: 'rgba(0, 0, 0, 0.18)',
colorReplyUsername: '#fff',
colorReply: '#d6d6d6',
colorTag: '#f0c60a',
backgroundImage: '#ddd',
colorNewMessages: '#fff',
backgroundScrollCounter: '#1976d2',
colorScrollCounter: '#fff',
backgroundReaction: 'none',
borderStyleReaction: 'none',
backgroundReactionHover: '#202223',
borderStyleReactionHover: 'none',
colorReactionCounter: '#fff',
backgroundReactionMe: '#4e9ad1',
borderStyleReactionMe: 'none',
backgroundReactionHoverMe: '#4e9ad1',
borderStyleReactionHoverMe: 'none',
colorReactionCounterMe: '#fff',
backgroundAudioRecord: '#eb4034',
backgroundAudioLine: 'rgba(255, 255, 255, 0.15)',
backgroundAudioProgress: '#b7d4d3',
backgroundAudioProgressSelector: '#b7d4d3',
colorFileExtension: '#a2a5a8',
},
markdown: {
background: 'rgba(239, 239, 239, 0.7)',
border: 'rgba(212, 212, 212, 0.9)',
color: '#e01e5a',
colorMulti: '#0a0a0a',
},
room: {
colorUsername: '#fff',
colorMessage: '#6c7278',
colorTimestamp: '#6c7278',
colorStateOnline: '#4caf50',
colorStateOffline: '#596269',
backgroundCounterBadge: '#1976d2',
colorCounterBadge: '#fff',
},
emoji: {
background: '#343740',
},
icons: {
search: '#596269',
add: '#fff',
toggle: '#fff',
menu: '#fff',
close: '#9ca6af',
closeImage: '#fff',
file: '#1976d2',
paperclip: '#fff',
closeOutline: '#fff',
closePreview: '#fff',
send: '#fff',
sendDisabled: '#646a70',
emoji: '#fff',
emojiReaction: '#fff',
document: '#1976d2',
pencil: '#ebedf2',
checkmark: '#ebedf2',
checkmarkSeen: '#f0d90a',
eye: '#fff',
dropdownMessage: '#fff',
dropdownMessageBackground: 'rgba(0, 0, 0, 0.25)',
dropdownRoom: '#fff',
dropdownScroll: '#0a0a0a',
microphone: '#fff',
audioPlay: '#b7d4d3',
audioPause: '#b7d4d3',
audioCancel: '#eb4034',
audioConfirm: '#1ba65b',
},
},
}
export default {
STYLE,
}

View File

@ -77,6 +77,10 @@ export const filterPosts = (i18n) => {
eventEnd
eventVenue
eventLocationName
eventLocation {
lng
lat
}
eventIsOnline
...post
...postCounts

View File

@ -14,15 +14,11 @@
<modal />
</client-only>
<div v-if="$store.getters['chat/showChat'].showChat" class="chat-modul">
<ds-text align="right" class="close">
RoomID: {{ $store.getters['chat/showChat'].roomID }}
<ds-button @click="$store.commit('chat/SET_OPEN_CHAT', { showChat: false, roomID: null })">
x
</ds-button>
</ds-text>
<chat-module :singleRoomId="$store.getters['chat/showChat'].roomID" />
<chat-module
v-on:close-single-room="closeSingleRoom"
:singleRoomId="$store.getters['chat/showChat'].roomID"
/>
</div>
>
</div>
</template>
<script>
@ -41,6 +37,11 @@ export default {
ChatModule,
},
mixins: [seo, mobile()],
methods: {
closeSingleRoom() {
this.$store.commit('chat/SET_OPEN_CHAT', { showChat: false, roomID: null })
},
},
beforeCreate() {
this.$store.commit('chat/SET_OPEN_CHAT', { showChat: false, roomID: null })
},
@ -58,7 +59,6 @@ export default {
.chat-modul {
background-color: rgb(233, 228, 228);
height: 667px;
width: 355px;
position: fixed;
bottom: 45px;

View File

@ -78,6 +78,18 @@
}
},
"chat": {
"cancelSelectMessage": "Abbrechen",
"conversationStarted": "Unterhaltung startete am:",
"isOnline": "online",
"isTyping": "tippt...",
"lastSeen": "zuletzt gesehen ",
"messageDeleted": "Diese Nachricht wuerde gelöscht",
"messagesEmpty": "Keine Nachrichten",
"newMessages": "Neue Nachrichten",
"roomEmpty": "Keinen Raum selektiert",
"roomsEmpty": "Keine Räume",
"search": "Suche",
"typeMessage": "Nachricht schreiben",
"userProfileButton": {
"label": "Chat",
"tooltip": "Chatte mit „{name}“"
@ -604,9 +616,16 @@
"button": {
"tooltip": "Landkarte anzeigen"
},
"markerTypes": {
"legend": {
"event": "Veranstaltung",
"group": "Gruppe",
"theUser": "deine Position",
"theUser": "Meine Position",
"user": "Nutzer"
},
"markerTypes": {
"event": "Veranstaltung",
"group": "Gruppe",
"theUser": "meine Position",
"user": "Nutzer"
},
"pageTitle": "Landkarte",
@ -715,11 +734,6 @@
"title": "Benachrichtigungen",
"user": "Nutzer"
},
"position": {
"group": "Gruppe",
"my": "Meine Position",
"user": "Nutzer"
},
"post": {
"comment": {
"reply": "Antworten",

View File

@ -78,6 +78,18 @@
}
},
"chat": {
"cancelSelectMessage": "Cancel",
"conversationStarted": "Conversation started on:",
"isOnline": "is online",
"isTyping": "is writing...",
"lastSeen": "last seen ",
"messageDeleted": "This message was deleted",
"messagesEmpty": "No messages",
"newMessages": "New Messages",
"roomEmpty": "No room selected",
"roomsEmpty": "No rooms",
"search": "Search",
"typeMessage": "Type message",
"userProfileButton": {
"label": "Chat",
"tooltip": "Chat with “{name}”"
@ -604,9 +616,16 @@
"button": {
"tooltip": "Show map"
},
"legend": {
"event": "Event",
"group": "Group",
"theUser": "My position",
"user": "User"
},
"markerTypes": {
"event": "event",
"group": "group",
"theUser": "your position",
"theUser": "my position",
"user": "user"
},
"pageTitle": "Map",
@ -715,11 +734,6 @@
"title": "Notifications",
"user": "User"
},
"position": {
"group": "Group",
"my": "My position",
"user": "User"
},
"post": {
"comment": {
"reply": "Reply",

View File

@ -105,6 +105,7 @@ export default {
styleguideStyles,
'~assets/_new/styles/tokens.scss',
'~assets/styles/imports/_branding.scss',
'~assets/_new/styles/export.scss',
],
},

View File

@ -5,16 +5,15 @@
<ds-heading tag="h1">{{ $t('map.pageTitle') }}</ds-heading>
<small>
<div>
<span v-for="type in markers.types" :key="type.id">
<img
alt="my position"
src="/img/mapbox/marker-icons/mapbox-marker-icon-orange.svg"
:alt="$t('map.legend.' + type.id)"
:src="'/img/mapbox/marker-icons/' + type.icon.legendName"
width="15"
/>
{{ $t('position.my') }}
<img alt="user" src="/img/mapbox/marker-icons/mapbox-marker-icon-green.svg" width="15" />
{{ $t('position.user') }}
<img alt="group" src="/img/mapbox/marker-icons/mapbox-marker-icon-blue.svg" width="15" />
{{ $t('position.group') }}
{{ $t('map.legend.' + type.id) }}
&nbsp;&nbsp;
</span>
</div>
</small>
</ds-space>
@ -66,6 +65,7 @@ import '@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css'
import { mapGetters } from 'vuex'
import { profileUserQuery, mapUserQuery } from '~/graphql/User'
import { groupQuery } from '~/graphql/groups'
import { filterPosts } from '~/graphql/PostQuery.js'
import mobile from '~/mixins/mobile'
import Empty from '~/components/Empty/Empty'
import MapStylesButtons from '~/components/Map/MapStylesButtons'
@ -95,19 +95,40 @@ export default {
currentUserCoordinates: null,
users: null,
groups: null,
posts: null,
markers: {
icons: [
{
id: 'marker-blue',
name: 'mapbox-marker-icon-20px-blue.png',
},
types: [
{
id: 'theUser',
icon: {
id: 'marker-orange',
name: 'mapbox-marker-icon-20px-orange.png',
legendName: 'mapbox-marker-icon-orange.svg',
mapName: 'mapbox-marker-icon-20px-orange.png',
},
},
{
id: 'user',
icon: {
id: 'marker-green',
name: 'mapbox-marker-icon-20px-green.png',
legendName: 'mapbox-marker-icon-green.svg',
mapName: 'mapbox-marker-icon-20px-green.png',
},
},
{
id: 'group',
icon: {
id: 'marker-red',
legendName: 'mapbox-marker-icon-red.svg',
mapName: 'mapbox-marker-icon-20px-red.png',
},
},
{
id: 'event',
icon: {
id: 'marker-purple',
legendName: 'mapbox-marker-icon-purple.svg',
mapName: 'mapbox-marker-icon-20px-purple.png',
},
},
],
isImagesLoaded: false,
@ -137,7 +158,8 @@ export default {
this.markers.isImagesLoaded &&
this.currentUser &&
this.users &&
this.groups
this.groups &&
this.posts
)
},
styles() {
@ -236,17 +258,27 @@ export default {
// Copy coordinates array.
const coordinates = e.features[0].geometry.coordinates.slice()
const markerTypeLabel =
e.features[0].properties.type === 'group'
? this.$t('map.markerTypes.group')
: e.features[0].properties.type === 'user'
? this.$t('map.markerTypes.user')
: this.$t('map.markerTypes.theUser')
const markerProfileLinkTitle =
(e.features[0].properties.type === 'group' ? '&' : '@') + e.features[0].properties.slug
const markerProfileLink =
(e.features[0].properties.type === 'group' ? '/group' : '/profile') +
`/${e.features[0].properties.id}/${e.features[0].properties.slug}`
const markerTypeLabel = this.$t(`map.markerTypes.${e.features[0].properties.type}`)
const markerProfile = {
theUser: {
linkTitle: '@' + e.features[0].properties.slug,
link: `/profile/${e.features[0].properties.id}/${e.features[0].properties.slug}`,
},
user: {
linkTitle: '@' + e.features[0].properties.slug,
link: `/profile/${e.features[0].properties.id}/${e.features[0].properties.slug}`,
},
group: {
linkTitle: '&' + e.features[0].properties.slug,
link: `/group/${e.features[0].properties.id}/${e.features[0].properties.slug}`,
},
event: {
linkTitle: e.features[0].properties.slug,
link: `/post/${e.features[0].properties.id}/${e.features[0].properties.slug}`,
},
}
const markerProfileLinkTitle = markerProfile[e.features[0].properties.type].linkTitle
const markerProfileLink = markerProfile[e.features[0].properties.type].link
let description = `
<div>
<div>
@ -258,11 +290,11 @@ export default {
</div>
`
description +=
e.features[0].properties.about && e.features[0].properties.about.length > 0
e.features[0].properties.description && e.features[0].properties.description.length > 0
? `
<hr>
<div>
${e.features[0].properties.about}
${e.features[0].properties.description}
</div>`
: ''
@ -305,15 +337,18 @@ export default {
},
loadMarkersIconsAndAddMarkers() {
Promise.all(
this.markers.icons.map(
this.markers.types.map(
(marker) =>
new Promise((resolve, reject) => {
// our images have to be in the 'static/img/*' folder otherwise they are not reachable via URL
this.map.loadImage('img/mapbox/marker-icons/' + marker.name, (error, image) => {
this.map.loadImage(
'img/mapbox/marker-icons/' + marker.icon.mapName,
(error, image) => {
if (error) throw error
this.map.addImage(marker.id, image)
this.map.addImage(marker.icon.id, image)
resolve()
})
},
)
}),
),
).then(() => {
@ -337,7 +372,7 @@ export default {
id: user.id,
slug: user.slug,
name: user.name,
about: user.about ? user.about : undefined,
description: user.about ? user.about : undefined,
},
geometry: {
type: 'Point',
@ -346,27 +381,6 @@ export default {
})
}
})
// add markers for "groups"
this.groups.forEach((group) => {
if (group.location) {
this.markers.geoJSON.push({
type: 'Feature',
properties: {
type: 'group',
iconName: 'marker-blue',
iconRotate: 0.0,
id: group.id,
slug: group.slug,
name: group.name,
about: group.about ? group.about : undefined,
},
geometry: {
type: 'Point',
coordinates: this.getCoordinates(group.location),
},
})
}
})
// add marker for "currentUser"
if (this.currentUserCoordinates) {
this.markers.geoJSON.push({
@ -378,7 +392,7 @@ export default {
id: this.currentUser.id,
slug: this.currentUser.slug,
name: this.currentUser.name,
about: this.currentUser.about ? this.currentUser.about : undefined,
description: this.currentUser.about ? this.currentUser.about : undefined,
},
geometry: {
type: 'Point',
@ -386,6 +400,48 @@ export default {
},
})
}
// add markers for "groups"
this.groups.forEach((group) => {
if (group.location) {
this.markers.geoJSON.push({
type: 'Feature',
properties: {
type: 'group',
iconName: 'marker-red',
iconRotate: 0.0,
id: group.id,
slug: group.slug,
name: group.name,
description: group.about ? group.about : undefined,
},
geometry: {
type: 'Point',
coordinates: this.getCoordinates(group.location),
},
})
}
})
// add markers for "posts", post type "Event" with location coordinates
this.posts.forEach((post) => {
if (post.postType.includes('Event') && post.eventLocation) {
this.markers.geoJSON.push({
type: 'Feature',
properties: {
type: 'event',
iconName: 'marker-purple',
iconRotate: 0.0,
id: post.id,
slug: post.slug,
name: post.title,
description: post.contentExcerpt,
},
geometry: {
type: 'Point',
coordinates: this.getCoordinates(post.eventLocation),
},
})
}
})
this.markers.isGeoJSON = true
}
@ -483,6 +539,24 @@ export default {
},
fetchPolicy: 'cache-and-network',
},
Post: {
query() {
return filterPosts(this.$i18n)
},
variables() {
return {
filter: {
postType_in: ['Event'],
eventStart_gte: new Date(),
// would be good to just query for events with defined "eventLocation". couldn't get it working
},
}
},
update({ Post }) {
this.posts = Post
},
fetchPolicy: 'cache-and-network',
},
},
}
</script>

1381
yarn.lock

File diff suppressed because it is too large Load Diff