mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2026-02-06 09:55:50 +00:00
feat(backend): group pins (#9034)
Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com>
This commit is contained in:
parent
524c4caf5e
commit
6fc3c03860
@ -48,3 +48,4 @@ IMAGOR_SECRET=mysecret
|
||||
|
||||
CATEGORIES_ACTIVE=false
|
||||
MAX_PINNED_POSTS=1
|
||||
MAX_GROUP_PINNED_POSTS=1
|
||||
|
||||
@ -40,3 +40,4 @@ IMAGOR_SECRET=mysecret
|
||||
|
||||
CATEGORIES_ACTIVE=false
|
||||
MAX_PINNED_POSTS=1
|
||||
MAX_GROUP_PINNED_POSTS=1
|
||||
|
||||
@ -18,7 +18,7 @@ module.exports = {
|
||||
],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
lines: 93,
|
||||
lines: 92,
|
||||
},
|
||||
},
|
||||
testMatch: ['**/src/**/?(*.)+(spec|test).ts?(x)'],
|
||||
|
||||
@ -138,6 +138,9 @@ const options = {
|
||||
MAX_PINNED_POSTS: Number.isNaN(Number(process.env.MAX_PINNED_POSTS))
|
||||
? 1
|
||||
: Number(process.env.MAX_PINNED_POSTS),
|
||||
MAX_GROUP_PINNED_POSTS: Number.isNaN(Number(process.env.MAX_GROUP_PINNED_POSTS))
|
||||
? 1
|
||||
: Number(process.env.MAX_GROUP_PINNED_POSTS),
|
||||
}
|
||||
|
||||
const language = {
|
||||
|
||||
@ -58,6 +58,7 @@ export default {
|
||||
},
|
||||
},
|
||||
pinned: { type: 'boolean', default: null, valid: [null, true] },
|
||||
groupPinned: { type: 'boolean', default: null, valid: [null, true] },
|
||||
postType: { type: 'string', default: 'Article', valid: ['Article', 'Event'] },
|
||||
observes: {
|
||||
type: 'relationship',
|
||||
|
||||
25
backend/src/graphql/queries/pinGroupPost.ts
Normal file
25
backend/src/graphql/queries/pinGroupPost.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const pinGroupPost = gql`
|
||||
mutation ($id: ID!) {
|
||||
pinGroupPost(id: $id) {
|
||||
id
|
||||
title
|
||||
content
|
||||
author {
|
||||
name
|
||||
slug
|
||||
}
|
||||
pinnedBy {
|
||||
id
|
||||
name
|
||||
role
|
||||
}
|
||||
createdAt
|
||||
updatedAt
|
||||
pinnedAt
|
||||
pinned
|
||||
groupPinned
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -11,6 +11,7 @@ export const profilePagePosts = gql`
|
||||
id
|
||||
title
|
||||
content
|
||||
groupPinned
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
25
backend/src/graphql/queries/unpinGroupPost.ts
Normal file
25
backend/src/graphql/queries/unpinGroupPost.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const unpinGroupPost = gql`
|
||||
mutation ($id: ID!) {
|
||||
unpinGroupPost(id: $id) {
|
||||
id
|
||||
title
|
||||
content
|
||||
author {
|
||||
name
|
||||
slug
|
||||
}
|
||||
pinnedBy {
|
||||
id
|
||||
name
|
||||
role
|
||||
}
|
||||
createdAt
|
||||
updatedAt
|
||||
pinned
|
||||
pinnedAt
|
||||
groupPinned
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -471,6 +471,18 @@ export default {
|
||||
})
|
||||
).records.map((r) => r.get('inviteCodes'))
|
||||
},
|
||||
currentlyPinnedPostsCount: async (parent, _args, context: Context, _resolveInfo) => {
|
||||
if (!parent.id) {
|
||||
throw new Error('Can not identify selected Group!')
|
||||
}
|
||||
const result = await context.database.query({
|
||||
query: `
|
||||
MATCH (:User)-[pinned:GROUP_PINNED]->(pinnedPosts:Post)-[:IN]->(:Group {id: $group.id})
|
||||
RETURN toString(count(pinnedPosts)) as count`,
|
||||
variables: { group: parent },
|
||||
})
|
||||
return result.records[0].get('count')
|
||||
},
|
||||
...Resolver('Group', {
|
||||
undefinedToNull: ['deleted', 'disabled', 'locationName', 'about'],
|
||||
hasMany: {
|
||||
|
||||
368
backend/src/graphql/resolvers/posts.group.pin.spec.ts
Normal file
368
backend/src/graphql/resolvers/posts.group.pin.spec.ts
Normal file
@ -0,0 +1,368 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import Factory, { cleanDatabase } from '@db/factories'
|
||||
import { ChangeGroupMemberRole } from '@graphql/queries/ChangeGroupMemberRole'
|
||||
import { CreateGroup } from '@graphql/queries/CreateGroup'
|
||||
import { CreatePost } from '@graphql/queries/CreatePost'
|
||||
import { pinGroupPost } from '@graphql/queries/pinGroupPost'
|
||||
import { profilePagePosts } from '@graphql/queries/profilePagePosts'
|
||||
import { unpinGroupPost } from '@graphql/queries/unpinGroupPost'
|
||||
import type { ApolloTestSetup } from '@root/test/helpers'
|
||||
import { createApolloTestSetup } from '@root/test/helpers'
|
||||
import type { Context } from '@src/context'
|
||||
|
||||
const defaultConfig = {
|
||||
CATEGORIES_ACTIVE: false,
|
||||
}
|
||||
let config: Partial<Context['config']>
|
||||
|
||||
let anyUser
|
||||
let allGroupsUser
|
||||
let publicUser
|
||||
let publicAdminUser
|
||||
let authenticatedUser: Context['user']
|
||||
const context = () => ({ authenticatedUser, config })
|
||||
let mutate: ApolloTestSetup['mutate']
|
||||
let query: ApolloTestSetup['query']
|
||||
let database: ApolloTestSetup['database']
|
||||
let server: ApolloTestSetup['server']
|
||||
|
||||
beforeAll(async () => {
|
||||
await cleanDatabase()
|
||||
const apolloSetup = createApolloTestSetup({ context })
|
||||
mutate = apolloSetup.mutate
|
||||
query = apolloSetup.query
|
||||
database = apolloSetup.database
|
||||
server = apolloSetup.server
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
void server.stop()
|
||||
void database.driver.close()
|
||||
database.neode.close()
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
config = { ...defaultConfig }
|
||||
authenticatedUser = null
|
||||
|
||||
anyUser = await Factory.build('user', {
|
||||
id: 'any-user',
|
||||
name: 'Any User',
|
||||
about: 'I am just an ordinary user and do not belong to any group.',
|
||||
})
|
||||
|
||||
allGroupsUser = await Factory.build('user', {
|
||||
id: 'all-groups-user',
|
||||
name: 'All Groups User',
|
||||
about: 'I am a member of all groups.',
|
||||
})
|
||||
publicUser = await Factory.build('user', {
|
||||
id: 'public-user',
|
||||
name: 'Public User',
|
||||
about: 'I am the owner of the public group.',
|
||||
})
|
||||
publicAdminUser = await Factory.build('user', {
|
||||
id: 'public-admin-user',
|
||||
name: 'Public Admin User',
|
||||
about: 'I am the admin of the public group.',
|
||||
})
|
||||
|
||||
authenticatedUser = await publicUser.toJson()
|
||||
await mutate({
|
||||
mutation: CreateGroup,
|
||||
variables: {
|
||||
id: 'public-group',
|
||||
name: 'The Public Group',
|
||||
about: 'The public group!',
|
||||
description: 'Anyone can see the posts of this group.',
|
||||
groupType: 'public',
|
||||
actionRadius: 'regional',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: ChangeGroupMemberRole,
|
||||
variables: {
|
||||
groupId: 'public-group',
|
||||
userId: 'all-groups-user',
|
||||
roleInGroup: 'usual',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: ChangeGroupMemberRole,
|
||||
variables: {
|
||||
groupId: 'public-group',
|
||||
userId: 'public-admin-user',
|
||||
roleInGroup: 'admin',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: ChangeGroupMemberRole,
|
||||
variables: {
|
||||
groupId: 'closed-group',
|
||||
userId: 'all-groups-user',
|
||||
roleInGroup: 'usual',
|
||||
},
|
||||
})
|
||||
authenticatedUser = await anyUser.toJson()
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
variables: {
|
||||
id: 'post-without-group',
|
||||
title: 'A post without a group',
|
||||
content: 'I am a user who does not belong to a group yet.',
|
||||
},
|
||||
})
|
||||
authenticatedUser = await publicUser.toJson()
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
variables: {
|
||||
id: 'post-1-to-public-group',
|
||||
title: 'Post 1 to a public group',
|
||||
content: 'I am posting into a public group as a member of the group',
|
||||
groupId: 'public-group',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
variables: {
|
||||
id: 'post-2-to-public-group',
|
||||
title: 'Post 1 to a public group',
|
||||
content: 'I am posting into a public group as a member of the group',
|
||||
groupId: 'public-group',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
variables: {
|
||||
id: 'post-3-to-public-group',
|
||||
title: 'Post 1 to a public group',
|
||||
content: 'I am posting into a public group as a member of the group',
|
||||
groupId: 'public-group',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await cleanDatabase()
|
||||
})
|
||||
|
||||
describe('pin groupPosts', () => {
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
authenticatedUser = null
|
||||
await expect(
|
||||
mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: { pinGroupPost: null },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('ordinary users', () => {
|
||||
it('throws authorization error', async () => {
|
||||
authenticatedUser = await anyUser.toJson()
|
||||
await expect(
|
||||
mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: { pinGroupPost: null },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('group usual', () => {
|
||||
it('throws authorization error', async () => {
|
||||
authenticatedUser = await allGroupsUser.toJson()
|
||||
await expect(
|
||||
mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: { pinGroupPost: null },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('group admin', () => {
|
||||
it('resolves without error', async () => {
|
||||
authenticatedUser = await publicAdminUser.toJson()
|
||||
await expect(
|
||||
mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
data: { pinGroupPost: { id: 'post-1-to-public-group', groupPinned: true } },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('group owner', () => {
|
||||
it('resolves without error', async () => {
|
||||
authenticatedUser = await publicUser.toJson()
|
||||
await expect(
|
||||
mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
data: { pinGroupPost: { id: 'post-1-to-public-group', groupPinned: true } },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('MAX_GROUP_PINNED_POSTS is 1', () => {
|
||||
beforeEach(async () => {
|
||||
config = { ...defaultConfig, MAX_GROUP_PINNED_POSTS: 1 }
|
||||
authenticatedUser = await publicUser.toJson()
|
||||
})
|
||||
it('returns post-1-to-public-group as first, pinned post', async () => {
|
||||
await mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } })
|
||||
await expect(
|
||||
query({
|
||||
query: profilePagePosts,
|
||||
variables: {
|
||||
filter: { group: { id: 'public-group' } },
|
||||
orderBy: ['groupPinned_asc', 'sortDate_desc'],
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
data: {
|
||||
profilePagePosts: [
|
||||
expect.objectContaining({ id: 'post-1-to-public-group', groupPinned: true }),
|
||||
expect.objectContaining({ id: 'post-3-to-public-group', groupPinned: null }),
|
||||
expect.objectContaining({ id: 'post-2-to-public-group', groupPinned: null }),
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('no error thrown when pinned post was pinned again', async () => {
|
||||
await mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } })
|
||||
await expect(
|
||||
mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
data: { pinGroupPost: { id: 'post-1-to-public-group', groupPinned: true } },
|
||||
})
|
||||
})
|
||||
|
||||
it('returns post-2-to-public-group as first, pinned post', async () => {
|
||||
authenticatedUser = await publicUser.toJson()
|
||||
await mutate({ mutation: pinGroupPost, variables: { id: 'post-2-to-public-group' } })
|
||||
await expect(
|
||||
query({
|
||||
query: profilePagePosts,
|
||||
variables: {
|
||||
filter: { group: { id: 'public-group' } },
|
||||
orderBy: ['groupPinned_asc', 'sortDate_desc'],
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
data: {
|
||||
profilePagePosts: [
|
||||
expect.objectContaining({ id: 'post-2-to-public-group', groupPinned: true }),
|
||||
expect.objectContaining({ id: 'post-3-to-public-group', groupPinned: null }),
|
||||
expect.objectContaining({ id: 'post-1-to-public-group', groupPinned: null }),
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('returns post-3-to-public-group as first, pinned post, when multiple are pinned', async () => {
|
||||
authenticatedUser = await publicUser.toJson()
|
||||
await mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } })
|
||||
await mutate({ mutation: pinGroupPost, variables: { id: 'post-2-to-public-group' } })
|
||||
await mutate({ mutation: pinGroupPost, variables: { id: 'post-3-to-public-group' } })
|
||||
await expect(
|
||||
query({
|
||||
query: profilePagePosts,
|
||||
variables: {
|
||||
filter: { group: { id: 'public-group' } },
|
||||
orderBy: ['groupPinned_asc', 'sortDate_desc'],
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
data: {
|
||||
profilePagePosts: [
|
||||
expect.objectContaining({ id: 'post-3-to-public-group', groupPinned: true }),
|
||||
expect.objectContaining({ id: 'post-2-to-public-group', groupPinned: null }),
|
||||
expect.objectContaining({ id: 'post-1-to-public-group', groupPinned: null }),
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('MAX_GROUP_PINNED_POSTS is 2', () => {
|
||||
beforeEach(async () => {
|
||||
config = { ...defaultConfig, MAX_GROUP_PINNED_POSTS: 2 }
|
||||
authenticatedUser = await publicUser.toJson()
|
||||
})
|
||||
it('returns post-1-to-public-group as first, post-2-to-public-group as second pinned post', async () => {
|
||||
await mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } })
|
||||
await mutate({ mutation: pinGroupPost, variables: { id: 'post-2-to-public-group' } })
|
||||
await expect(
|
||||
query({
|
||||
query: profilePagePosts,
|
||||
variables: {
|
||||
filter: { group: { id: 'public-group' } },
|
||||
orderBy: ['groupPinned_asc', 'sortDate_desc'],
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
data: {
|
||||
profilePagePosts: [
|
||||
expect.objectContaining({ id: 'post-2-to-public-group', groupPinned: true }),
|
||||
expect.objectContaining({ id: 'post-1-to-public-group', groupPinned: true }),
|
||||
expect.objectContaining({ id: 'post-3-to-public-group', groupPinned: null }),
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('throws an error when three posts are pinned', async () => {
|
||||
await mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } })
|
||||
await mutate({ mutation: pinGroupPost, variables: { id: 'post-2-to-public-group' } })
|
||||
await expect(
|
||||
mutate({ mutation: pinGroupPost, variables: { id: 'post-3-to-public-group' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Reached maxed pinned posts already. Unpin a post first.' }],
|
||||
data: {
|
||||
pinGroupPost: null,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('throws no error when first unpinned before a third post is pinned', async () => {
|
||||
await mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } })
|
||||
await mutate({ mutation: pinGroupPost, variables: { id: 'post-2-to-public-group' } })
|
||||
await mutate({ mutation: unpinGroupPost, variables: { id: 'post-1-to-public-group' } })
|
||||
await expect(
|
||||
mutate({ mutation: pinGroupPost, variables: { id: 'post-3-to-public-group' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
})
|
||||
await expect(
|
||||
query({
|
||||
query: profilePagePosts,
|
||||
variables: {
|
||||
filter: { group: { id: 'public-group' } },
|
||||
orderBy: ['groupPinned_asc', 'sortDate_desc'],
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
data: {
|
||||
profilePagePosts: [
|
||||
expect.objectContaining({ id: 'post-3-to-public-group', groupPinned: true }),
|
||||
expect.objectContaining({ id: 'post-2-to-public-group', groupPinned: true }),
|
||||
expect.objectContaining({ id: 'post-1-to-public-group', groupPinned: null }),
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -29,6 +29,20 @@ const maintainPinnedPosts = (params) => {
|
||||
return params
|
||||
}
|
||||
|
||||
const maintainGroupPinnedPosts = (params) => {
|
||||
// only show GroupPinnedPosts when Groups is selected
|
||||
if (!params.filter?.group) {
|
||||
return params
|
||||
}
|
||||
const pinnedPostFilter = { groupPinned: true, group: params.filter.group }
|
||||
if (isEmpty(params.filter)) {
|
||||
params.filter = { OR: [pinnedPostFilter, {}] }
|
||||
} else {
|
||||
params.filter = { OR: [pinnedPostFilter, { ...params.filter }] }
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
const filterEventDates = (params) => {
|
||||
if (params.filter?.eventStart_gte) {
|
||||
const date = params.filter.eventStart_gte
|
||||
@ -52,6 +66,7 @@ export default {
|
||||
params = await filterPostsOfMyGroups(params, context)
|
||||
params = await filterInvisiblePosts(params, context)
|
||||
params = await filterForMutedUsers(params, context)
|
||||
params = await maintainGroupPinnedPosts(params)
|
||||
return neo4jgraphql(object, params, context, resolveInfo)
|
||||
},
|
||||
PostsEmotionsCountByEmotion: async (_object, params, context, _resolveInfo) => {
|
||||
@ -453,6 +468,68 @@ export default {
|
||||
}
|
||||
return unpinnedPost
|
||||
},
|
||||
pinGroupPost: async (_parent, params, context: Context, _resolveInfo) => {
|
||||
if (!context.user) {
|
||||
throw new Error('Missing authenticated user.')
|
||||
}
|
||||
const { config } = context
|
||||
|
||||
if (config.MAX_GROUP_PINNED_POSTS === 0) {
|
||||
throw new Error('Pinned posts are not allowed!')
|
||||
}
|
||||
|
||||
// If MAX_GROUP_PINNED_POSTS === 1 -> Delete old pin
|
||||
if (config.MAX_GROUP_PINNED_POSTS === 1) {
|
||||
await context.database.write({
|
||||
query: `
|
||||
MATCH (post:Post {id: $params.id})-[:IN]->(group:Group)
|
||||
MATCH (:User)-[pinned:GROUP_PINNED]->(oldPinnedPost:Post)-[:IN]->(:Group {id: group.id})
|
||||
REMOVE oldPinnedPost.groupPinned
|
||||
DELETE pinned`,
|
||||
variables: { user: context.user, params },
|
||||
})
|
||||
// If MAX_GROUP_PINNED_POSTS !== 1 -> Check if max is reached
|
||||
} else {
|
||||
const result = await context.database.query({
|
||||
query: `
|
||||
MATCH (post:Post {id: $params.id})-[:IN]->(group:Group)
|
||||
MATCH (:User)-[pinned:GROUP_PINNED]->(pinnedPosts:Post)-[:IN]->(:Group {id: group.id})
|
||||
RETURN toString(count(pinnedPosts)) as count`,
|
||||
variables: { user: context.user, params },
|
||||
})
|
||||
if (result.records[0].get('count') >= config.MAX_GROUP_PINNED_POSTS) {
|
||||
throw new Error('Reached maxed pinned posts already. Unpin a post first.')
|
||||
}
|
||||
}
|
||||
|
||||
// Set new pin
|
||||
const result = await context.database.write({
|
||||
query: `
|
||||
MATCH (user:User {id: $user.id})
|
||||
MATCH (post:Post {id: $params.id})-[:IN]->(group:Group)
|
||||
MERGE (user)-[pinned:GROUP_PINNED {createdAt: toString(datetime())}]->(post)
|
||||
SET post.groupPinned = true
|
||||
RETURN post {.*, pinnedAt: pinned.createdAt}`,
|
||||
variables: { user: context.user, params },
|
||||
})
|
||||
|
||||
// Return post
|
||||
return result.records[0].get('post')
|
||||
},
|
||||
unpinGroupPost: async (_parent, params, context, _resolveInfo) => {
|
||||
const result = await context.database.write({
|
||||
query: `
|
||||
MATCH (post:Post {id: $postId})
|
||||
OPTIONAL MATCH (:User)-[pinned:GROUP_PINNED]->(post)
|
||||
DELETE pinned
|
||||
REMOVE post.groupPinned
|
||||
RETURN post {.*}`,
|
||||
variables: { postId: params.id },
|
||||
})
|
||||
|
||||
// Return post
|
||||
return result.records[0].get('post')
|
||||
},
|
||||
markTeaserAsViewed: async (_parent, params, context, _resolveInfo) => {
|
||||
const session = context.driver.session()
|
||||
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
||||
@ -550,6 +627,7 @@ export default {
|
||||
'language',
|
||||
'pinnedAt',
|
||||
'pinned',
|
||||
'groupPinned',
|
||||
'eventVenue',
|
||||
'eventLocation',
|
||||
'eventLocationName',
|
||||
@ -589,6 +667,21 @@ export default {
|
||||
'MATCH (this)<-[obs:OBSERVES]-(related:User {id: $cypherParams.currentUserId}) WHERE obs.active = true RETURN COUNT(related) >= 1',
|
||||
},
|
||||
}),
|
||||
// As long as we rely on the filter capabilities of the neo4jgraphql library,
|
||||
// we cannot filter on a relation or their properties.
|
||||
// Hence we need to save the value to the group node in the database.
|
||||
/* groupPinned: async (parent, _params, context, _resolveInfo) => {
|
||||
return (
|
||||
(
|
||||
await context.database.query({
|
||||
query: `
|
||||
MATCH (:User)-[pinned:GROUP_PINNED]->(:Post {id: $parent.id})
|
||||
RETURN pinned`,
|
||||
variables: { parent },
|
||||
})
|
||||
).records.length === 1
|
||||
)
|
||||
}, */
|
||||
relatedContributions: async (parent, _params, context, _resolveInfo) => {
|
||||
if (typeof parent.relatedContributions !== 'undefined') return parent.relatedContributions
|
||||
const { id } = parent
|
||||
|
||||
@ -51,6 +51,8 @@ type Group {
|
||||
|
||||
"inviteCodes to this group the current user has generated"
|
||||
inviteCodes: [InviteCode]! @neo4j_ignore
|
||||
|
||||
currentlyPinnedPostsCount: Int! @neo4j_ignore
|
||||
}
|
||||
|
||||
input _GroupFilter {
|
||||
|
||||
@ -53,6 +53,7 @@ input _PostFilter {
|
||||
language_in: [String!]
|
||||
language_not_in: [String!]
|
||||
pinned: Boolean # required for `maintainPinnedPost`
|
||||
groupPinned: Boolean # required for `maintainGroupPinnedPost`
|
||||
tags: _TagFilter
|
||||
tags_not: _TagFilter
|
||||
tags_in: [_TagFilter!]
|
||||
@ -115,6 +116,8 @@ enum _PostOrdering {
|
||||
pinned_desc
|
||||
eventStart_asc
|
||||
eventStart_desc
|
||||
groupPinned_asc
|
||||
groupPinned_desc
|
||||
}
|
||||
|
||||
type Post {
|
||||
@ -131,6 +134,7 @@ type Post {
|
||||
deleted: Boolean
|
||||
disabled: Boolean
|
||||
pinned: Boolean
|
||||
groupPinned: Boolean
|
||||
createdAt: String
|
||||
updatedAt: String
|
||||
sortDate: String
|
||||
@ -246,8 +250,12 @@ type Mutation {
|
||||
DeletePost(id: ID!): Post
|
||||
AddPostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED
|
||||
RemovePostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED
|
||||
|
||||
pinPost(id: ID!): Post
|
||||
unpinPost(id: ID!): Post
|
||||
pinGroupPost(id: ID!): Post
|
||||
unpinGroupPost(id: ID!): Post
|
||||
|
||||
markTeaserAsViewed(id: ID!): Post
|
||||
pushPost(id: ID!): Post!
|
||||
unpushPost(id: ID!): Post!
|
||||
|
||||
@ -397,6 +397,26 @@ const isAllowedToGenerateGroupInviteCode = rule({
|
||||
).records[0].get('count')
|
||||
})
|
||||
|
||||
const isAllowedToPinGroupPost = rule({
|
||||
cache: 'no_cache',
|
||||
})(async (_parent, args, context: Context) => {
|
||||
if (!context.user) return false
|
||||
|
||||
return (
|
||||
(
|
||||
await context.database.query({
|
||||
query: `
|
||||
MATCH (post:Post{id: $args.id})-[:IN]->(group:Group)
|
||||
MATCH (user:User{id: $user.id})-[membership:MEMBER_OF]->(group)
|
||||
WHERE (membership.role IN ['admin', 'owner'])
|
||||
RETURN toString(count(group)) as count
|
||||
`,
|
||||
variables: { user: context.user, args },
|
||||
})
|
||||
).records[0].get('count') === '1'
|
||||
)
|
||||
})
|
||||
|
||||
// Permissions
|
||||
export default shield(
|
||||
{
|
||||
@ -485,6 +505,8 @@ export default shield(
|
||||
VerifyEmailAddress: isAuthenticated,
|
||||
pinPost: isAdmin,
|
||||
unpinPost: isAdmin,
|
||||
pinGroupPost: isAllowedToPinGroupPost,
|
||||
unpinGroupPost: isAllowedToPinGroupPost,
|
||||
pushPost: isAdmin,
|
||||
unpushPost: isAdmin,
|
||||
UpdateDonations: isAdmin,
|
||||
|
||||
@ -57,6 +57,7 @@ export const TEST_CONFIG = {
|
||||
INVITE_CODES_GROUP_PER_USER: 7,
|
||||
CATEGORIES_ACTIVE: false,
|
||||
MAX_PINNED_POSTS: 1,
|
||||
MAX_GROUP_PINNED_POSTS: 1,
|
||||
|
||||
LANGUAGE_DEFAULT: 'en',
|
||||
LOG_LEVEL: 'DEBUG',
|
||||
|
||||
@ -11,4 +11,5 @@ NETWORK_NAME="Ocelot.social"
|
||||
|
||||
ASK_FOR_REAL_NAME=false
|
||||
|
||||
REQUIRE_LOCATION=false
|
||||
REQUIRE_LOCATION=false
|
||||
MAX_GROUP_PINNED_POSTS=1
|
||||
823
webapp/components/ContentMenu/ContentMenu.Group.spec.js
Normal file
823
webapp/components/ContentMenu/ContentMenu.Group.spec.js
Normal file
@ -0,0 +1,823 @@
|
||||
import { mount, createLocalVue } from '@vue/test-utils'
|
||||
import Vuex from 'vuex'
|
||||
import VTooltip from 'v-tooltip'
|
||||
import Styleguide from '@@/'
|
||||
import ContentMenu from './ContentMenu.vue'
|
||||
|
||||
const localVue = createLocalVue()
|
||||
|
||||
localVue.use(Styleguide)
|
||||
localVue.use(VTooltip)
|
||||
localVue.use(Vuex)
|
||||
|
||||
let mocks
|
||||
|
||||
describe('ContentMenu.vue - Group', () => {
|
||||
beforeEach(() => {
|
||||
mocks = {
|
||||
$t: jest.fn((str) => str),
|
||||
$i18n: {
|
||||
locale: () => 'en',
|
||||
},
|
||||
$router: {
|
||||
push: jest.fn(),
|
||||
},
|
||||
$env: {
|
||||
MAX_GROUP_PINNED_POSTS: 0,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const stubs = {
|
||||
'router-link': {
|
||||
template: '<span><slot /></span>',
|
||||
},
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
'modal/SET_OPEN': jest.fn(),
|
||||
}
|
||||
const getters = {
|
||||
'auth/isModerator': () => false,
|
||||
'auth/isAdmin': () => false,
|
||||
'pinnedPosts/maxPinnedPosts': () => 1,
|
||||
'pinnedPosts/currentlyPinnedPosts': () => 1,
|
||||
}
|
||||
const actions = {
|
||||
'pinnedPosts/fetch': jest.fn(),
|
||||
}
|
||||
|
||||
const openContentMenu = async (values = {}) => {
|
||||
const store = new Vuex.Store({ mutations, getters, actions })
|
||||
const wrapper = mount(ContentMenu, {
|
||||
propsData: {
|
||||
...values,
|
||||
},
|
||||
mocks,
|
||||
store,
|
||||
localVue,
|
||||
stubs,
|
||||
})
|
||||
const menuToggle = wrapper.find('[data-test="content-menu-button"]')
|
||||
await menuToggle.trigger('click')
|
||||
return wrapper
|
||||
}
|
||||
|
||||
describe('as group owner', () => {
|
||||
const myRole = 'owner'
|
||||
|
||||
describe('when maxGroupPinnedPosts = 0', () => {
|
||||
beforeEach(() => {
|
||||
mocks.$env = {
|
||||
MAX_GROUP_PINNED_POSTS: 0,
|
||||
}
|
||||
})
|
||||
|
||||
it('can not pin unpinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(
|
||||
wrapper.findAll('.ds-menu-item').filter((item) => item.text() === 'post.menu.groupPin'),
|
||||
).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('can unpin pinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.groupUnpin')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('unpinGroupPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
},
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('when maxPinnedPosts = 1', () => {
|
||||
beforeEach(() => {
|
||||
mocks.$env = {
|
||||
MAX_GROUP_PINNED_POSTS: 1,
|
||||
}
|
||||
})
|
||||
|
||||
describe('when currentlyPinnedPostsCount = 0', () => {
|
||||
const currentlyPinnedPostsCount = 0
|
||||
|
||||
it('pin unpinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.groupPin')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('pinGroupPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
|
||||
it('unpin pinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.groupUnpin')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('unpinGroupPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('when currentlyPinnedPostsCount = 1', () => {
|
||||
const currentlyPinnedPostsCount = 1
|
||||
|
||||
it('pin unpinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.groupPin')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('pinGroupPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
|
||||
it('unpin pinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.groupUnpin')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('unpinGroupPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when maxPinnedPosts = 2', () => {
|
||||
beforeEach(() => {
|
||||
mocks.$env = {
|
||||
MAX_GROUP_PINNED_POSTS: 2,
|
||||
}
|
||||
})
|
||||
|
||||
describe('when currentlyPinnedPostsCount = 1', () => {
|
||||
const currentlyPinnedPostsCount = 1
|
||||
|
||||
it('pin unpinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.groupPin')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('pinGroupPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
|
||||
it('unpin pinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.groupUnpin')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('unpinGroupPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('when currentlyPinnedPostsCount = 2', () => {
|
||||
const currentlyPinnedPostsCount = 2
|
||||
|
||||
it('pin unpinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(
|
||||
wrapper.findAll('.ds-menu-item').filter((item) => item.text() === 'post.menu.groupPin')
|
||||
.length,
|
||||
).toEqual(0)
|
||||
})
|
||||
|
||||
it('unpin pinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.groupUnpin')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('unpinGroupPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('as group admin', () => {
|
||||
const myRole = 'admin'
|
||||
|
||||
describe('when maxGroupPinnedPosts = 0', () => {
|
||||
beforeEach(() => {
|
||||
mocks.$env = {
|
||||
MAX_GROUP_PINNED_POSTS: 0,
|
||||
}
|
||||
})
|
||||
|
||||
it('can not pin unpinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(
|
||||
wrapper.findAll('.ds-menu-item').filter((item) => item.text() === 'post.menu.groupPin'),
|
||||
).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('can unpin pinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.groupUnpin')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('unpinGroupPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
},
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('when maxPinnedPosts = 1', () => {
|
||||
beforeEach(() => {
|
||||
mocks.$env = {
|
||||
MAX_GROUP_PINNED_POSTS: 1,
|
||||
}
|
||||
})
|
||||
|
||||
describe('when currentlyPinnedPostsCount = 0', () => {
|
||||
const currentlyPinnedPostsCount = 0
|
||||
|
||||
it('pin unpinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.groupPin')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('pinGroupPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
|
||||
it('unpin pinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.groupUnpin')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('unpinGroupPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('when currentlyPinnedPostsCount = 1', () => {
|
||||
const currentlyPinnedPostsCount = 1
|
||||
|
||||
it('pin unpinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.groupPin')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('pinGroupPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
|
||||
it('unpin pinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.groupUnpin')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('unpinGroupPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when maxPinnedPosts = 2', () => {
|
||||
beforeEach(() => {
|
||||
mocks.$env = {
|
||||
MAX_GROUP_PINNED_POSTS: 2,
|
||||
}
|
||||
})
|
||||
|
||||
describe('when currentlyPinnedPostsCount = 1', () => {
|
||||
const currentlyPinnedPostsCount = 1
|
||||
|
||||
it('pin unpinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.groupPin')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('pinGroupPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
|
||||
it('unpin pinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.groupUnpin')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('unpinGroupPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('when currentlyPinnedPostsCount = 2', () => {
|
||||
const currentlyPinnedPostsCount = 2
|
||||
|
||||
it('pin unpinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(
|
||||
wrapper.findAll('.ds-menu-item').filter((item) => item.text() === 'post.menu.groupPin')
|
||||
.length,
|
||||
).toEqual(0)
|
||||
})
|
||||
|
||||
it('unpin pinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.groupUnpin')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('unpinGroupPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount,
|
||||
},
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('as group usual', () => {
|
||||
const myRole = 'usual'
|
||||
|
||||
describe('when maxGroupPinnedPosts = 0', () => {
|
||||
beforeEach(() => {
|
||||
mocks.$env = {
|
||||
MAX_GROUP_PINNED_POSTS: 0,
|
||||
}
|
||||
})
|
||||
|
||||
it('can not pin unpinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(
|
||||
wrapper.findAll('.ds-menu-item').filter((item) => item.text() === 'post.menu.groupPin'),
|
||||
).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('can not unpin pinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(
|
||||
wrapper.findAll('.ds-menu-item').filter((item) => item.text() === 'post.menu.groupUnpin'),
|
||||
).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when maxPinnedPosts = 1', () => {
|
||||
beforeEach(() => {
|
||||
mocks.$env = {
|
||||
MAX_GROUP_PINNED_POSTS: 1,
|
||||
}
|
||||
})
|
||||
|
||||
it('can not pin unpinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: false,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount: 0,
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(
|
||||
wrapper.findAll('.ds-menu-item').filter((item) => item.text() === 'post.menu.groupPin'),
|
||||
).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('can not unpin pinned post', async () => {
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
groupPinned: true,
|
||||
group: {
|
||||
myRole,
|
||||
currentlyPinnedPostsCount: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(
|
||||
wrapper.findAll('.ds-menu-item').filter((item) => item.text() === 'post.menu.groupUnpin'),
|
||||
).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -244,6 +244,23 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
this.resourceType === 'contribution' &&
|
||||
this.resource.group &&
|
||||
['admin', 'owner'].includes(this.resource.group.myRole) &&
|
||||
(this.canBeGroupPinned || this.resource.groupPinned)
|
||||
) {
|
||||
routes.push({
|
||||
label: this.resource.groupPinned
|
||||
? this.$t(`post.menu.groupUnpin`)
|
||||
: this.$t(`post.menu.groupPin`),
|
||||
callback: () => {
|
||||
this.$emit(this.resource.groupPinned ? 'unpinGroupPost' : 'pinGroupPost', this.resource)
|
||||
},
|
||||
icon: this.resource.groupPinned ? 'unlink' : 'link',
|
||||
})
|
||||
}
|
||||
|
||||
return routes
|
||||
},
|
||||
isModerator() {
|
||||
@ -258,6 +275,15 @@ export default {
|
||||
(this.maxPinnedPosts > 1 && this.currentlyPinnedPosts < this.maxPinnedPosts)
|
||||
)
|
||||
},
|
||||
canBeGroupPinned() {
|
||||
const maxGroupPinnedPosts = this.$env.MAX_GROUP_PINNED_POSTS
|
||||
return (
|
||||
maxGroupPinnedPosts === 1 ||
|
||||
(maxGroupPinnedPosts > 1 &&
|
||||
this.resource.group &&
|
||||
this.resource.group.currentlyPinnedPostsCount < maxGroupPinnedPosts)
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openItem(route, toggleMenu) {
|
||||
|
||||
@ -51,13 +51,17 @@ describe('PostTeaser', () => {
|
||||
}
|
||||
getters = {
|
||||
'auth/isModerator': () => false,
|
||||
'auth/isAdmin': () => false,
|
||||
'auth/user': () => {
|
||||
return {}
|
||||
},
|
||||
'categories/categoriesActive': () => false,
|
||||
'pinnedPosts/maxPinnedPosts': () => 0,
|
||||
'pinnedPosts/currentlyPinnedPosts': () => 0,
|
||||
}
|
||||
actions = {
|
||||
'categories/init': jest.fn(),
|
||||
'pinnedPosts/fetch': jest.fn().mockResolvedValue(),
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@ -112,6 +112,8 @@
|
||||
:is-owner="isAuthor"
|
||||
@pinPost="pinPost"
|
||||
@unpinPost="unpinPost"
|
||||
@pinGroupPost="pinGroupPost"
|
||||
@unpinGroupPost="unpinGroupPost"
|
||||
@pushPost="pushPost"
|
||||
@unpushPost="unpushPost"
|
||||
@toggleObservePost="toggleObservePost"
|
||||
@ -172,6 +174,10 @@ export default {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
showGroupPinned: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
const { image } = this.post
|
||||
@ -203,10 +209,11 @@ export default {
|
||||
)
|
||||
},
|
||||
isPinned() {
|
||||
return this.post && this.post.pinned
|
||||
return this.post && (this.post.pinned || (this.showGroupPinned && this.post.groupPinned))
|
||||
},
|
||||
ribbonText() {
|
||||
if (this.post.pinned) return this.$t('post.pinned')
|
||||
if (this.post && (this.post.pinned || (this.showGroupPinned && this.post.groupPinned)))
|
||||
return this.$t('post.pinned')
|
||||
if (this.post.postType[0] === 'Event') return this.$t('post.event')
|
||||
return this.$t('post.name')
|
||||
},
|
||||
@ -229,6 +236,12 @@ export default {
|
||||
unpinPost(post) {
|
||||
this.$emit('unpinPost', post)
|
||||
},
|
||||
pinGroupPost(post) {
|
||||
this.$emit('pinGroupPost', post)
|
||||
},
|
||||
unpinGroupPost(post) {
|
||||
this.$emit('unpinGroupPost', post)
|
||||
},
|
||||
pushPost(post) {
|
||||
this.$emit('pushPost', post)
|
||||
},
|
||||
|
||||
@ -15,11 +15,15 @@ const stubs = {
|
||||
}
|
||||
|
||||
describe('SearchResults', () => {
|
||||
let mocks, getters, actions, propsData, wrapper
|
||||
let mocks, getters, propsData, wrapper
|
||||
|
||||
const Wrapper = () => {
|
||||
const store = new Vuex.Store({
|
||||
getters,
|
||||
actions,
|
||||
actions: {
|
||||
'categories/init': jest.fn(),
|
||||
'pinnedPosts/fetch': jest.fn(),
|
||||
},
|
||||
})
|
||||
return mount(SearchResults, { mocks, localVue, propsData, store, stubs })
|
||||
}
|
||||
@ -35,9 +39,6 @@ describe('SearchResults', () => {
|
||||
'auth/isModerator': () => false,
|
||||
'categories/categoriesActive': () => false,
|
||||
}
|
||||
actions = {
|
||||
'categories/init': jest.fn(),
|
||||
}
|
||||
propsData = {
|
||||
pageSize: 12,
|
||||
search: '',
|
||||
|
||||
@ -45,9 +45,12 @@
|
||||
<post-teaser
|
||||
:post="post"
|
||||
:width="{ base: '100%', md: '100%', xl: '50%' }"
|
||||
:showGroupPinned="true"
|
||||
@removePostFromList="posts = removePostFromList(post, posts)"
|
||||
@pinPost="pinPost(post, refetchPostList)"
|
||||
@unpinPost="unpinPost(post, refetchPostList)"
|
||||
@pinGroupPost="pinGroupPost(post, refetchPostList)"
|
||||
@unpinGroupPost="unpinGroupPost(post, refetchPostList)"
|
||||
@pushPost="pushPost(post, refetchPostList)"
|
||||
@unpushPost="unpushPost(post, refetchPostList)"
|
||||
@toggleObservePost="
|
||||
|
||||
@ -38,6 +38,9 @@ const options = {
|
||||
NETWORK_NAME: process.env.NETWORK_NAME || 'Ocelot.social',
|
||||
ASK_FOR_REAL_NAME: process.env.ASK_FOR_REAL_NAME === 'true' || false,
|
||||
REQUIRE_LOCATION: process.env.REQUIRE_LOCATION === 'true' || false,
|
||||
MAX_GROUP_PINNED_POSTS: Number.isNaN(Number(process.env.MAX_GROUP_PINNED_POSTS))
|
||||
? 1
|
||||
: Number(process.env.MAX_GROUP_PINNED_POSTS),
|
||||
}
|
||||
|
||||
const language = {
|
||||
|
||||
@ -173,6 +173,40 @@ export default () => {
|
||||
}
|
||||
}
|
||||
`,
|
||||
pinGroupPost: gql`
|
||||
mutation ($id: ID!) {
|
||||
pinGroupPost(id: $id) {
|
||||
id
|
||||
title
|
||||
slug
|
||||
content
|
||||
contentExcerpt
|
||||
language
|
||||
pinnedBy {
|
||||
id
|
||||
name
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
unpinGroupPost: gql`
|
||||
mutation ($id: ID!) {
|
||||
unpinGroupPost(id: $id) {
|
||||
id
|
||||
title
|
||||
slug
|
||||
content
|
||||
contentExcerpt
|
||||
language
|
||||
pinnedBy {
|
||||
id
|
||||
name
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
pushPost: gql`
|
||||
mutation ($id: ID!) {
|
||||
pushPost(id: $id) {
|
||||
|
||||
@ -47,12 +47,6 @@ export default (i18n) => {
|
||||
...badges
|
||||
}
|
||||
}
|
||||
group {
|
||||
id
|
||||
name
|
||||
slug
|
||||
groupType
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -90,12 +84,6 @@ export const filterPosts = (i18n) => {
|
||||
...location
|
||||
...badges
|
||||
}
|
||||
group {
|
||||
id
|
||||
name
|
||||
slug
|
||||
groupType
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -132,12 +120,6 @@ export const profilePagePosts = (i18n) => {
|
||||
...location
|
||||
...badges
|
||||
}
|
||||
group {
|
||||
id
|
||||
name
|
||||
slug
|
||||
groupType
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@ -27,7 +27,16 @@ export const post = gql`
|
||||
}
|
||||
pinnedAt
|
||||
pinned
|
||||
groupPinned
|
||||
isObservedByMe
|
||||
observingUsersCount
|
||||
group {
|
||||
id
|
||||
name
|
||||
slug
|
||||
groupType
|
||||
myRole
|
||||
currentlyPinnedPostsCount
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@ -861,6 +861,10 @@
|
||||
"menu": {
|
||||
"delete": "Beitrag löschen",
|
||||
"edit": "Beitrag bearbeiten",
|
||||
"groupPin": "Beitrag anheften (Gruppe)",
|
||||
"groupPinnedSuccessfully": "Beitrag erfolgreich angeheftet!",
|
||||
"groupUnpin": "Beitrag loslösen (Gruppe)",
|
||||
"groupUnpinnedSuccessfully": "Angehefteten Beitrag erfolgreich losgelöst!",
|
||||
"observe": "Beitrag beobachten",
|
||||
"observedSuccessfully": "Du beobachtest diesen Beitrag!",
|
||||
"pin": "Beitrag anheften",
|
||||
|
||||
@ -861,6 +861,10 @@
|
||||
"menu": {
|
||||
"delete": "Delete post",
|
||||
"edit": "Edit post",
|
||||
"groupPin": "Pin post (Group)",
|
||||
"groupPinnedSuccessfully": "Post pinned successfully!",
|
||||
"groupUnpin": "Unpin post (Group)",
|
||||
"groupUnpinnedSuccessfully": "Post unpinned successfully!",
|
||||
"observe": "Observe post",
|
||||
"observedSuccessfully": "You are now observing this post!",
|
||||
"pin": "Pin post",
|
||||
|
||||
@ -861,6 +861,10 @@
|
||||
"menu": {
|
||||
"delete": "Borrar contribución",
|
||||
"edit": "Editar contribución",
|
||||
"groupPin": null,
|
||||
"groupPinnedSuccessfully": null,
|
||||
"groupUnpin": null,
|
||||
"groupUnpinnedSuccessfully": null,
|
||||
"observe": "Observar contribución",
|
||||
"observedSuccessfully": null,
|
||||
"pin": "Anclar contribución",
|
||||
|
||||
@ -861,6 +861,10 @@
|
||||
"menu": {
|
||||
"delete": "Supprimer le Post",
|
||||
"edit": "Modifier le Post",
|
||||
"groupPin": null,
|
||||
"groupPinnedSuccessfully": null,
|
||||
"groupUnpin": null,
|
||||
"groupUnpinnedSuccessfully": null,
|
||||
"observe": "Observer le Post",
|
||||
"observedSuccessfully": null,
|
||||
"pin": "Épingler le Post",
|
||||
|
||||
@ -861,6 +861,10 @@
|
||||
"menu": {
|
||||
"delete": null,
|
||||
"edit": null,
|
||||
"groupPin": null,
|
||||
"groupPinnedSuccessfully": null,
|
||||
"groupUnpin": null,
|
||||
"groupUnpinnedSuccessfully": null,
|
||||
"observe": null,
|
||||
"observedSuccessfully": null,
|
||||
"pin": null,
|
||||
|
||||
@ -861,6 +861,10 @@
|
||||
"menu": {
|
||||
"delete": null,
|
||||
"edit": null,
|
||||
"groupPin": null,
|
||||
"groupPinnedSuccessfully": null,
|
||||
"groupUnpin": null,
|
||||
"groupUnpinnedSuccessfully": null,
|
||||
"observe": null,
|
||||
"observedSuccessfully": null,
|
||||
"pin": null,
|
||||
|
||||
@ -861,6 +861,10 @@
|
||||
"menu": {
|
||||
"delete": "Usuń wpis",
|
||||
"edit": "Edytuj wpis",
|
||||
"groupPin": null,
|
||||
"groupPinnedSuccessfully": null,
|
||||
"groupUnpin": null,
|
||||
"groupUnpinnedSuccessfully": null,
|
||||
"observe": null,
|
||||
"observedSuccessfully": null,
|
||||
"pin": null,
|
||||
|
||||
@ -861,6 +861,10 @@
|
||||
"menu": {
|
||||
"delete": "Excluir publicação",
|
||||
"edit": "Editar publicação",
|
||||
"groupPin": null,
|
||||
"groupPinnedSuccessfully": null,
|
||||
"groupUnpin": null,
|
||||
"groupUnpinnedSuccessfully": null,
|
||||
"observe": "Observar publicação",
|
||||
"observedSuccessfully": null,
|
||||
"pin": "Fixar publicação",
|
||||
|
||||
@ -861,6 +861,10 @@
|
||||
"menu": {
|
||||
"delete": "Удалить пост",
|
||||
"edit": "Редактировать пост",
|
||||
"groupPin": null,
|
||||
"groupPinnedSuccessfully": null,
|
||||
"groupUnpin": null,
|
||||
"groupUnpinnedSuccessfully": null,
|
||||
"observe": null,
|
||||
"observedSuccessfully": null,
|
||||
"pin": "Закрепить пост",
|
||||
|
||||
@ -38,6 +38,36 @@ export default {
|
||||
})
|
||||
.catch((error) => this.$toast.error(error.message))
|
||||
},
|
||||
pinGroupPost(post, refetchPostList = () => {}) {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: PostMutations().pinGroupPost,
|
||||
variables: {
|
||||
id: post.id,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.$toast.success(this.$t('post.menu.groupPinnedSuccessfully'))
|
||||
// this.storePinGroupPost()
|
||||
refetchPostList()
|
||||
})
|
||||
.catch((error) => this.$toast.error(error.message))
|
||||
},
|
||||
unpinGroupPost(post, refetchPostList = () => {}) {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: PostMutations().unpinGroupPost,
|
||||
variables: {
|
||||
id: post.id,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.$toast.success(this.$t('post.menu.groupUnpinnedSuccessfully'))
|
||||
// this.storeUnpinGroupPost()
|
||||
refetchPostList()
|
||||
})
|
||||
.catch((error) => this.$toast.error(error.message))
|
||||
},
|
||||
pushPost(post, refetchPostList = () => {}) {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
</base-button>
|
||||
<!-- Group join / leave -->
|
||||
<join-leave-button
|
||||
:group="group || {}"
|
||||
:group="group"
|
||||
:userId="currentUser.id"
|
||||
:isMember="isGroupMember"
|
||||
:isNonePendingMember="isGroupMemberNonePending"
|
||||
@ -243,9 +243,12 @@
|
||||
<post-teaser
|
||||
:post="post"
|
||||
:width="{ base: '100%', md: '100%', xl: '50%' }"
|
||||
:showGroupPinned="true"
|
||||
@removePostFromList="posts = removePostFromList(post, posts)"
|
||||
@pinPost="pinPost(post, refetchPostList)"
|
||||
@unpinPost="unpinPost(post, refetchPostList)"
|
||||
@pinGroupPost="pinGroupPost(post, refetchPostList)"
|
||||
@unpinGroupPost="unpinGroupPost(post, refetchPostList)"
|
||||
@pushPost="pushPost(post, refetchPostList)"
|
||||
@unpushPost="unpushPost(post, refetchPostList)"
|
||||
@toggleObservePost="
|
||||
@ -467,7 +470,7 @@ export default {
|
||||
offset: this.offset,
|
||||
filter: this.filter,
|
||||
first: this.pageSize,
|
||||
orderBy: 'sortDate_desc',
|
||||
orderBy: ['groupPinned_asc', 'sortDate_desc'],
|
||||
},
|
||||
updateQuery: UpdateQuery(this, { $state, pageKey: 'profilePagePosts' }),
|
||||
})
|
||||
@ -576,7 +579,7 @@ export default {
|
||||
filter: this.filter,
|
||||
first: this.pageSize,
|
||||
offset: 0,
|
||||
orderBy: 'sortDate_desc',
|
||||
orderBy: ['groupPinned_asc', 'sortDate_desc'],
|
||||
}
|
||||
},
|
||||
update({ profilePagePosts }) {
|
||||
|
||||
@ -116,6 +116,8 @@
|
||||
@removePostFromList="posts = removePostFromList(post, posts)"
|
||||
@pinPost="pinPost(post, refetchPostList)"
|
||||
@unpinPost="unpinPost(post, refetchPostList)"
|
||||
@pinGroupPost="pinGroupPost(post, refetchPostList)"
|
||||
@unpinGroupPost="unpinGroupPost(post, refetchPostList)"
|
||||
@pushPost="pushPost(post, refetchPostList)"
|
||||
@unpushPost="unpushPost(post, refetchPostList)"
|
||||
@toggleObservePost="
|
||||
|
||||
@ -58,6 +58,8 @@
|
||||
:is-owner="isAuthor"
|
||||
@pinPost="pinPost"
|
||||
@unpinPost="unpinPost"
|
||||
@pinGroupPost="pinGroupPost"
|
||||
@unpinGroupPost="unpinGroupPost"
|
||||
@pushPost="pushPost"
|
||||
@unpushPost="unpushPost"
|
||||
@toggleObservePost="toggleObservePost"
|
||||
|
||||
@ -78,7 +78,11 @@ describe('ProfileSlug', () => {
|
||||
'auth/user': {
|
||||
id: 'u23',
|
||||
},
|
||||
'auth/isAdmin': () => false,
|
||||
'pinnedPosts/maxPinnedPosts': () => 0,
|
||||
'pinnedPosts/currentlyPinnedPosts': () => 0,
|
||||
},
|
||||
dispatch: jest.fn().mockResolvedValue(),
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@ -160,6 +160,8 @@
|
||||
@removePostFromList="posts = removePostFromList(post, posts)"
|
||||
@pinPost="pinPost(post, refetchPostList)"
|
||||
@unpinPost="unpinPost(post, refetchPostList)"
|
||||
@pinGroupPost="pinGroupPost(post, refetchPostList)"
|
||||
@unpinGroupPost="unpinGroupPost(post, refetchPostList)"
|
||||
@pushPost="pushPost(post, refetchPostList)"
|
||||
@unpushPost="unpushPost(post, refetchPostList)"
|
||||
@toggleObservePost="
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user