Merge branch 'master' of https://github.com/Human-Connection/Human-Connection into 552-update_comment

This commit is contained in:
ALau2088 2019-06-13 10:22:25 -07:00
commit d91221fe7e
18 changed files with 392 additions and 189 deletions

View File

@ -21,6 +21,9 @@ install:
- wait-on http://localhost:7474
script:
- export CYPRESS_RETRIES=1
- export BRANCH=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)
- echo "TRAVIS_BRANCH=$TRAVIS_BRANCH, PR=$PR, BRANCH=$BRANCH"
# Backend
- docker-compose exec backend yarn run lint
- docker-compose exec backend yarn run test:jest --ci --verbose=false --coverage
@ -34,7 +37,9 @@ script:
- docker-compose exec webapp yarn run test --ci --verbose=false --coverage
- docker-compose exec -d backend yarn run test:before:seeder
# Fullstack
- CYPRESS_RETRIES=1 yarn run cypress:run
# Disable recording cypress tests if we just update dependencies. This is to
# avoid running out of quota.
- if [[ $BRANCH == *"dependabot"* ]]; then yarn run cypress:run; else yarn run cypress:run --record; fi
# Coverage
- codecov

View File

@ -24,4 +24,5 @@ RUN yarn run build
FROM base as production
ENV NODE_ENV=production
COPY --from=builder /nitro-backend/dist ./dist
COPY ./public/img/ ./public/img/
RUN yarn install --frozen-lockfile --non-interactive

View File

@ -61,7 +61,7 @@
"graphql-custom-directives": "~0.2.14",
"graphql-iso-date": "~3.6.1",
"graphql-middleware": "~3.0.2",
"graphql-shield": "~5.3.6",
"graphql-shield": "~5.3.8",
"graphql-tag": "~2.10.1",
"graphql-yoga": "~1.17.4",
"helmet": "~3.18.0",
@ -105,7 +105,7 @@
"graphql-request": "~1.8.2",
"jest": "~24.8.0",
"nodemon": "~1.19.1",
"prettier": "~1.17.1",
"prettier": "~1.18.2",
"supertest": "~4.0.2"
}
}

View File

