mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge pull request #3293 from Human-Connection/1741-follow-lists
feat: FollowerList [1741]
This commit is contained in:
commit
9aacb22a58
@ -928,7 +928,15 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
|||||||
trollingComment.update({ disabled: true, updatedAt: new Date().toISOString(), closed: true }),
|
trollingComment.update({ disabled: true, updatedAt: new Date().toISOString(), closed: true }),
|
||||||
])
|
])
|
||||||
|
|
||||||
await Promise.all([...Array(30).keys()].map(() => Factory.build('user')))
|
const additionalUsers = await Promise.all(
|
||||||
|
[...Array(30).keys()].map(() => Factory.build('user')),
|
||||||
|
)
|
||||||
|
await Promise.all(
|
||||||
|
additionalUsers.map(async (user) => {
|
||||||
|
await jennyRostock.relateTo(user, 'following')
|
||||||
|
await user.relateTo(jennyRostock, 'following')
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
[...Array(30).keys()].map(() =>
|
[...Array(30).keys()].map(() =>
|
||||||
|
|||||||
@ -230,6 +230,7 @@ $space-small: 16px;
|
|||||||
$space-x-small: 8px;
|
$space-x-small: 8px;
|
||||||
$space-xx-small: 4px;
|
$space-xx-small: 4px;
|
||||||
$space-xxx-small: 2px;
|
$space-xxx-small: 2px;
|
||||||
|
$space-none: 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @tokens Size Height
|
* @tokens Size Height
|
||||||
@ -240,6 +241,7 @@ $size-height-base: 42px;
|
|||||||
$size-height-large: 50px;
|
$size-height-large: 50px;
|
||||||
$size-height-xlarge: 60px;
|
$size-height-xlarge: 60px;
|
||||||
$size-height-footer: 64px;
|
$size-height-footer: 64px;
|
||||||
|
$size-height-connections: 315px;
|
||||||
$size-tappable-square: 44px;
|
$size-tappable-square: 44px;
|
||||||
$size-ribbon: 6px;
|
$size-ribbon: 6px;
|
||||||
|
|
||||||
|
|||||||
@ -3,9 +3,11 @@
|
|||||||
<span class="initials">{{ userInitials }}</span>
|
<span class="initials">{{ userInitials }}</span>
|
||||||
<base-icon v-if="isAnonymous" name="eye-slash" />
|
<base-icon v-if="isAnonymous" name="eye-slash" />
|
||||||
<img
|
<img
|
||||||
v-else
|
v-if="user && user.avatar"
|
||||||
:src="user.avatar | proxyApiUrl"
|
:src="user.avatar | proxyApiUrl"
|
||||||
class="image"
|
class="image"
|
||||||
|
:alt="user.name"
|
||||||
|
:title="user.name"
|
||||||
@error="$event.target.style.display = 'none'"
|
@error="$event.target.style.display = 'none'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
150
webapp/components/features/FollowList/FollowList.spec.js
Normal file
150
webapp/components/features/FollowList/FollowList.spec.js
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import { config, mount } from '@vue/test-utils'
|
||||||
|
import Vuex from 'vuex'
|
||||||
|
|
||||||
|
import helpers from '~/storybook/helpers'
|
||||||
|
import FollowList from './FollowList.vue'
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
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, getters
|
||||||
|
const Wrapper = (customProps) =>
|
||||||
|
mount(FollowList, {
|
||||||
|
store,
|
||||||
|
propsData: { user, ...customProps },
|
||||||
|
mocks: {
|
||||||
|
$t: jest.fn((str) => str),
|
||||||
|
},
|
||||||
|
localVue,
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
getters = {
|
||||||
|
'auth/user': () => {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
'auth/isModerator': () => false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
store = new Vuex.Store({
|
||||||
|
getters,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('given a type', () => {
|
||||||
|
describe('of `following`', () => {
|
||||||
|
it('uses the `following` data on :user', () => {
|
||||||
|
const wrapper = Wrapper({ user, type: 'following' })
|
||||||
|
wrapper.find('.base-button').trigger('click')
|
||||||
|
|
||||||
|
expect(wrapper.vm.allConnectionsCount).toBe(user.followingCount)
|
||||||
|
expect(wrapper.findAll('.user-teaser')).toHaveLength(user.following.length)
|
||||||
|
expect(wrapper.emitted('fetchAllConnections')).toEqual([['following']])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('of `followedBy`', () => {
|
||||||
|
it('uses the `followedBy` data on :user', () => {
|
||||||
|
const wrapper = Wrapper({ type: 'followedBy' })
|
||||||
|
wrapper.find('.base-button').trigger('click')
|
||||||
|
|
||||||
|
expect(wrapper.vm.allConnectionsCount).toBe(user.followedByCount)
|
||||||
|
expect(wrapper.findAll('.user-teaser')).toHaveLength(user.followedBy.length)
|
||||||
|
expect(wrapper.emitted('fetchAllConnections')).toEqual([['followedBy']])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('given no type', () => {
|
||||||
|
it('defaults type to `following`', () => {
|
||||||
|
const wrapper = Wrapper()
|
||||||
|
expect(wrapper.text()).toContain('profile.network.following')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('given a user', () => {
|
||||||
|
describe('without connections', () => {
|
||||||
|
it('displays the followingNobody message', () => {
|
||||||
|
const wrapper = Wrapper({ user: noConnectionsUser })
|
||||||
|
expect(wrapper.find('.nobody-message').text()).toContain(
|
||||||
|
'profile.network.followingNobody',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('displays the followedByNobody message', () => {
|
||||||
|
const wrapper = Wrapper({ user: noConnectionsUser, type: 'followedBy' })
|
||||||
|
expect(wrapper.find('.nobody-message').text()).toContain(
|
||||||
|
'profile.network.followedByNobody',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with up to 7 loaded connections', () => {
|
||||||
|
let wrapper
|
||||||
|
beforeAll(() => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`renders the connections`, () => {
|
||||||
|
expect(wrapper.findAll('.user-teaser')).toHaveLength(user.following.length)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`has a button to load all remaining connections`, async () => {
|
||||||
|
wrapper.find('.base-button').trigger('click')
|
||||||
|
expect(wrapper.emitted('fetchAllConnections')).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with more than 7 loaded connections', () => {
|
||||||
|
let wrapper
|
||||||
|
beforeAll(() => {
|
||||||
|
wrapper = Wrapper({ user: allConnectionsUser })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders the connections', () => {
|
||||||
|
expect(wrapper.findAll('.user-teaser')).toHaveLength(allConnectionsUser.followingCount)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders the user-teasers as an overflowing list', () => {
|
||||||
|
expect(wrapper.find('.--overflow').is('ul')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders a filter text input', () => {
|
||||||
|
expect(wrapper.find('[name="followingFilter"]').is('input')).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
111
webapp/components/features/FollowList/FollowList.story.js
Normal file
111
webapp/components/features/FollowList/FollowList.story.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import { storiesOf } from '@storybook/vue'
|
||||||
|
import { withA11y } from '@storybook/addon-a11y'
|
||||||
|
import { action } from '@storybook/addon-actions'
|
||||||
|
|
||||||
|
import helpers from '~/storybook/helpers'
|
||||||
|
import FollowList from './FollowList.vue'
|
||||||
|
|
||||||
|
import fuzzyFilterUser from './FollowList.story.json'
|
||||||
|
|
||||||
|
helpers.init()
|
||||||
|
|
||||||
|
const user = {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u3',
|
||||||
|
followedByCount: 12,
|
||||||
|
followedBy: helpers.fakeUser(7),
|
||||||
|
followingCount: 28,
|
||||||
|
following: helpers.fakeUser(7),
|
||||||
|
}
|
||||||
|
|
||||||
|
const lessThanSevenUser = {
|
||||||
|
...user,
|
||||||
|
followedByCount: 3,
|
||||||
|
followedBy: user.followedBy.slice(0, 3),
|
||||||
|
followingCount: 3,
|
||||||
|
following: user.following.slice(0, 3),
|
||||||
|
}
|
||||||
|
|
||||||
|
const allConnectionsUser = {
|
||||||
|
...user,
|
||||||
|
followedBy: [...user.followedBy, ...helpers.fakeUser(5)],
|
||||||
|
following: [...user.following, ...helpers.fakeUser(21)],
|
||||||
|
}
|
||||||
|
|
||||||
|
const noConnectionsUser = {
|
||||||
|
...user,
|
||||||
|
followedBy: [],
|
||||||
|
followedByCount: 0,
|
||||||
|
following: [],
|
||||||
|
followingCount: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
storiesOf('FollowList', module)
|
||||||
|
.addDecorator(withA11y)
|
||||||
|
.addDecorator(helpers.layout)
|
||||||
|
.add('without connections', () => {
|
||||||
|
return {
|
||||||
|
components: { FollowList },
|
||||||
|
store: helpers.store,
|
||||||
|
data() {
|
||||||
|
return { user: noConnectionsUser }
|
||||||
|
},
|
||||||
|
template: '<follow-list :user="user" type="following" />',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.add('with all connections loaded', () => {
|
||||||
|
return {
|
||||||
|
components: { FollowList },
|
||||||
|
store: helpers.store,
|
||||||
|
data() {
|
||||||
|
return { user: lessThanSevenUser }
|
||||||
|
},
|
||||||
|
|
||||||
|
template: '<follow-list :user="user"/>',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
.add('with more connections loadable', () => {
|
||||||
|
return {
|
||||||
|
components: { FollowList },
|
||||||
|
store: helpers.store,
|
||||||
|
data() {
|
||||||
|
return { user: { ...user } }
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchAllConnections(type) {
|
||||||
|
this.user[type] = allConnectionsUser[type]
|
||||||
|
action('fetchAllConnections')(type, this.user)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: '<follow-list :user="user" @fetchAllConnections="fetchAllConnections"/>',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.add('with 1000 connections loaded', () => {
|
||||||
|
return {
|
||||||
|
components: { FollowList },
|
||||||
|
store: helpers.store,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
user: {
|
||||||
|
...user,
|
||||||
|
followedByCount: 1000,
|
||||||
|
followingCount: 1000,
|
||||||
|
followedBy: helpers.fakeUser(1000),
|
||||||
|
following: helpers.fakeUser(1000),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: '<follow-list :user="user" />',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.add('Fuzzy Filter', () => {
|
||||||
|
return {
|
||||||
|
components: { FollowList },
|
||||||
|
store: helpers.store,
|
||||||
|
data() {
|
||||||
|
return { user: fuzzyFilterUser }
|
||||||
|
},
|
||||||
|
template: '<follow-list :user="user" />',
|
||||||
|
}
|
||||||
|
})
|
||||||
519
webapp/components/features/FollowList/FollowList.story.json
Normal file
519
webapp/components/features/FollowList/FollowList.story.json
Normal file
@ -0,0 +1,519 @@
|
|||||||
|
{
|
||||||
|
"id": "u3",
|
||||||
|
"slug": "jenny-rostock",
|
||||||
|
"name": "Jenny Rostock",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/nessoila/128.jpg?random=16aba55f-48c6-4ef5-966a-af0e31693e6a"
|
||||||
|
},
|
||||||
|
"followedByCount": 32,
|
||||||
|
"followingCount": 31,
|
||||||
|
"following": [
|
||||||
|
{
|
||||||
|
"id": "8c0b0191-454a-4806-b0c3-1ed1d9dc5416",
|
||||||
|
"slug": "wanda-stanton-sr",
|
||||||
|
"name": "Wanda Stanton Sr.",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/nicolasfolliot/128.jpg?random=be2e59fb-f038-44d5-9e21-28fb123e8c7c"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6f4fdb51-fe93-4373-bf44-c36ea17f6535",
|
||||||
|
"slug": "walter-ohara",
|
||||||
|
"name": "Walter O'Hara",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/mutlu82/128.jpg?random=0866e1b7-8285-4eaf-a4c8-8509bd99f172"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "20f5080b-1fc2-4c70-94c7-badbe2aa2370",
|
||||||
|
"slug": "hannah-bradtke",
|
||||||
|
"name": "Hannah Bradtke",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/Skyhartman/128.jpg?random=c02415a6-1e7e-488a-acb6-47587399ff07"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c5d2c492-5b8c-41a5-a9a2-26f8c866a885",
|
||||||
|
"slug": "georgia-koss-dvm",
|
||||||
|
"name": "Georgia Koss DVM",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/lvovenok/128.jpg?random=8e1e75b9-5dad-4fc8-9c52-38b89a3e0484"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "f4c10451-f367-4afb-97fe-e48f60236655",
|
||||||
|
"slug": "genevieve-aufderhar-v",
|
||||||
|
"name": "Genevieve Aufderhar V",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/dss49/128.jpg?random=fc59a664-d2b4-4677-81e9-26ed5d4bc37e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "df65a474-b661-4fd8-b49f-2d609bfc4089",
|
||||||
|
"slug": "wanda-gerlach",
|
||||||
|
"name": "Wanda Gerlach",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/lewisainslie/128.jpg?random=5f0976d5-7871-4606-a416-6b67b0e67c15"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5f2b3c14-07fc-4dfa-978e-9bf21d8586d0",
|
||||||
|
"slug": "roland-kohler",
|
||||||
|
"name": "Roland Kohler",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/bcrad/128.jpg?random=4f77c5b2-0214-4342-a18e-424787f04896"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "a2c49c22-940a-4598-8285-b8ebb34bd045",
|
||||||
|
"slug": "krista-hammes-dvm",
|
||||||
|
"name": "Krista Hammes DVM",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/sterlingrules/128.jpg?random=28167137-eee4-42e6-98a3-ec9a7d4b9509"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "da70bae8-daf2-4481-b2db-506d00b73210",
|
||||||
|
"slug": "miss-lester-kovacek",
|
||||||
|
"name": "Miss Lester Kovacek",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/stefvdham/128.jpg?random=7de26019-8114-4caa-9aea-2172ae3c537c"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "868952d8-c5df-4415-a0bb-930acd26749c",
|
||||||
|
"slug": "teri-stamm-iii",
|
||||||
|
"name": "Teri Stamm III",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/salimianoff/128.jpg?random=d2305511-39b2-4207-bee6-90799557546e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "7a569786-fbb1-4e27-8f2c-38d2a0963b9d",
|
||||||
|
"slug": "gerardo-batz",
|
||||||
|
"name": "Gerardo Batz",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/colirpixoil/128.jpg?random=8536b980-103e-4e5e-8141-31142e3c6330"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dca673f2-89b8-46cc-a836-2515f319c02f",
|
||||||
|
"slug": "carlton-botsford",
|
||||||
|
"name": "Carlton Botsford",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/cocolero/128.jpg?random=3ef57800-7694-4ec4-a9eb-39abca8d59e4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "58ba4ab0-990e-4f44-a7a8-42d10962f8b7",
|
||||||
|
"slug": "edward-boehm",
|
||||||
|
"name": "Edward Boehm",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/d_nny_m_cher/128.jpg?random=cf3ba671-4564-4dc1-bf94-a5fc3ea4fafa"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "u4",
|
||||||
|
"slug": "huey",
|
||||||
|
"name": "Huey",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/hellofeverrrr/128.jpg?random=46fef3a9-44a0-4784-9854-03be6b3f37e1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "40cf7e21-a4dc-4055-a49d-3137c53dd815",
|
||||||
|
"slug": "ms-laurie-bergnaum",
|
||||||
|
"name": "Ms. Laurie Bergnaum",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/kohette/128.jpg?random=45516cde-9073-4ceb-8b42-2bdf66a26c67"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4c377f54-72c3-4e13-9532-6d0f2278a794",
|
||||||
|
"slug": "jeffery-waelchi",
|
||||||
|
"name": "Jeffery Waelchi",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/andresenfredrik/128.jpg?random=b643d8ba-5e49-4854-b2a6-2ff609ffcba0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "d861e775-2344-40d5-b6e4-8479e7dbe97f",
|
||||||
|
"slug": "marvin-gutkowski-iii",
|
||||||
|
"name": "Marvin Gutkowski III",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/kiwiupover/128.jpg?random=d38c1508-0342-46e0-9ef9-e0fd14b33072"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3102346c-ea94-41d3-8474-5241e5e473a6",
|
||||||
|
"slug": "edmund-rolfson-jr",
|
||||||
|
"name": "Edmund Rolfson Jr.",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/nerrsoft/128.jpg?random=d6f0804c-f38f-4c8b-821d-27a02373c74e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "0290fd5e-7ce8-4d70-8c29-f5ed96bfaa1f",
|
||||||
|
"slug": "bert-oconner",
|
||||||
|
"name": "Bert O'Conner",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/mangosango/128.jpg?random=03807c6c-c2f6-4ed3-8433-7a1823079eb0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9fe670ff-1968-4b15-ab80-7adee8173dd8",
|
||||||
|
"slug": "angela-schiller",
|
||||||
|
"name": "Angela Schiller",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/dudestein/128.jpg?random=0b511fa4-8603-419a-acf0-2a89bc41a71b"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "63798c79-2476-4eb8-a09c-167420bd05d7",
|
||||||
|
"slug": "peggy-carter",
|
||||||
|
"name": "Peggy Carter",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/uxpiper/128.jpg?random=c4dc0de5-99d7-410d-8088-6a2be8358fe9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4d3fbb67-7ddc-4aa9-96d0-57bb20f95f59",
|
||||||
|
"slug": "krystal-braun",
|
||||||
|
"name": "Krystal Braun",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/salvafc/128.jpg?random=1dbd4549-a525-4fa3-be41-15db84ceb41a"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bcd5f271-bca5-492e-a6b6-7fd490ac37aa",
|
||||||
|
"slug": "janet-bruen",
|
||||||
|
"name": "Janet Bruen",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/yigitpinarbasi/128.jpg?random=3c7ab2c0-8e53-4edb-8c74-fd8cdfae5f8e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "38ccbf58-305a-4400-a1e4-785661604d5f",
|
||||||
|
"slug": "ebony-corwin",
|
||||||
|
"name": "Ebony Corwin",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/thibaut_re/128.jpg?random=f2137643-6f58-45b8-8266-d1fb893d0528"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5137e045-aef0-4ce1-8327-b516a72fffd6",
|
||||||
|
"slug": "terrell-gorczany",
|
||||||
|
"name": "Terrell Gorczany",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/jimmuirhead/128.jpg?random=5e61976f-529e-47f2-b4f0-b5e4550942ea"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b224eb01-5a71-4126-b811-51b7b35afd51",
|
||||||
|
"slug": "dr-jamie-romaguera",
|
||||||
|
"name": "Dr. Jamie Romaguera",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/leonfedotov/128.jpg?random=9179538d-58be-4707-8ad6-485f703bea26"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fdbb3e7f-f211-4685-831e-9d7e394b95a1",
|
||||||
|
"slug": "shannon-schuster",
|
||||||
|
"name": "Shannon Schuster",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/markretzloff/128.jpg?random=f8a223ff-d49f-499f-a161-350e8078c353"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "d7ee00ea-5d22-4a1c-8670-7e4d4bd4ebfa",
|
||||||
|
"slug": "guadalupe-kessler",
|
||||||
|
"name": "Guadalupe Kessler",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/linux29/128.jpg?random=a87d13e1-ff0e-4a39-a018-e89a04d72572"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "49b868e3-dba5-48ed-8b6c-e686c70dfcfb",
|
||||||
|
"slug": "lonnie-rogahn",
|
||||||
|
"name": "Lonnie Rogahn",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/herbigt/128.jpg?random=fde8a699-624d-4052-96ef-0aaacf217184"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fa70eecb-fbae-4133-b447-6c80b335fc01",
|
||||||
|
"slug": "marcella-ledner",
|
||||||
|
"name": "Marcella Ledner",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/dawidwu/128.jpg?random=03869abc-1a6b-4f26-8d73-7ba508c148d0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ef4cc540-c784-407f-b646-cff4d8fe666e",
|
||||||
|
"slug": "darrell-runolfsson",
|
||||||
|
"name": "Darrell Runolfsson",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/andrewofficer/128.jpg?random=e6c24db9-f1c9-45b5-8507-609fca960e92"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"followedBy": [
|
||||||
|
{
|
||||||
|
"id": "8c0b0191-454a-4806-b0c3-1ed1d9dc5416",
|
||||||
|
"slug": "wanda-stanton-sr",
|
||||||
|
"name": "Wanda Stanton Sr.",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/nicolasfolliot/128.jpg?random=be2e59fb-f038-44d5-9e21-28fb123e8c7c"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c5d2c492-5b8c-41a5-a9a2-26f8c866a885",
|
||||||
|
"slug": "georgia-koss-dvm",
|
||||||
|
"name": "Georgia Koss DVM",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/lvovenok/128.jpg?random=8e1e75b9-5dad-4fc8-9c52-38b89a3e0484"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5f2b3c14-07fc-4dfa-978e-9bf21d8586d0",
|
||||||
|
"slug": "roland-kohler",
|
||||||
|
"name": "Roland Kohler",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/bcrad/128.jpg?random=4f77c5b2-0214-4342-a18e-424787f04896"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6f4fdb51-fe93-4373-bf44-c36ea17f6535",
|
||||||
|
"slug": "walter-ohara",
|
||||||
|
"name": "Walter O'Hara",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/mutlu82/128.jpg?random=0866e1b7-8285-4eaf-a4c8-8509bd99f172"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "20f5080b-1fc2-4c70-94c7-badbe2aa2370",
|
||||||
|
"slug": "hannah-bradtke",
|
||||||
|
"name": "Hannah Bradtke",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/Skyhartman/128.jpg?random=c02415a6-1e7e-488a-acb6-47587399ff07"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "df65a474-b661-4fd8-b49f-2d609bfc4089",
|
||||||
|
"slug": "wanda-gerlach",
|
||||||
|
"name": "Wanda Gerlach",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/lewisainslie/128.jpg?random=5f0976d5-7871-4606-a416-6b67b0e67c15"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "f4c10451-f367-4afb-97fe-e48f60236655",
|
||||||
|
"slug": "genevieve-aufderhar-v",
|
||||||
|
"name": "Genevieve Aufderhar V",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/dss49/128.jpg?random=fc59a664-d2b4-4677-81e9-26ed5d4bc37e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "7a569786-fbb1-4e27-8f2c-38d2a0963b9d",
|
||||||
|
"slug": "gerardo-batz",
|
||||||
|
"name": "Gerardo Batz",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/colirpixoil/128.jpg?random=8536b980-103e-4e5e-8141-31142e3c6330"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "da70bae8-daf2-4481-b2db-506d00b73210",
|
||||||
|
"slug": "miss-lester-kovacek",
|
||||||
|
"name": "Miss Lester Kovacek",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/stefvdham/128.jpg?random=7de26019-8114-4caa-9aea-2172ae3c537c"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "a2c49c22-940a-4598-8285-b8ebb34bd045",
|
||||||
|
"slug": "krista-hammes-dvm",
|
||||||
|
"name": "Krista Hammes DVM",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/sterlingrules/128.jpg?random=28167137-eee4-42e6-98a3-ec9a7d4b9509"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bcd5f271-bca5-492e-a6b6-7fd490ac37aa",
|
||||||
|
"slug": "janet-bruen",
|
||||||
|
"name": "Janet Bruen",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/yigitpinarbasi/128.jpg?random=3c7ab2c0-8e53-4edb-8c74-fd8cdfae5f8e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "38ccbf58-305a-4400-a1e4-785661604d5f",
|
||||||
|
"slug": "ebony-corwin",
|
||||||
|
"name": "Ebony Corwin",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/thibaut_re/128.jpg?random=f2137643-6f58-45b8-8266-d1fb893d0528"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "d7ee00ea-5d22-4a1c-8670-7e4d4bd4ebfa",
|
||||||
|
"slug": "guadalupe-kessler",
|
||||||
|
"name": "Guadalupe Kessler",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/linux29/128.jpg?random=a87d13e1-ff0e-4a39-a018-e89a04d72572"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dca673f2-89b8-46cc-a836-2515f319c02f",
|
||||||
|
"slug": "carlton-botsford",
|
||||||
|
"name": "Carlton Botsford",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/cocolero/128.jpg?random=3ef57800-7694-4ec4-a9eb-39abca8d59e4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "49b868e3-dba5-48ed-8b6c-e686c70dfcfb",
|
||||||
|
"slug": "lonnie-rogahn",
|
||||||
|
"name": "Lonnie Rogahn",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/herbigt/128.jpg?random=fde8a699-624d-4052-96ef-0aaacf217184"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4d3fbb67-7ddc-4aa9-96d0-57bb20f95f59",
|
||||||
|
"slug": "krystal-braun",
|
||||||
|
"name": "Krystal Braun",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/salvafc/128.jpg?random=1dbd4549-a525-4fa3-be41-15db84ceb41a"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "58ba4ab0-990e-4f44-a7a8-42d10962f8b7",
|
||||||
|
"slug": "edward-boehm",
|
||||||
|
"name": "Edward Boehm",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/d_nny_m_cher/128.jpg?random=cf3ba671-4564-4dc1-bf94-a5fc3ea4fafa"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "868952d8-c5df-4415-a0bb-930acd26749c",
|
||||||
|
"slug": "teri-stamm-iii",
|
||||||
|
"name": "Teri Stamm III",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/salimianoff/128.jpg?random=d2305511-39b2-4207-bee6-90799557546e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fa70eecb-fbae-4133-b447-6c80b335fc01",
|
||||||
|
"slug": "marcella-ledner",
|
||||||
|
"name": "Marcella Ledner",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/dawidwu/128.jpg?random=03869abc-1a6b-4f26-8d73-7ba508c148d0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "63798c79-2476-4eb8-a09c-167420bd05d7",
|
||||||
|
"slug": "peggy-carter",
|
||||||
|
"name": "Peggy Carter",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/uxpiper/128.jpg?random=c4dc0de5-99d7-410d-8088-6a2be8358fe9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9fe670ff-1968-4b15-ab80-7adee8173dd8",
|
||||||
|
"slug": "angela-schiller",
|
||||||
|
"name": "Angela Schiller",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/dudestein/128.jpg?random=0b511fa4-8603-419a-acf0-2a89bc41a71b"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fdbb3e7f-f211-4685-831e-9d7e394b95a1",
|
||||||
|
"slug": "shannon-schuster",
|
||||||
|
"name": "Shannon Schuster",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/markretzloff/128.jpg?random=f8a223ff-d49f-499f-a161-350e8078c353"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5137e045-aef0-4ce1-8327-b516a72fffd6",
|
||||||
|
"slug": "terrell-gorczany",
|
||||||
|
"name": "Terrell Gorczany",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/jimmuirhead/128.jpg?random=5e61976f-529e-47f2-b4f0-b5e4550942ea"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ef4cc540-c784-407f-b646-cff4d8fe666e",
|
||||||
|
"slug": "darrell-runolfsson",
|
||||||
|
"name": "Darrell Runolfsson",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/andrewofficer/128.jpg?random=e6c24db9-f1c9-45b5-8507-609fca960e92"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b224eb01-5a71-4126-b811-51b7b35afd51",
|
||||||
|
"slug": "dr-jamie-romaguera",
|
||||||
|
"name": "Dr. Jamie Romaguera",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/leonfedotov/128.jpg?random=9179538d-58be-4707-8ad6-485f703bea26"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "u1",
|
||||||
|
"slug": "peter-lustig",
|
||||||
|
"name": "Peter Lustig",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/timgthomas/128.jpg?random=189d17f8-02d1-4b01-a000-af7121ae6897"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "u6",
|
||||||
|
"slug": "louie",
|
||||||
|
"name": "Louie",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/yayteejay/128.jpg?random=eefe597c-d49a-494f-b311-d6f2645d2264"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3102346c-ea94-41d3-8474-5241e5e473a6",
|
||||||
|
"slug": "edmund-rolfson-jr",
|
||||||
|
"name": "Edmund Rolfson Jr.",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/nerrsoft/128.jpg?random=d6f0804c-f38f-4c8b-821d-27a02373c74e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "40cf7e21-a4dc-4055-a49d-3137c53dd815",
|
||||||
|
"slug": "ms-laurie-bergnaum",
|
||||||
|
"name": "Ms. Laurie Bergnaum",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/kohette/128.jpg?random=45516cde-9073-4ceb-8b42-2bdf66a26c67"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "d861e775-2344-40d5-b6e4-8479e7dbe97f",
|
||||||
|
"slug": "marvin-gutkowski-iii",
|
||||||
|
"name": "Marvin Gutkowski III",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/kiwiupover/128.jpg?random=d38c1508-0342-46e0-9ef9-e0fd14b33072"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4c377f54-72c3-4e13-9532-6d0f2278a794",
|
||||||
|
"slug": "jeffery-waelchi",
|
||||||
|
"name": "Jeffery Waelchi",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/andresenfredrik/128.jpg?random=b643d8ba-5e49-4854-b2a6-2ff609ffcba0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "0290fd5e-7ce8-4d70-8c29-f5ed96bfaa1f",
|
||||||
|
"slug": "bert-oconner",
|
||||||
|
"name": "Bert O'Conner",
|
||||||
|
"avatar": {
|
||||||
|
"url": "https://s3.amazonaws.com/uifaces/faces/twitter/mangosango/128.jpg?random=03807c6c-c2f6-4ed3-8433-7a1823079eb0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
165
webapp/components/features/FollowList/FollowList.vue
Normal file
165
webapp/components/features/FollowList/FollowList.vue
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
<template>
|
||||||
|
<base-card class="follow-list">
|
||||||
|
<template v-if="connections && connections.length">
|
||||||
|
<h5 class="title spacer-x-small">
|
||||||
|
{{ userName | truncate(15) }} {{ $t(`profile.network.${type}`) }}
|
||||||
|
</h5>
|
||||||
|
<ul :class="connectionsClass">
|
||||||
|
<li
|
||||||
|
v-for="connection in filteredConnections"
|
||||||
|
:key="connection.id"
|
||||||
|
class="connections__item"
|
||||||
|
>
|
||||||
|
<user-teaser :user="connection" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<base-button
|
||||||
|
v-if="hasMore"
|
||||||
|
:loading="loading"
|
||||||
|
class="spacer-x-small"
|
||||||
|
size="small"
|
||||||
|
@click="$emit('fetchAllConnections', type)"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
$t('profile.network.andMore', {
|
||||||
|
number: allConnectionsCount - connections.length,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</base-button>
|
||||||
|
<ds-input
|
||||||
|
v-if="!hasMore"
|
||||||
|
v-focus="true"
|
||||||
|
:name="`${type}Filter`"
|
||||||
|
:placeholder="filter"
|
||||||
|
class="spacer-x-small"
|
||||||
|
icon="filter"
|
||||||
|
size="small"
|
||||||
|
@input.native="setFilter"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<p v-else class="nobody-message">{{ userName }} {{ $t(`profile.network.${type}Nobody`) }}</p>
|
||||||
|
</base-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { escape } from 'xregexp/xregexp-all.js'
|
||||||
|
import UserTeaser from '~/components/UserTeaser/UserTeaser'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'FollowerList',
|
||||||
|
components: {
|
||||||
|
UserTeaser,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
user: { type: Object, default: null },
|
||||||
|
type: { type: String, default: 'following' },
|
||||||
|
loading: { type: Boolean, default: false },
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
filter: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
userName() {
|
||||||
|
const { name } = this.user || {}
|
||||||
|
return name || this.$t('profile.userAnonym')
|
||||||
|
},
|
||||||
|
allConnectionsCount() {
|
||||||
|
return this.user[`${this.type}Count`]
|
||||||
|
},
|
||||||
|
connections() {
|
||||||
|
return this.user[this.type]
|
||||||
|
},
|
||||||
|
hasMore() {
|
||||||
|
return this.allConnectionsCount > this.connections.length
|
||||||
|
},
|
||||||
|
connectionsClass() {
|
||||||
|
return `connections${this.hasMore ? '' : ' --overflow'}`
|
||||||
|
},
|
||||||
|
filteredConnections() {
|
||||||
|
if (!this.filter) {
|
||||||
|
return this.connections
|
||||||
|
}
|
||||||
|
|
||||||
|
// @example
|
||||||
|
// this.filter = 'foo';
|
||||||
|
// fuzzyExpression = /([^f]*f)([^o]*o)([^o]*o)/i
|
||||||
|
const fuzzyExpression = new RegExp(
|
||||||
|
`${this.filter.split('').reduce((expr, c) => `${expr}([^${escape(c)}]*${escape(c)})`, '')}`,
|
||||||
|
'i',
|
||||||
|
)
|
||||||
|
|
||||||
|
const fuzzyScores = this.connections
|
||||||
|
.map((user) => {
|
||||||
|
const match = user.name.match(fuzzyExpression)
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let score = 1
|
||||||
|
for (let i = 1; i <= this.filter.length; i++) {
|
||||||
|
score *= match[i].length
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
user,
|
||||||
|
score,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.sort((a, b) => a.score - b.score)
|
||||||
|
|
||||||
|
return fuzzyScores.map((score) => score.user)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setFilter(evt) {
|
||||||
|
this.filter = evt.target.value
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.follow-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
width: auto;
|
||||||
|
|
||||||
|
> .title {
|
||||||
|
color: $text-color-soft;
|
||||||
|
font-size: $font-size-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
.connections {
|
||||||
|
height: $size-height-connections;
|
||||||
|
padding: $space-none;
|
||||||
|
list-style-type: none;
|
||||||
|
|
||||||
|
&.--overflow {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .connections__item {
|
||||||
|
padding: $space-xx-small;
|
||||||
|
|
||||||
|
&.is-selected,
|
||||||
|
&:hover {
|
||||||
|
background-color: $background-color-primary-inverse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nobody-message {
|
||||||
|
text-align: center;
|
||||||
|
color: $text-color-soft;
|
||||||
|
}
|
||||||
|
|
||||||
|
> :nth-child(n):not(:last-child) {
|
||||||
|
margin-bottom: $space-small;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -14,7 +14,7 @@ export default (i18n) => {
|
|||||||
${userCountsFragment}
|
${userCountsFragment}
|
||||||
${locationAndBadgesFragment(lang)}
|
${locationAndBadgesFragment(lang)}
|
||||||
|
|
||||||
query User($id: ID!) {
|
query User($id: ID!, $followedByCount: Int, $followingCount: Int) {
|
||||||
User(id: $id) {
|
User(id: $id) {
|
||||||
...user
|
...user
|
||||||
...userCounts
|
...userCounts
|
||||||
@ -26,12 +26,12 @@ export default (i18n) => {
|
|||||||
isMuted
|
isMuted
|
||||||
isBlocked
|
isBlocked
|
||||||
blocked
|
blocked
|
||||||
following(first: 7) {
|
following(first: $followingCount) {
|
||||||
...user
|
...user
|
||||||
...userCounts
|
...userCounts
|
||||||
...locationAndBadges
|
...locationAndBadges
|
||||||
}
|
}
|
||||||
followedBy(first: 7) {
|
followedBy(first: $followedByCount) {
|
||||||
...user
|
...user
|
||||||
...userCounts
|
...userCounts
|
||||||
...locationAndBadges
|
...locationAndBadges
|
||||||
|
|||||||
@ -9,6 +9,7 @@ config.stubs['client-only'] = '<span><slot /></span>'
|
|||||||
config.stubs['v-popover'] = '<span><slot /></span>'
|
config.stubs['v-popover'] = '<span><slot /></span>'
|
||||||
config.stubs['nuxt-link'] = '<span><slot /></span>'
|
config.stubs['nuxt-link'] = '<span><slot /></span>'
|
||||||
config.stubs['infinite-loading'] = '<span><slot /></span>'
|
config.stubs['infinite-loading'] = '<span><slot /></span>'
|
||||||
|
config.stubs['follow-list'] = '<span><slot /></span>'
|
||||||
|
|
||||||
describe('ProfileSlug', () => {
|
describe('ProfileSlug', () => {
|
||||||
let wrapper
|
let wrapper
|
||||||
|
|||||||
@ -89,65 +89,19 @@
|
|||||||
<ds-heading tag="h3" soft style="text-align: center; margin-bottom: 10px;">
|
<ds-heading tag="h3" soft style="text-align: center; margin-bottom: 10px;">
|
||||||
{{ $t('profile.network.title') }}
|
{{ $t('profile.network.title') }}
|
||||||
</ds-heading>
|
</ds-heading>
|
||||||
<base-card style="position: relative; height: auto;">
|
<follow-list
|
||||||
<ds-space v-if="user.following && user.following.length" margin="x-small">
|
:loading="$apollo.loading"
|
||||||
<ds-text tag="h5" color="soft">
|
:user="user"
|
||||||
{{ userName | truncate(15) }} {{ $t('profile.network.following') }}
|
type="followedBy"
|
||||||
</ds-text>
|
@fetchAllConnections="fetchAllConnections"
|
||||||
</ds-space>
|
/>
|
||||||
<template v-if="user.following && user.following.length">
|
|
||||||
<ds-space v-for="follow in uniq(user.following)" :key="follow.id" margin="x-small">
|
|
||||||
<!-- TODO: find better solution for rendering errors -->
|
|
||||||
<client-only>
|
|
||||||
<user-teaser :user="follow" />
|
|
||||||
</client-only>
|
|
||||||
</ds-space>
|
|
||||||
<ds-space v-if="user.followingCount - user.following.length" margin="small">
|
|
||||||
<ds-text size="small" color="softer">
|
|
||||||
{{
|
|
||||||
$t('profile.network.andMore', {
|
|
||||||
number: user.followingCount - user.following.length,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</ds-text>
|
|
||||||
</ds-space>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<p style="text-align: center; opacity: 0.5;">
|
|
||||||
{{ userName }} {{ $t('profile.network.followingNobody') }}
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
</base-card>
|
|
||||||
<ds-space />
|
<ds-space />
|
||||||
<base-card style="position: relative; height: auto;">
|
<follow-list
|
||||||
<ds-space v-if="user.followedBy && user.followedBy.length" margin="x-small">
|
:loading="$apollo.loading"
|
||||||
<ds-text tag="h5" color="soft">
|
:user="user"
|
||||||
{{ userName | truncate(15) }} {{ $t('profile.network.followedBy') }}
|
type="following"
|
||||||
</ds-text>
|
@fetchAllConnections="fetchAllConnections"
|
||||||
</ds-space>
|
/>
|
||||||
<template v-if="user.followedBy && user.followedBy.length">
|
|
||||||
<ds-space v-for="follow in uniq(user.followedBy)" :key="follow.id" margin="x-small">
|
|
||||||
<!-- TODO: find better solution for rendering errors -->
|
|
||||||
<client-only>
|
|
||||||
<user-teaser :user="follow" />
|
|
||||||
</client-only>
|
|
||||||
</ds-space>
|
|
||||||
<ds-space v-if="user.followedByCount - user.followedBy.length" margin="small">
|
|
||||||
<ds-text size="small" color="softer">
|
|
||||||
{{
|
|
||||||
$t('profile.network.andMore', {
|
|
||||||
number: user.followedByCount - user.followedBy.length,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</ds-text>
|
|
||||||
</ds-space>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<p style="text-align: center; opacity: 0.5;">
|
|
||||||
{{ userName }} {{ $t('profile.network.followedByNobody') }}
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
</base-card>
|
|
||||||
<ds-space v-if="user.socialMedia && user.socialMedia.length" margin="large">
|
<ds-space v-if="user.socialMedia && user.socialMedia.length" margin="large">
|
||||||
<base-card style="position: relative; height: auto;">
|
<base-card style="position: relative; height: auto;">
|
||||||
<ds-space margin="x-small">
|
<ds-space margin="x-small">
|
||||||
@ -271,11 +225,11 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import uniqBy from 'lodash/uniqBy'
|
import uniqBy from 'lodash/uniqBy'
|
||||||
import UserTeaser from '~/components/UserTeaser/UserTeaser'
|
|
||||||
import PostTeaser from '~/components/PostTeaser/PostTeaser.vue'
|
import PostTeaser from '~/components/PostTeaser/PostTeaser.vue'
|
||||||
import HcFollowButton from '~/components/FollowButton.vue'
|
import HcFollowButton from '~/components/FollowButton.vue'
|
||||||
import HcCountTo from '~/components/CountTo.vue'
|
import HcCountTo from '~/components/CountTo.vue'
|
||||||
import HcBadges from '~/components/Badges.vue'
|
import HcBadges from '~/components/Badges.vue'
|
||||||
|
import FollowList from '~/components/features/FollowList/FollowList'
|
||||||
import HcEmpty from '~/components/Empty/Empty'
|
import HcEmpty from '~/components/Empty/Empty'
|
||||||
import ContentMenu from '~/components/ContentMenu/ContentMenu'
|
import ContentMenu from '~/components/ContentMenu/ContentMenu'
|
||||||
import HcUpload from '~/components/Upload'
|
import HcUpload from '~/components/Upload'
|
||||||
@ -299,7 +253,6 @@ const tabToFilterMapping = ({ tab, id }) => {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
UserTeaser,
|
|
||||||
PostTeaser,
|
PostTeaser,
|
||||||
HcFollowButton,
|
HcFollowButton,
|
||||||
HcCountTo,
|
HcCountTo,
|
||||||
@ -310,6 +263,7 @@ export default {
|
|||||||
HcUpload,
|
HcUpload,
|
||||||
MasonryGrid,
|
MasonryGrid,
|
||||||
MasonryGridItem,
|
MasonryGridItem,
|
||||||
|
FollowList,
|
||||||
},
|
},
|
||||||
transition: {
|
transition: {
|
||||||
name: 'slide-up',
|
name: 'slide-up',
|
||||||
@ -326,6 +280,8 @@ export default {
|
|||||||
tabActive: 'post',
|
tabActive: 'post',
|
||||||
filter,
|
filter,
|
||||||
followedByCountStartValue: 0,
|
followedByCountStartValue: 0,
|
||||||
|
followedByCount: 7,
|
||||||
|
followingCount: 7,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -355,13 +311,6 @@ export default {
|
|||||||
return slug && `@${slug}`
|
return slug && `@${slug}`
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
User(val) {
|
|
||||||
if (!val || !val.length) {
|
|
||||||
throw new Error('User not found!')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
removePostFromList(deletedPost) {
|
removePostFromList(deletedPost) {
|
||||||
this.posts = this.posts.filter((post) => {
|
this.posts = this.posts.filter((post) => {
|
||||||
@ -483,6 +432,10 @@ export default {
|
|||||||
this.user.followedByCurrentUser = followedByCurrentUser
|
this.user.followedByCurrentUser = followedByCurrentUser
|
||||||
this.user.followedBy = followedBy
|
this.user.followedBy = followedBy
|
||||||
},
|
},
|
||||||
|
fetchAllConnections(type) {
|
||||||
|
if (type === 'following') this.followingCount = Infinity
|
||||||
|
if (type === 'followedBy') this.followedByCount = Infinity
|
||||||
|
},
|
||||||
},
|
},
|
||||||
apollo: {
|
apollo: {
|
||||||
profilePagePosts: {
|
profilePagePosts: {
|
||||||
@ -507,7 +460,11 @@ export default {
|
|||||||
return UserQuery(this.$i18n)
|
return UserQuery(this.$i18n)
|
||||||
},
|
},
|
||||||
variables() {
|
variables() {
|
||||||
return { id: this.$route.params.id }
|
return {
|
||||||
|
id: this.$route.params.id,
|
||||||
|
followedByCount: this.followedByCount,
|
||||||
|
followingCount: this.followingCount,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
fetchPolicy: 'cache-and-network',
|
fetchPolicy: 'cache-and-network',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -7,7 +7,10 @@ export default ({ app }) => {
|
|||||||
// Focus the element
|
// Focus the element
|
||||||
Vue.nextTick(() => {
|
Vue.nextTick(() => {
|
||||||
if (binding.value !== false) {
|
if (binding.value !== false) {
|
||||||
el.focus()
|
const target = el instanceof HTMLInputElement ? el : el.querySelector('input')
|
||||||
|
if (target) {
|
||||||
|
target.focus()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
|
import faker from 'faker'
|
||||||
import vuexI18n from 'vuex-i18n/dist/vuex-i18n.umd.js'
|
import vuexI18n from 'vuex-i18n/dist/vuex-i18n.umd.js'
|
||||||
import Styleguide from '@human-connection/styleguide'
|
import Styleguide from '@human-connection/styleguide'
|
||||||
import Filters from '~/plugins/vue-filters'
|
import Filters from '~/plugins/vue-filters'
|
||||||
|
import Directives from '~/plugins/vue-directives'
|
||||||
import IziToast from '~/plugins/izi-toast'
|
import IziToast from '~/plugins/izi-toast'
|
||||||
import layout from './layout.vue'
|
import layout from './layout.vue'
|
||||||
import locales from '~/locales/index.js'
|
import locales from '~/locales/index.js'
|
||||||
@ -15,6 +17,7 @@ const helpers = {
|
|||||||
Vue.use(Styleguide)
|
Vue.use(Styleguide)
|
||||||
Vue.use(Filters)
|
Vue.use(Filters)
|
||||||
Vue.use(IziToast)
|
Vue.use(IziToast)
|
||||||
|
Vue.use(Directives)
|
||||||
|
|
||||||
Vue.use(vuexI18n.plugin, helpers.store)
|
Vue.use(vuexI18n.plugin, helpers.store)
|
||||||
locales.forEach(({ code }) => {
|
locales.forEach(({ code }) => {
|
||||||
@ -38,7 +41,7 @@ const helpers = {
|
|||||||
isAdmin() {
|
isAdmin() {
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
user(state) {
|
user() {
|
||||||
return { id: '1', name: 'admin', slug: 'admin' }
|
return { id: '1', name: 'admin', slug: 'admin' }
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -57,6 +60,16 @@ const helpers = {
|
|||||||
</layout>`,
|
</layout>`,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
fakeUser(n) {
|
||||||
|
return new Array(n || 1).fill(0).map(() => {
|
||||||
|
const name = faker.name.findName()
|
||||||
|
return {
|
||||||
|
id: faker.random.uuid(),
|
||||||
|
name,
|
||||||
|
slug: faker.helpers.slugify(name),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default helpers
|
export default helpers
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<ds-container class="container">
|
<div class="container">
|
||||||
<slot />
|
<slot />
|
||||||
</ds-container>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@ -10,5 +10,7 @@
|
|||||||
|
|
||||||
.container {
|
.container {
|
||||||
padding: 5rem;
|
padding: 5rem;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import Styleguide from '@human-connection/styleguide'
|
|||||||
import BaseComponents from '~/plugins/base-components'
|
import BaseComponents from '~/plugins/base-components'
|
||||||
import Filters from '~/plugins/vue-filters'
|
import Filters from '~/plugins/vue-filters'
|
||||||
import InfiniteLoading from '~/plugins/vue-infinite-loading'
|
import InfiniteLoading from '~/plugins/vue-infinite-loading'
|
||||||
|
import Directives from '~/plugins/vue-directives'
|
||||||
|
|
||||||
global.localVue = createLocalVue()
|
global.localVue = createLocalVue()
|
||||||
|
|
||||||
@ -13,4 +14,5 @@ global.localVue.use(VTooltip)
|
|||||||
global.localVue.use(Styleguide)
|
global.localVue.use(Styleguide)
|
||||||
global.localVue.use(BaseComponents)
|
global.localVue.use(BaseComponents)
|
||||||
global.localVue.use(Filters)
|
global.localVue.use(Filters)
|
||||||
|
global.localVue.use(Directives)
|
||||||
global.localVue.use(InfiniteLoading)
|
global.localVue.use(InfiniteLoading)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user