mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-12 23:35:58 +00:00
Refactor: move following/followedBy updating to page component
This commit is contained in:
parent
f69e7e42de
commit
e6361c9b74
@ -10,29 +10,50 @@ config.stubs['client-only'] = '<span><slot /></span>'
|
||||
config.stubs['ds-space'] = '<span><slot /></span>'
|
||||
config.stubs['nuxt-link'] = '<span><slot /></span>'
|
||||
|
||||
const user = {
|
||||
...helpers.fakeUser()[0],
|
||||
followedByCount: 12,
|
||||
followingCount: 15,
|
||||
followedBy: helpers.fakeUser(7),
|
||||
following: helpers.fakeUser(7),
|
||||
}
|
||||
|
||||
const allConnectionsUser = {
|
||||
...user,
|
||||
followedBy: [
|
||||
...user.followedBy,
|
||||
...helpers.fakeUser(user.followedByCount - user.followedBy.length),
|
||||
],
|
||||
following: [...user.following, ...helpers.fakeUser(user.followingCount - user.following.length)],
|
||||
}
|
||||
|
||||
const noConnectionsUser = {
|
||||
...user,
|
||||
followedByCount: 0,
|
||||
followingCount: 0,
|
||||
followedBy: [],
|
||||
following: [],
|
||||
}
|
||||
|
||||
describe('FollowList.vue', () => {
|
||||
let store, mocks, getters, propsData
|
||||
let store, getters
|
||||
const Wrapper = (customProps) =>
|
||||
mount(FollowList, {
|
||||
store,
|
||||
propsData: { user, ...customProps },
|
||||
mocks: {
|
||||
$t: jest.fn((str) => str),
|
||||
},
|
||||
localVue,
|
||||
})
|
||||
|
||||
beforeAll(() => {
|
||||
mocks = {
|
||||
$t: jest.fn(),
|
||||
}
|
||||
getters = {
|
||||
'auth/user': () => {
|
||||
return {}
|
||||
},
|
||||
'auth/isModerator': () => false,
|
||||
}
|
||||
const [_user] = helpers.fakeUser()
|
||||
propsData = {
|
||||
user: {
|
||||
..._user,
|
||||
followedByCount: 12,
|
||||
followingCount: 15,
|
||||
followedBy: helpers.fakeUser(7),
|
||||
following: helpers.fakeUser(7),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
describe('mount', () => {
|
||||
@ -42,88 +63,71 @@ describe('FollowList.vue', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('given a user with connections', () => {
|
||||
;['following', 'followedBy'].forEach((type) =>
|
||||
describe(`and type=${type}`, () => {
|
||||
let wrapper
|
||||
let queryMock
|
||||
describe('given a user', () => {
|
||||
describe('without connections', () => {
|
||||
it('displays the followingNobody message', () => {
|
||||
const wrapper = Wrapper({ user: noConnectionsUser })
|
||||
expect(wrapper.find('.no-connections').text()).toBe(
|
||||
`${noConnectionsUser.name} ${wrapper.vm.$t(`profile.network.followingNobody`)}`,
|
||||
)
|
||||
})
|
||||
|
||||
beforeAll(() => {
|
||||
queryMock = jest.fn().mockResolvedValue({
|
||||
data: { User: [{ [type]: additionalConnections[type] }] },
|
||||
})
|
||||
it('displays the followedByNobody message', () => {
|
||||
const wrapper = Wrapper({ user: noConnectionsUser, type: 'followedBy' })
|
||||
expect(wrapper.find('.no-connections').text()).toBe(
|
||||
`${noConnectionsUser.name} ${wrapper.vm.$t(`profile.network.followedByNobody`)}`,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
wrapper = mount(FollowList, {
|
||||
store,
|
||||
propsData: { ...propsData, type: type },
|
||||
mocks: {
|
||||
...mocks,
|
||||
$apollo: {
|
||||
query: queryMock,
|
||||
},
|
||||
},
|
||||
localVue,
|
||||
})
|
||||
})
|
||||
describe('with up to 7 loaded connections', () => {
|
||||
let followingWrapper
|
||||
let followedByWrapper
|
||||
beforeAll(() => {
|
||||
followingWrapper = Wrapper()
|
||||
followedByWrapper = Wrapper({ type: 'followedBy' })
|
||||
})
|
||||
|
||||
it(`shows the users ${type}`, () => {
|
||||
expect(wrapper.findAll('.user-teaser').length).toEqual(propsData.user[type].length)
|
||||
})
|
||||
it(`renders the connections`, () => {
|
||||
expect(followedByWrapper.findAll('.user-teaser').length).toEqual(user.followedBy.length)
|
||||
expect(followingWrapper.findAll('.user-teaser').length).toEqual(user.following.length)
|
||||
})
|
||||
|
||||
it(`has a button to load all remaining users ${type}`, async () => {
|
||||
jest.useFakeTimers()
|
||||
it(`has a button to load all remaining connections`, async () => {
|
||||
followingWrapper.find('.base-button').trigger('click')
|
||||
followedByWrapper.find('.base-button').trigger('click')
|
||||
expect(followingWrapper.emitted('fetchAllConnections')).toBeTruthy()
|
||||
expect(followedByWrapper.emitted('fetchAllConnections')).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
wrapper.find('.base-button').trigger('click')
|
||||
await jest.runAllTicks()
|
||||
await wrapper.vm.$nextTick()
|
||||
describe('with more than 7 loaded connections', () => {
|
||||
let followingWrapper
|
||||
let followedByWrapper
|
||||
beforeAll(() => {
|
||||
followingWrapper = Wrapper({ user: allConnectionsUser })
|
||||
followedByWrapper = Wrapper({ user: allConnectionsUser, type: 'followedBy' })
|
||||
})
|
||||
|
||||
expect(wrapper.vm.connections.length).toBe(propsData.user[`${type}Count`])
|
||||
expect(queryMock).toHaveBeenCalledWith({
|
||||
query: wrapper.vm.queries[type],
|
||||
variables: { id: propsData.user.id },
|
||||
})
|
||||
})
|
||||
}),
|
||||
)
|
||||
})
|
||||
it('renders the connections', () => {
|
||||
expect(followedByWrapper.findAll('.user-teaser')).toHaveLength(
|
||||
allConnectionsUser.followedByCount,
|
||||
)
|
||||
expect(followingWrapper.findAll('.user-teaser')).toHaveLength(
|
||||
allConnectionsUser.followingCount,
|
||||
)
|
||||
})
|
||||
|
||||
describe('given a user without connections', () => {
|
||||
;['following', 'followedBy'].forEach((type) =>
|
||||
describe(`and type=${type}`, () => {
|
||||
let wrapper
|
||||
it('renders the user-teaser in an overflow-container', () => {
|
||||
expect(followingWrapper.find('.overflow-container').is('div')).toBe(true)
|
||||
expect(followedByWrapper.find('.overflow-container').is('div')).toBe(true)
|
||||
})
|
||||
|
||||
beforeAll(() => {
|
||||
wrapper = mount(FollowList, {
|
||||
store,
|
||||
mocks: {
|
||||
$t: jest.fn().mockReturnValue('has no connections'),
|
||||
},
|
||||
localVue,
|
||||
propsData: {
|
||||
user: {
|
||||
...propsData.user,
|
||||
followedByCount: 0,
|
||||
followingCount: 0,
|
||||
followedBy: [],
|
||||
following: [],
|
||||
},
|
||||
type,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('displays the no-follower message', () => {
|
||||
expect(wrapper.find('.no-connections').text()).toBe(
|
||||
`${propsData.user.name} ${wrapper.vm.$t()}`,
|
||||
)
|
||||
})
|
||||
}),
|
||||
)
|
||||
it('renders a filter text input', () => {
|
||||
expect(followingWrapper.find('[name="followingFilter"]').is('input')).toBe(true)
|
||||
expect(followedByWrapper.find('[name="followedByFilter"]').is('input')).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const additionalConnections = {
|
||||
followedBy: helpers.fakeUser(5),
|
||||
following: helpers.fakeUser(8),
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import Vue from 'vue'
|
||||
import { storiesOf } from '@storybook/vue'
|
||||
import { withA11y } from '@storybook/addon-a11y'
|
||||
import apolloStorybookDecorator from 'apollo-storybook-vue'
|
||||
import { action } from '@storybook/addon-actions'
|
||||
|
||||
import helpers from '~/storybook/helpers'
|
||||
import FollowList from './FollowList.vue'
|
||||
@ -20,38 +19,9 @@ const allConnectionsUser = {
|
||||
followedBy: [...sevenConnectionsUser.followedBy, ...helpers.fakeUser(5)],
|
||||
}
|
||||
|
||||
const mocks = {
|
||||
Query: () => ({
|
||||
User: () => [allConnectionsUser],
|
||||
}),
|
||||
}
|
||||
const typeDefs = `
|
||||
type User {
|
||||
followedByCount: Int
|
||||
followedBy(offset: Int): [User]
|
||||
name: String
|
||||
slug: String
|
||||
id: String
|
||||
}
|
||||
|
||||
type Query {
|
||||
User(id: ID!): [User]
|
||||
}
|
||||
schema {
|
||||
query: Query
|
||||
}
|
||||
`
|
||||
|
||||
storiesOf('FollowList', module)
|
||||
.addDecorator(withA11y)
|
||||
.addDecorator(helpers.layout)
|
||||
.addDecorator(
|
||||
apolloStorybookDecorator({
|
||||
mocks,
|
||||
typeDefs,
|
||||
Vue,
|
||||
}),
|
||||
)
|
||||
.add('without connections', () => {
|
||||
const user = {
|
||||
...sevenConnectionsUser,
|
||||
@ -77,7 +47,13 @@ storiesOf('FollowList', module)
|
||||
user,
|
||||
}
|
||||
},
|
||||
template: `<follow-list :user="user" type="followedBy" />`,
|
||||
methods: {
|
||||
fetchAllConnections(type) {
|
||||
this.user = allConnectionsUser
|
||||
action('fetchAllConnections')(type, this.user)
|
||||
},
|
||||
},
|
||||
template: `<follow-list :user="user" type="followedBy" @fetchAllConnections="fetchAllConnections"/>`,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@ -9,14 +9,19 @@
|
||||
<p class="no-connections">{{ userName }} {{ $t(`profile.network.${type}Nobody`) }}</p>
|
||||
</template>
|
||||
<template v-if="connections && connections.length <= 7">
|
||||
<ds-space v-for="follow in uniq(connections)" :key="follow.id" margin="x-small">
|
||||
<ds-space v-for="connection in connections" :key="connection.id" margin="x-small">
|
||||
<!-- TODO: find better solution for rendering errors -->
|
||||
<client-only>
|
||||
<user-teaser :user="follow" />
|
||||
<user-teaser :user="connection" />
|
||||
</client-only>
|
||||
</ds-space>
|
||||
<ds-space v-if="allConnectionsCount - connections.length" margin="small">
|
||||
<base-button @click="fetchConnections" :loading="isLoading" size="small" color="softer">
|
||||
<base-button
|
||||
@click="$emit('fetchAllConnections', type)"
|
||||
:loading="loading"
|
||||
size="small"
|
||||
color="softer"
|
||||
>
|
||||
{{
|
||||
$t('profile.network.andMore', {
|
||||
number: allConnectionsCount - connections.length,
|
||||
@ -27,13 +32,9 @@
|
||||
</template>
|
||||
<template v-else-if="connections.length > 7">
|
||||
<div class="overflow-container">
|
||||
<ds-space
|
||||
v-for="follow in uniq(filteredConnections)"
|
||||
:key="follow.id"
|
||||
margin="x-small"
|
||||
>
|
||||
<ds-space v-for="connection in filteredConnections" :key="connection.id" margin="x-small">
|
||||
<client-only>
|
||||
<user-teaser :user="follow" />
|
||||
<user-teaser :user="connection" />
|
||||
</client-only>
|
||||
</ds-space>
|
||||
</div>
|
||||
@ -44,6 +45,7 @@
|
||||
v-focus="true"
|
||||
size="small"
|
||||
icon="filter"
|
||||
:name="`${type}Filter`"
|
||||
/>
|
||||
</ds-space>
|
||||
</template>
|
||||
@ -51,9 +53,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uniqBy from 'lodash/uniqBy'
|
||||
import UserTeaser from '~/components/UserTeaser/UserTeaser'
|
||||
import { followedByQuery, followingQuery } from '~/graphql/User'
|
||||
|
||||
export default {
|
||||
name: 'FollowerList',
|
||||
@ -63,16 +63,11 @@ export default {
|
||||
props: {
|
||||
user: { type: Object, default: null },
|
||||
type: { type: String, default: 'following' },
|
||||
loading: { type: Boolean, default: false },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
additionalConnections: [],
|
||||
filter: null,
|
||||
isLoading: false,
|
||||
queries: {
|
||||
followedBy: followedByQuery,
|
||||
following: followingQuery,
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -80,8 +75,11 @@ export default {
|
||||
const { name } = this.user || {}
|
||||
return name || this.$t('profile.userAnonym')
|
||||
},
|
||||
allConnectionsCount() {
|
||||
return this.user[`${this.type}Count`]
|
||||
},
|
||||
connections() {
|
||||
return [...this.user[this.type], ...this.additionalConnections]
|
||||
return this.user[this.type]
|
||||
},
|
||||
filteredConnections() {
|
||||
if (!this.filter) {
|
||||
@ -107,26 +105,10 @@ export default {
|
||||
|
||||
return fuzzyScores.map((score) => score.user)
|
||||
},
|
||||
allConnectionsCount() {
|
||||
return this.user[`${this.type}Count`]
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
uniq(items, field = 'id') {
|
||||
return uniqBy(items, field)
|
||||
},
|
||||
async fetchConnections() {
|
||||
this.$set(this, 'isLoading', true)
|
||||
const { data } = await this.$apollo.query({
|
||||
query: this.queries[this.type],
|
||||
variables: { id: this.user.id },
|
||||
// neither result nor update are being called when defined here (?)
|
||||
})
|
||||
this.additionalConnections = data.User[0][this.type]
|
||||
this.$set(this, 'isLoading', false)
|
||||
},
|
||||
setFilter(evt) {
|
||||
this.$set(this, 'filter', evt.target.value)
|
||||
this.filter = evt.target.value
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ export default (i18n) => {
|
||||
${userCountsFragment}
|
||||
${locationAndBadgesFragment(lang)}
|
||||
|
||||
query User($id: ID!) {
|
||||
query User($id: ID!, $followedByCount: Int, $followingCount: Int) {
|
||||
User(id: $id) {
|
||||
...user
|
||||
...userCounts
|
||||
@ -26,12 +26,12 @@ export default (i18n) => {
|
||||
isMuted
|
||||
isBlocked
|
||||
blocked
|
||||
following(first: 7) {
|
||||
following(first: $followingCount) {
|
||||
...user
|
||||
...userCounts
|
||||
...locationAndBadges
|
||||
}
|
||||
followedBy(first: 7) {
|
||||
followedBy(first: $followedByCount) {
|
||||
...user
|
||||
...userCounts
|
||||
...locationAndBadges
|
||||
@ -283,27 +283,3 @@ export const currentUserQuery = gql`
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const followedByQuery = gql`
|
||||
query($id: ID!) {
|
||||
User(id: $id) {
|
||||
followedBy(offset: 7) {
|
||||
id
|
||||
slug
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const followingQuery = gql`
|
||||
query($id: ID!) {
|
||||
User(id: $id) {
|
||||
following(offset: 7) {
|
||||
id
|
||||
slug
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@ -89,9 +89,19 @@
|
||||
<ds-heading tag="h3" soft style="text-align: center; margin-bottom: 10px;">
|
||||
{{ $t('profile.network.title') }}
|
||||
</ds-heading>
|
||||
<follow-list :user="user" type="followedBy" />
|
||||
<follow-list
|
||||
:user="user"
|
||||
type="followedBy"
|
||||
@fetchAllConnections="fetchAllConnections"
|
||||
:loading="$apollo.loading"
|
||||
/>
|
||||
<ds-space />
|
||||
<follow-list :user="user" type="following" />
|
||||
<follow-list
|
||||
:user="user"
|
||||
type="following"
|
||||
@fetchAllConnections="fetchAllConnections"
|
||||
:loading="$apollo.loading"
|
||||
/>
|
||||
<ds-space v-if="user.socialMedia && user.socialMedia.length" margin="large">
|
||||
<base-card style="position: relative; height: auto;">
|
||||
<ds-space margin="x-small">
|
||||
@ -270,6 +280,8 @@ export default {
|
||||
tabActive: 'post',
|
||||
filter,
|
||||
followedByCountStartValue: 0,
|
||||
followedByCount: 7,
|
||||
followingCount: 7,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -299,13 +311,6 @@ export default {
|
||||
return slug && `@${slug}`
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
User(val) {
|
||||
if (!val || !val.length) {
|
||||
throw new Error('User not found!')
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
removePostFromList(deletedPost) {
|
||||
this.posts = this.posts.filter((post) => {
|
||||
@ -427,6 +432,9 @@ export default {
|
||||
this.user.followedByCurrentUser = followedByCurrentUser
|
||||
this.user.followedBy = followedBy
|
||||
},
|
||||
fetchAllConnections(type) {
|
||||
this[`${type}Count`] = Infinity
|
||||
},
|
||||
},
|
||||
apollo: {
|
||||
profilePagePosts: {
|
||||
@ -451,7 +459,11 @@ export default {
|
||||
return UserQuery(this.$i18n)
|
||||
},
|
||||
variables() {
|
||||
return { id: this.$route.params.id }
|
||||
return {
|
||||
id: this.$route.params.id,
|
||||
followedByCount: this.followedByCount || 7,
|
||||
followingCount: this.followingCount || 7,
|
||||
}
|
||||
},
|
||||
fetchPolicy: 'cache-and-network',
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user