@ -505,9 +505,7 @@ export default class NitroDataSource {
const result2 = await this.client.mutate({
mutation: gql`
mutation {
AddCommentAuthor(from: {id: "${
result.data.CreateComment.id
}"}, to: {id: "${toUserId}"}) {
AddCommentAuthor(from: {id: "${result.data.CreateComment.id}"}, to: {id: "${toUserId}"}) {
id
}
}
@ -519,9 +517,7 @@ export default class NitroDataSource {
result = await this.client.mutate({
mutation: gql`
mutation {
AddCommentPost(from: { id: "${
result.data.CreateComment.id
}", to: { id: "${postId}" }}) {
AddCommentPost(from: { id: "${result.data.CreateComment.id}", to: { id: "${postId}" }}) {
id
}
}

View File

@ -6,8 +6,11 @@ const legacyUrls = [
export const fixUrl = url => {
legacyUrls.forEach(legacyUrl => {
url = url.replace(legacyUrl, '/api')
url = url.replace(legacyUrl, '')
})
if (!url.startsWith('/')) {
url = `/${url}`
}
return url
}

View File

@ -1,12 +1,19 @@
import { fixImageURLs } from './fixImageUrlsMiddleware'
describe('fixImageURLs', () => {
describe('edge case: image url is exact match of legacy url', () => {
it('replaces it with `/`', () => {
const url = 'https://api-alpha.human-connection.org'
expect(fixImageURLs(url)).toEqual('/')
})
})
describe('image url of legacy alpha', () => {
it('removes domain', () => {
const url =
'https://api-alpha.human-connection.org/uploads/4bfaf9172c4ba03d7645108bbbd16f0a696a37d01eacd025fb131e5da61b15d9.png'
expect(fixImageURLs(url)).toEqual(
'/api/uploads/4bfaf9172c4ba03d7645108bbbd16f0a696a37d01eacd025fb131e5da61b15d9.png',
'/uploads/4bfaf9172c4ba03d7645108bbbd16f0a696a37d01eacd025fb131e5da61b15d9.png',
)
})
})
@ -16,7 +23,7 @@ describe('fixImageURLs', () => {
const url =
'https://staging-api.human-connection.org/uploads/1b3c39a24f27e2fb62b69074b2f71363b63b263f0c4574047d279967124c026e.jpeg'
expect(fixImageURLs(url)).toEqual(
'/api/uploads/1b3c39a24f27e2fb62b69074b2f71363b63b263f0c4574047d279967124c026e.jpeg',
'/uploads/1b3c39a24f27e2fb62b69074b2f71363b63b263f0c4574047d279967124c026e.jpeg',
)
})
})

View File

@ -87,9 +87,7 @@ describe('currentUser { notifications }', () => {
describe('who mentions me again', () => {
beforeEach(async () => {
const updatedContent = `${
post.content
} One more mention to <a href="/profile/you" class="mention">@al-capone</a>`
const updatedContent = `${post.content} One more mention to <a href="/profile/you" class="mention">@al-capone</a>`
// The response `post.content` contains a link but the XSSmiddleware
// should have the `mention` CSS class removed. I discovered this
// during development and thought: A feature not a bug! This way we

View File

@ -56,14 +56,14 @@ type User {
contributionsCount: Int! @cypher(
statement: """
MATCH (this)-[:WROTE]->(r:Post)
WHERE (NOT exists(r.deleted) OR r.deleted = false)
AND (NOT exists(r.disabled) OR r.disabled = false)
WHERE NOT r.deleted = true AND NOT r.disabled = true
RETURN COUNT(r)
"""
)
comments: [Comment]! @relation(name: "WROTE", direction: "OUT")
commentsCount: Int! @cypher(statement: "MATCH (this)-[:WROTE]->(r:Comment) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(r)")
commentedCount: Int! @cypher(statement: "MATCH (this)-[:WROTE]->(r:Comment)-[:COMMENTS]->(p:Post) WHERE NOT r.deleted = true AND NOT r.disabled = true AND NOT p.deleted = true AND NOT p.disabled = true RETURN COUNT(DISTINCT(p))")
shouted: [Post]! @relation(name: "SHOUTED", direction: "OUT")
shoutedCount: Int! @cypher(statement: "MATCH (this)-[:SHOUTED]->(r:Post) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)")
@ -77,4 +77,4 @@ type User {
badges: [Badge]! @relation(name: "REWARDED", direction: "IN")
badgesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)")
}
}

View File

@ -1119,10 +1119,10 @@
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.9.tgz#693e76a52f61a2f1e7fb48c0eef167b95ea4ffd0"
integrity sha512-sCZy4SxP9rN2w30Hlmg5dtdRwgYQfYRiLo9usw8X9cxlf+H4FqM1xX7+sNH7NNKVdbXMJWqva7iyy+fxh/V7fA==
"@types/yup@0.26.14":
version "0.26.14"
resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.26.14.tgz#d31f3b9a04039cca70ebb4db4d6c7fc3f694e80b"
integrity sha512-OcBtVLHvYULVSltpuBdhFiVOKoSsOS58D872HydO93oBf3OdGq5zb+LnqGo18TNNSV2aW8hjIdS6H+wp68zFtQ==
"@types/yup@0.26.16":
version "0.26.16"
resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.26.16.tgz#75c428236207c48d9f8062dd1495cda8c5485a15"
integrity sha512-E2RNc7DSeQ+2EIJ1H3+yFjYu6YiyQBUJ7yNpIxomrYJ3oFizLZ5yDS3T1JTUNBC2OCRkgnhLS0smob5UuCHfNA==
"@types/zen-observable@^0.5.3":
version "0.5.4"
@ -3772,12 +3772,12 @@ graphql-request@~1.8.2:
dependencies:
cross-fetch "2.2.2"
graphql-shield@~5.3.6:
version "5.3.6"
resolved "https://registry.yarnpkg.com/graphql-shield/-/graphql-shield-5.3.6.tgz#20061b02f77056c0870a623c530ef28a1bf4fff4"
integrity sha512-ihw/i4X+d1kpj1SVA6iBkVl2DZhPsI+xV08geR2TX3FWhpU7zakk/16yBzDRJTTCUgKsWfgyebrgIBsuhTwMnA==
graphql-shield@~5.3.8:
version "5.3.8"
resolved "https://registry.yarnpkg.com/graphql-shield/-/graphql-shield-5.3.8.tgz#f9e7ad2285f6cfbe20a8a49154ce6c1b184e3893"
integrity sha512-33rQ8U5jMurHIapctHk7hBcUg3nxC7fmMIMtyWiomJXhBmztFq/SG7jNaapnL5M7Q/0BmoaSQd3FLSpelP9KPw==
dependencies:
"@types/yup" "0.26.14"
"@types/yup" "0.26.16"
lightercollective "^0.3.0"
object-hash "^1.3.1"
yup "^0.27.0"
@ -6230,10 +6230,10 @@ prettier-linter-helpers@^1.0.0:
dependencies:
fast-diff "^1.1.2"
prettier@~1.17.1:
version "1.17.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.17.1.tgz#ed64b4e93e370cb8a25b9ef7fef3e4fd1c0995db"
integrity sha512-TzGRNvuUSmPgwivDqkZ9tM/qTGW9hqDKWOE9YHiyQdixlKbv7kvEqsmDPrcHJTKwthU774TQwZXVtaQ/mMsvjg==
prettier@~1.18.2:
version "1.18.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea"
integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==
pretty-format@^24.8.0:
version "24.8.0"

View File

@ -1,5 +1,5 @@
<template>
<ds-space margin-top="large" style="text-align: center">
<ds-space class="load-more" margin-top="large" style="text-align: center">
<ds-button :loading="loading" icon="arrow-down" ghost @click="$emit('click')">
{{ $t('actions.loadMore') }}
</ds-button>

View File

@ -0,0 +1,38 @@
import gql from 'graphql-tag'
export default i18n => {
const lang = i18n.locale().toUpperCase()
return gql(`
query Post($filter: _PostFilter, $first: Int, $offset: Int) {
Post(filter: $filter, first: $first, offset: $offset, orderBy: createdAt_desc) {
id
slug
title
contentExcerpt
shoutedCount
commentsCount
deleted
image
createdAt
disabled
deleted
categories {
id
name
icon
}
author {
id
slug
avatar
name
disabled
deleted
location {
name: name${lang}
}
}
}
}
`)
}

View File

@ -1,10 +1,10 @@
import gql from 'graphql-tag'
export default app => {
const lang = app.$i18n.locale().toUpperCase()
export default i18n => {
const lang = i18n.locale().toUpperCase()
return gql(`
query User($slug: String!, $first: Int, $offset: Int) {
User(slug: $slug) {
query User($id: ID!) {
User(id: $id) {
id
slug
name
@ -24,7 +24,7 @@ export default app => {
}
badgesCount
shoutedCount
commentsCount
commentedCount
followingCount
following(first: 7) {
id
@ -69,35 +69,6 @@ export default app => {
}
}
contributionsCount
contributions(first: $first, offset: $offset, orderBy: createdAt_desc) {
id
slug
title
contentExcerpt
shoutedCount
commentsCount
deleted
image
createdAt
disabled
deleted
categories {
id
name
icon
}
author {
id
slug
avatar
name
disabled
deleted
location {
name: name${lang}
}
}
}
socialMedia {
id
url

View File

@ -172,11 +172,6 @@
}
},
"common": {
"your": {
"post": "Dein Beitrag ::: Deine Beiträge",
"comment": "Dein Kommentar ::: Deine Kommentare",
"shout": "Deine Empfehlung ::: Deine Empfehlungen"
},
"post": "Beitrag ::: Beiträge",
"comment": "Kommentar ::: Kommentare",
"letsTalk": "Miteinander reden",

View File

@ -173,11 +173,6 @@
}
},
"common": {
"your": {
"post": "Your Post ::: Your Posts",
"comment": "Your Comment ::: Your Comments",
"shout": "Your Shout ::: Your Shouts"
},
"post": "Post ::: Posts",
"comment": "Comment ::: Comments",
"letsTalk": "Let`s Talk",

View File

@ -57,7 +57,7 @@
"accounting": "~0.4.1",
"apollo-cache-inmemory": "~1.5.1",
"apollo-client": "~2.6.2",
"cookie-universal-nuxt": "~2.0.14",
"cookie-universal-nuxt": "~2.0.16",
"cross-env": "~5.2.0",
"date-fns": "2.0.0-alpha.31",
"express": "~4.17.1",
@ -70,7 +70,7 @@
"stack-utils": "^1.0.2",
"string-hash": "^1.1.3",
"tiptap": "1.20.1",
"tiptap-extensions": "1.21.0",
"tiptap-extensions": "1.22.2",
"v-tooltip": "~2.0.2",
"vue-count-to": "~1.0.13",
"vue-izitoast": "1.1.2",
@ -104,9 +104,9 @@
"jest": "~24.8.0",
"node-sass": "~4.12.0",
"nodemon": "~1.19.1",
"prettier": "~1.18.0",
"prettier": "~1.18.2",
"sass-loader": "~7.1.0",
"tippy.js": "^4.3.3",
"tippy.js": "^4.3.4",
"vue-jest": "~3.0.4",
"vue-svg-loader": "~0.12.0"
}

View File

@ -1,4 +1,4 @@
import { shallowMount, createLocalVue } from '@vue/test-utils'
import { config, mount, shallowMount, createLocalVue } from '@vue/test-utils'
import ProfileSlug from './_slug.vue'
import Vuex from 'vuex'
import Styleguide from '@human-connection/styleguide'
@ -7,6 +7,11 @@ const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(Styleguide)
localVue.filter('date', d => d)
config.stubs['no-ssr'] = '<span><slot /></span>'
config.stubs['v-popover'] = '<span><slot /></span>'
config.stubs['nuxt-link'] = '<span><slot /></span>'
describe('ProfileSlug', () => {
let wrapper
@ -19,8 +24,14 @@ describe('ProfileSlug', () => {
id: 'p23',
name: 'It is a post',
},
$t: jest.fn(),
// If you mocking router, than don't use VueRouter with lacalVue: https://vue-test-utils.vuejs.org/guides/using-with-vue-router.html
$t: jest.fn(t => t),
// If you mocking router, than don't use VueRouter with localVue: https://vue-test-utils.vuejs.org/guides/using-with-vue-router.html
$route: {
params: {
id: '4711',
slug: 'john-doe',
},
},
$router: {
history: {
push: jest.fn(),
@ -31,6 +42,7 @@ describe('ProfileSlug', () => {
error: jest.fn(),
},
$apollo: {
loading: false,
mutate: jest.fn().mockResolvedValue(),
},
}
@ -60,7 +72,7 @@ describe('ProfileSlug', () => {
beforeEach(jest.runAllTimers)
it('emits "deletePost"', () => {
expect(wrapper.emitted().deletePost.length).toBe(1)
expect(wrapper.emitted().deletePost).toHaveLength(1)
})
it('does not go to index (main) page', () => {
@ -78,4 +90,128 @@ describe('ProfileSlug', () => {
})
})
})
describe('mount', () => {
Wrapper = () => {
return mount(ProfileSlug, {
mocks,
localVue,
})
}
describe('given an authenticated user', () => {
beforeEach(() => {
mocks.$filters = {
removeLinks: c => c,
}
mocks.$store = {
getters: {
'auth/user': {
id: 'u23',
},
},
}
})
describe('given a user for the profile', () => {
beforeEach(() => {
wrapper = Wrapper()
wrapper.setData({
User: [
{
id: 'u3',
name: 'Bob the builder',
contributionsCount: 6,
shoutedCount: 7,
commentedCount: 8,
},
],
})
})
it('displays name of the user', () => {
expect(wrapper.text()).toContain('Bob the builder')
})
describe('load more button', () => {
const aPost = {
title: 'I am a post',
content: 'This is my content',
contentExcerpt: 'This is my content',
}
describe('currently no posts available (e.g. after tab switching)', () => {
beforeEach(() => {
wrapper.setData({
Post: null,
})
})
it('displays no "load more" button', () => {
expect(wrapper.find('.load-more').exists()).toBe(false)
})
describe('apollo client in `loading` state', () => {
beforeEach(() => {
wrapper.vm.$apollo.loading = true
})
it('never displays more than one loading spinner', () => {
expect(wrapper.findAll('.ds-spinner')).toHaveLength(1)
})
it('displays a loading spinner below the posts list', () => {
expect(wrapper.find('.user-profile-posts-list .ds-spinner').exists()).toBe(true)
})
})
})
describe('pagination returned less posts than available', () => {
beforeEach(() => {
const posts = [1, 2, 3, 4, 5].map(id => {
return { ...aPost, id }
})
wrapper.setData({
Post: posts,
})
})
it('displays a "load more" button', () => {
expect(wrapper.find('.load-more').exists()).toBe(true)
})
describe('apollo client in `loading` state', () => {
beforeEach(() => {
wrapper.vm.$apollo.loading = true
})
it('never displays more than one loading spinner', () => {
expect(wrapper.findAll('.ds-spinner')).toHaveLength(1)
})
it('displays a loading spinner below the posts list', () => {
expect(wrapper.find('.load-more .ds-spinner').exists()).toBe(true)
})
})
})
describe('pagination returned as many posts as available', () => {
beforeEach(() => {
const posts = [1, 2, 3, 4, 5, 6].map(id => {
return { ...aPost, id }
})
wrapper.setData({
Post: posts,
})
})
it('displays no "load more" button', () => {
expect(wrapper.find('.load-more').exists()).toBe(false)
})
})
})
})
})
})
})

View File

@ -142,69 +142,50 @@
</ds-card>
</ds-space>
</ds-flex-item>
<ds-flex-item :width="{ base: '100%', sm: 3, md: 5, lg: 3 }">
<ds-flex :width="{ base: '100%' }" gutter="small">
<ds-flex class="user-profile-posts-list" :width="{ base: '100%' }" gutter="small">
<ds-flex-item class="profile-top-navigation">
<ds-card class="ds-tab-nav">
<ds-flex>
<ds-flex-item
v-tooltip="{
content: $t('common.your.post', null, user.contributionsCount),
placement: 'right',
delay: { show: 500 },
}"
class="ds-tab-nav-item pointer ds-tab-nav-item-active"
@click="tabActivity('posts', $event)"
>
<ds-space margin="small">
<!-- TODO: find better solution for rendering errors -->
<no-ssr>
<ds-number :label="$t('common.post', null, user.contributionsCount)">
<hc-count-to slot="count" :end-val="user.contributionsCount" />
</ds-number>
</no-ssr>
</ds-space>
</ds-flex-item>
<ds-flex-item
v-tooltip="{
content: $t('common.your.comment', null, user.commentsCount),
placement: 'right',
delay: { show: 500 },
}"
class="ds-tab-nav-item pointer"
@click="tabActivity('commented', $event)"
>
<ds-space margin="small">
<!-- TODO: find better solution for rendering errors -->
<no-ssr>
<ds-number :label="$t('profile.commented')">
<hc-count-to slot="count" :end-val="user.commentsCount" />
</ds-number>
</no-ssr>
</ds-space>
</ds-flex-item>
<ds-flex-item
v-tooltip="{
content: $t('common.your.shout', null, user.shoutedCount),
placement: 'right',
delay: { show: 500 },
}"
class="ds-tab-nav-item pointer"
>
<ds-space margin="small">
<!-- TODO: find better solution for rendering errors -->
<no-ssr>
<ds-number :label="$t('profile.shouted')">
<hc-count-to slot="count" :end-val="user.shoutedCount" />
</ds-number>
</no-ssr>
</ds-space>
</ds-flex-item>
</ds-flex>
<ul class="Tabs">
<li class="Tabs__tab Tab pointer" :class="{ active: tabActive === 'post' }">
<a @click="handleTab('post')">
<ds-space margin="small">
<no-ssr placeholder="Loading...">
<ds-number :label="$t('common.post', null, user.contributionsCount)">
<hc-count-to slot="count" :end-val="user.contributionsCount" />
</ds-number>
</no-ssr>
</ds-space>
</a>
</li>
<li class="Tabs__tab Tab pointer" :class="{ active: tabActive === 'comment' }">
<a @click="handleTab('comment')">
<ds-space margin="small">
<no-ssr placeholder="Loading...">
<ds-number :label="$t('profile.commented')">
<hc-count-to slot="count" :end-val="user.commentedCount" />
</ds-number>
</no-ssr>
</ds-space>
</a>
</li>
<li class="Tabs__tab Tab pointer" :class="{ active: tabActive === 'shout' }">
<a @click="handleTab('shout')">
<ds-space margin="small">
<no-ssr placeholder="Loading...">
<ds-number :label="$t('profile.shouted')">
<hc-count-to slot="count" :end-val="user.shoutedCount" />
</ds-number>
</no-ssr>
</ds-space>
</a>
</li>
<li class="Tabs__presentation-slider" role="presentation"></li>
</ul>
</ds-card>
</ds-flex-item>
<ds-flex-item style="text-align: center">
<ds-button
v-if="myProfile"
@ -216,15 +197,23 @@
primary
/>
</ds-flex-item>
<template v-if="activePosts.length">
<hc-post-card
v-for="(post, index) in activePosts"
:key="post.id"
:post="post"
:width="{ base: '100%', md: '100%', xl: '50%' }"
@deletePost="user.contributions.splice(index, 1)"
@deletePost="Post.splice(index, 1)"
/>
</template>
<template v-else-if="$apollo.loading">
<ds-flex-item>
<ds-section centered>
<ds-spinner size="base"></ds-spinner>
</ds-section>
</ds-flex-item>
</template>
<template v-else>
<ds-flex-item :width="{ base: '100%' }">
<hc-empty margin="xx-large" icon="file" />
@ -252,6 +241,17 @@ import HcUpload from '~/components/Upload'
import HcAvatar from '~/components/Avatar/Avatar.vue'
import PostMutationHelpers from '~/mixins/PostMutationHelpers'
import PostQuery from '~/graphql/UserProfile/Post.js'
import UserQuery from '~/graphql/UserProfile/User.js'
const tabToFilterMapping = ({ tab, id }) => {
return {
post: { author: { id } },
comment: { comments_some: { author: { id } } },
shout: { shoutedBy_some: { id } },
}[tab]
}
export default {
components: {
User,
@ -271,16 +271,28 @@ export default {
mode: 'out-in',
},
data() {
const filter = tabToFilterMapping({ tab: 'post', id: this.$route.params.id })
return {
User: [],
Post: [],
voted: false,
page: 1,
pageSize: 6,
tabActive: 'post',
filter,
}
},
computed: {
hasMore() {
const total = {
post: this.user.contributionsCount,
shout: this.user.shoutedCount,
comment: this.user.commentedCount,
}[this.tabActive]
return this.Post && this.Post.length < total
},
myProfile() {
return this.$route.params.slug === this.$store.getters['auth/user'].slug
return this.$route.params.id === this.$store.getters['auth/user'].id
},
followedByCount() {
let count = Number(this.user.followedByCount) || 0
@ -292,16 +304,11 @@ export default {
offset() {
return (this.page - 1) * this.pageSize
},
hasMore() {
return (
this.user.contributions && this.user.contributions.length < this.user.contributionsCount
)
},
activePosts() {
if (!this.user.contributions) {
if (!this.Post) {
return []
}
return this.uniq(this.user.contributions.filter(post => !post.deleted))
return this.uniq(this.Post.filter(post => !post.deleted))
},
socialMediaLinks() {
const { socialMedia = [] } = this.user
@ -327,6 +334,11 @@ export default {
},
},
methods: {
handleTab(tab) {
this.tabActive = tab
this.Post = null
this.filter = tabToFilterMapping({ tab, id: this.$route.params.id })
},
uniq(items, field = 'id') {
return uniqBy(items, field)
},
@ -338,19 +350,16 @@ export default {
// this.page++
// Fetch more data and transform the original result
this.page++
this.$apollo.queries.User.fetchMore({
this.$apollo.queries.Post.fetchMore({
variables: {
slug: this.$route.params.slug,
filter: this.filter,
first: this.pageSize,
offset: this.offset,
},
// Transform the previous result with new data
updateQuery: (previousResult, { fetchMoreResult }) => {
let output = { User: this.User }
output.User[0].contributions = [
...previousResult.User[0].contributions,
...fetchMoreResult.User[0].contributions,
]
let output = { Post: this.Post }
output.Post = [...previousResult.Post, ...fetchMoreResult.Post]
return output
},
fetchPolicy: 'cache-and-network',
@ -358,19 +367,28 @@ export default {
},
},
apollo: {
User: {
Post: {
query() {
return require('~/graphql/UserProfileQuery.js').default(this)
return PostQuery(this.$i18n)
},
variables() {
return {
slug: this.$route.params.slug,
filter: this.filter,
first: this.pageSize,
offset: 0,
}
},
fetchPolicy: 'cache-and-network',
},
User: {
query() {
return UserQuery(this.$i18n)
},
variables() {
return { id: this.$route.params.id }
},
fetchPolicy: 'cache-and-network',
},
},
}
</script>
@ -380,8 +398,48 @@ export default {
cursor: pointer;
}
.ds-tab-nav .ds-card-content .ds-tab-nav-item:hover {
border-bottom: 3px solid #c9c6ce;
.Tab {
border-collapse: collapse;
padding-bottom: 5px;
}
.Tab:hover {
border-bottom: 2px solid #c9c6ce;
}
.Tabs {
position: relative;
background-color: #fff;
&:after {
content: ' ';
display: table;
clear: both;
}
margin: 0;
padding: 0;
list-style: none;
&__tab {
float: left;
width: 33.333%;
text-align: center;
&:first-child.active ~ .Tabs__presentation-slider {
left: 0;
}
&:nth-child(2).active ~ .Tabs__presentation-slider {
left: 33.333%;
}
&:nth-child(3).active ~ .Tabs__presentation-slider {
left: calc(33.333% * 2);
}
}
&__presentation-slider {
position: absolute;
bottom: 0;
left: 0;
width: 33.333%;
height: 2px;
background-color: #17b53f;
transition: left 0.25s;
}
}
.profile-avatar.ds-avatar {

View File

@ -3305,18 +3305,18 @@ cookie-signature@1.0.6:
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
cookie-universal-nuxt@~2.0.14:
version "2.0.14"
resolved "https://registry.yarnpkg.com/cookie-universal-nuxt/-/cookie-universal-nuxt-2.0.14.tgz#6fdf8e928eadd7611c04a57614fe2e29b60eb971"
integrity sha512-ih9Z0Z2K6eLaugTttGCVN85nogKseIFF/dqup3klvYC4mQS3+1IloqBqzTL/N7degBBAols2oppwYNDmaRtVig==
cookie-universal-nuxt@~2.0.16:
version "2.0.16"
resolved "https://registry.yarnpkg.com/cookie-universal-nuxt/-/cookie-universal-nuxt-2.0.16.tgz#8d528098c973162b352199240e40da0e5429b13f"
integrity sha512-wRK2zw8w+a5xPehb5kLbgOic/4mbjl2exUCxWZwGuttcwsFgOymiwDrCOzmQslqrDevPDL2SsBbH6wtOm7dB9g==
dependencies:
"@types/cookie" "^0.3.1"
cookie-universal "^2.0.14"
cookie-universal "^2.0.16"
cookie-universal@^2.0.14:
version "2.0.14"
resolved "https://registry.yarnpkg.com/cookie-universal/-/cookie-universal-2.0.14.tgz#1b4f27cffccfc2e47703fa235c1f67f931213041"
integrity sha512-m6J0DQa4/RQvXhzUG37EY1ynK3Uq1BKzp5hotST9olrzjrRx+B0vNPx7azg0/X0XrYQvL7MMbPXwou8m0BNDwg==
cookie-universal@^2.0.16:
version "2.0.16"
resolved "https://registry.yarnpkg.com/cookie-universal/-/cookie-universal-2.0.16.tgz#ec8b55789b502a377ef02ad230923c1dfa5c1061"
integrity sha512-EHtQ5Tg3UoUHG7LmeV3rlV3iYthkhUuYZ0y86EseypxGcUuvzxuHExEb6mHKDhDPrIrdewAHdG/aCHuG/T4zEg==
dependencies:
"@types/cookie" "^0.3.1"
cookie "^0.3.1"
@ -8859,10 +8859,10 @@ prettier@1.16.3:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.3.tgz#8c62168453badef702f34b45b6ee899574a6a65d"
integrity sha512-kn/GU6SMRYPxUakNXhpP0EedT/KmaPzr0H5lIsDogrykbaxOpOfAFfk5XA7DZrJyMAv1wlMV3CPcZruGXVVUZw==
prettier@^1.15.2, prettier@~1.18.0:
version "1.18.0"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.0.tgz#d1701ca9b2941864b52f3262b35946d2c9cd88f0"
integrity sha512-YsdAD29M0+WY2xXZk3i0PA16olY9qZss+AuODxglXcJ+2ZBwFv+6k5tE8GS8/HKAthaajlS/WqhdgcjumOrPlg==
prettier@^1.15.2, prettier@~1.18.2:
version "1.18.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea"
integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==
pretty-bytes@^5.2.0:
version "5.2.0"
@ -10558,10 +10558,10 @@ timsort@^0.3.0:
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
tippy.js@^4.3.3:
version "4.3.3"
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-4.3.3.tgz#396304bea577bbff03f2700a1761329e8c1fce86"
integrity sha512-2fPMlquzVQxpLoOd0eJA1sPZ86/R6zD/9985wV0d2zhhX52DiO3aeg7TTS/mBrUjgFwVZh19YLb4l2c8bJkQPw==
tippy.js@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-4.3.4.tgz#9a91fd5ce8c401f181b7adaa6b2c27f3d105f3ba"
integrity sha512-O2ukxHOJTLVYZ/TfHjNd8WgAWoefX9uk5QiWRdHfX2PR2lBpUU4BJQLl7U2Ykc8K7o16gTeHEElpuRfgD5b0aA==
dependencies:
popper.js "^1.14.7"
@ -10579,10 +10579,10 @@ tiptap-commands@^1.10.5, tiptap-commands@^1.10.7:
prosemirror-utils "^0.9.0"
tiptap-utils "^1.5.5"
tiptap-extensions@1.21.0:
version "1.21.0"
resolved "https://registry.yarnpkg.com/tiptap-extensions/-/tiptap-extensions-1.21.0.tgz#c2f228144b6943755d2de4617e11febe08c78a42"
integrity sha512-dIm9Q/G1qL1+sEqQiPXElUzSBcRhXXPuyOdtHgMrncUaCbnaDxsHdkJl700OXfA/GCu7AlhUpd3R67Rmb+voCQ==
tiptap-extensions@1.22.2:
version "1.22.2"
resolved "https://registry.yarnpkg.com/tiptap-extensions/-/tiptap-extensions-1.22.2.tgz#66b24ef63653481da16419efdf6459c7a9f518f7"
integrity sha512-JJe2yAnbaKBWO/16+lfFphePphG9UYhp3wAUkzhSefJx3dtIdDfCmp5jBzZ6NLahUHNT/Z9WeYdhQmngAeVhoA==
dependencies:
lowlight "^1.12.1"
prosemirror-collab "^1.1.2"
@ -10593,7 +10593,7 @@ tiptap-extensions@1.21.0:
prosemirror-transform "^1.1.3"
prosemirror-utils "^0.9.0"
prosemirror-view "^1.9.8"
tiptap "^1.21.1"
tiptap "^1.22.2"
tiptap-commands "^1.10.7"
tiptap-utils@^1.5.3:
@ -10632,10 +10632,10 @@ tiptap@1.20.1:
tiptap-commands "^1.10.5"
tiptap-utils "^1.5.3"
tiptap@^1.21.1:
version "1.21.1"
resolved "https://registry.yarnpkg.com/tiptap/-/tiptap-1.21.1.tgz#c0340375795088b899541b64ce86ae45e98d0369"
integrity sha512-vCKT/UGorAx1SSX5+9vmtZa+WC+LKfJIArgkEJFXmxfZeyBhNXRSwR+rR+UtdPYi8V4CtmIwtv6eRPP+bH6SWA==
tiptap@^1.22.2:
version "1.22.2"
resolved "https://registry.yarnpkg.com/tiptap/-/tiptap-1.22.2.tgz#f3f2b822d9ed087a853520c86593b2e6bd822bdd"
integrity sha512-qMFQJ358Ga8gXzUAMaHGrZDab/IqZf28N8BYYo4hyFIMoEYQVWmBGWuSBHOgpxiueXpC9zMuCoIgrNWN3VpoNQ==
dependencies:
prosemirror-commands "^1.0.8"
prosemirror-dropcursor "^1.1.1"