mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-12 23:35:58 +00:00
feat(backend): push posts (#8609)
* push posts push posts * unpush posts * fix comment query * locales * fix locales * fix tests * Update webapp/locales/de.json Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com> * Update webapp/locales/de.json Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com> * Update webapp/locales/de.json Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com> * fix unpushedSuccessfully english message * remove paremeters from unpushPost * rename pushPostToTop -> pushPost, tests * update locales & tests webapp * fix lint --------- Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com>
This commit is contained in:
parent
d80ff05116
commit
e87a33eb3f
53
backend/src/db/migrations/20250530140506-post-sort-date.ts
Normal file
53
backend/src/db/migrations/20250530140506-post-sort-date.ts
Normal file
@ -0,0 +1,53 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
|
||||
import { getDriver } from '@db/neo4j'
|
||||
|
||||
export const description = ''
|
||||
|
||||
export async function up(_next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const transaction = session.beginTransaction()
|
||||
|
||||
try {
|
||||
// Implement your migration here.
|
||||
await transaction.run(`
|
||||
MATCH (p:Post)
|
||||
SET p.sortDate = p.createdAt
|
||||
`)
|
||||
await transaction.commit()
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
await transaction.rollback()
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('rolled back')
|
||||
throw new Error(error)
|
||||
} finally {
|
||||
await session.close()
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(_next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const transaction = session.beginTransaction()
|
||||
|
||||
try {
|
||||
// Implement your migration here.
|
||||
await transaction.run(`
|
||||
MATCH (p:Post)
|
||||
REMOVE p.sortDate
|
||||
`)
|
||||
await transaction.commit()
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
await transaction.rollback()
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('rolled back')
|
||||
throw new Error(error)
|
||||
} finally {
|
||||
await session.close()
|
||||
}
|
||||
}
|
||||
@ -45,6 +45,7 @@ export default {
|
||||
required: true,
|
||||
default: () => new Date().toISOString(),
|
||||
},
|
||||
sortDate: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
language: { type: 'string', allow: [null] },
|
||||
comments: {
|
||||
type: 'relationship',
|
||||
|
||||
12
backend/src/graphql/queries/Post.ts
Normal file
12
backend/src/graphql/queries/Post.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const Post = gql`
|
||||
query ($orderBy: [_PostOrdering]) {
|
||||
Post(orderBy: $orderBy) {
|
||||
id
|
||||
pinned
|
||||
createdAt
|
||||
pinnedAt
|
||||
}
|
||||
}
|
||||
`
|
||||
9
backend/src/graphql/queries/pushPost.ts
Normal file
9
backend/src/graphql/queries/pushPost.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const pushPost = gql`
|
||||
mutation pushPost($id: ID!) {
|
||||
pushPost(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
9
backend/src/graphql/queries/unpushPost.ts
Normal file
9
backend/src/graphql/queries/unpushPost.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const unpushPost = gql`
|
||||
mutation unpushPost($id: ID!) {
|
||||
unpushPost(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -12,6 +12,9 @@ import Factory, { cleanDatabase } from '@db/factories'
|
||||
import Image from '@db/models/Image'
|
||||
import { createGroupMutation } from '@graphql/queries/createGroupMutation'
|
||||
import { createPostMutation } from '@graphql/queries/createPostMutation'
|
||||
import { Post } from '@graphql/queries/Post'
|
||||
import { pushPost } from '@graphql/queries/pushPost'
|
||||
import { unpushPost } from '@graphql/queries/unpushPost'
|
||||
import createServer, { getContext } from '@src/server'
|
||||
|
||||
CONFIG.CATEGORIES_ACTIVE = true
|
||||
@ -1009,6 +1012,281 @@ describe('UpdatePost', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('push posts', () => {
|
||||
let author
|
||||
beforeEach(async () => {
|
||||
author = await Factory.build('user', { slug: 'the-author' })
|
||||
await Factory.build(
|
||||
'post',
|
||||
{
|
||||
id: 'pFirst',
|
||||
},
|
||||
{
|
||||
author,
|
||||
categoryIds,
|
||||
},
|
||||
)
|
||||
await Factory.build(
|
||||
'post',
|
||||
{
|
||||
id: 'pSecond',
|
||||
},
|
||||
{
|
||||
author,
|
||||
categoryIds,
|
||||
},
|
||||
)
|
||||
await Factory.build(
|
||||
'post',
|
||||
{
|
||||
id: 'pThird',
|
||||
},
|
||||
{
|
||||
author,
|
||||
categoryIds,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
authenticatedUser = null
|
||||
await expect(
|
||||
mutate({ mutation: pushPost, variables: { id: 'pSecond' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: null,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('ordinary users', () => {
|
||||
it('throws authorization error', async () => {
|
||||
await expect(
|
||||
mutate({ mutation: pushPost, variables: { id: 'pSecond' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: null,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('moderators', () => {
|
||||
let moderator
|
||||
beforeEach(async () => {
|
||||
moderator = await user.update({ role: 'moderator', updatedAt: new Date().toISOString() })
|
||||
authenticatedUser = await moderator.toJson()
|
||||
})
|
||||
|
||||
it('throws authorization error', async () => {
|
||||
await expect(
|
||||
mutate({ mutation: pushPost, variables: { id: 'pSecond' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: null,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('admins', () => {
|
||||
let admin
|
||||
beforeEach(async () => {
|
||||
admin = await Factory.build('user', {
|
||||
id: 'admin',
|
||||
role: 'admin',
|
||||
})
|
||||
authenticatedUser = await admin.toJson()
|
||||
})
|
||||
|
||||
it('pushes the post to the front of the feed', async () => {
|
||||
await expect(
|
||||
query({ query: Post, variables: { orderBy: ['sortDate_desc'] } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
data: {
|
||||
Post: [
|
||||
{
|
||||
id: 'pThird',
|
||||
},
|
||||
{
|
||||
id: 'pSecond',
|
||||
},
|
||||
{
|
||||
id: 'pFirst',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
await expect(
|
||||
mutate({ mutation: pushPost, variables: { id: 'pSecond' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
data: {
|
||||
pushPost: {
|
||||
id: 'pSecond',
|
||||
},
|
||||
},
|
||||
})
|
||||
await expect(
|
||||
query({ query: Post, variables: { orderBy: ['sortDate_desc'] } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
data: {
|
||||
Post: [
|
||||
{
|
||||
id: 'pSecond',
|
||||
},
|
||||
{
|
||||
id: 'pThird',
|
||||
},
|
||||
{
|
||||
id: 'pFirst',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('unpush posts', () => {
|
||||
let author
|
||||
let admin
|
||||
beforeEach(async () => {
|
||||
author = await Factory.build('user', { slug: 'the-author' })
|
||||
await Factory.build(
|
||||
'post',
|
||||
{
|
||||
id: 'pFirst',
|
||||
},
|
||||
{
|
||||
author,
|
||||
categoryIds,
|
||||
},
|
||||
)
|
||||
await Factory.build(
|
||||
'post',
|
||||
{
|
||||
id: 'pSecond',
|
||||
},
|
||||
{
|
||||
author,
|
||||
categoryIds,
|
||||
},
|
||||
)
|
||||
await Factory.build(
|
||||
'post',
|
||||
{
|
||||
id: 'pThird',
|
||||
},
|
||||
{
|
||||
author,
|
||||
categoryIds,
|
||||
},
|
||||
)
|
||||
admin = await Factory.build('user', {
|
||||
id: 'admin',
|
||||
role: 'admin',
|
||||
})
|
||||
authenticatedUser = await admin.toJson()
|
||||
await mutate({ mutation: pushPost, variables: { id: 'pSecond' } })
|
||||
authenticatedUser = null
|
||||
})
|
||||
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
authenticatedUser = null
|
||||
await expect(
|
||||
mutate({ mutation: unpushPost, variables: { id: 'pSecond' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: null,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('ordinary users', () => {
|
||||
it('throws authorization error', async () => {
|
||||
authenticatedUser = await user.toJson()
|
||||
await expect(
|
||||
mutate({ mutation: unpushPost, variables: { id: 'pSecond' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: null,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('moderators', () => {
|
||||
let moderator
|
||||
beforeEach(async () => {
|
||||
moderator = await user.update({ role: 'moderator', updatedAt: new Date().toISOString() })
|
||||
authenticatedUser = await moderator.toJson()
|
||||
})
|
||||
|
||||
it('throws authorization error', async () => {
|
||||
await expect(
|
||||
mutate({ mutation: unpushPost, variables: { id: 'pSecond' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
data: null,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('admins', () => {
|
||||
it('cancels the push of the post and puts it in the original order', async () => {
|
||||
authenticatedUser = await admin.toJson()
|
||||
await expect(
|
||||
query({ query: Post, variables: { orderBy: ['sortDate_desc'] } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
data: {
|
||||
Post: [
|
||||
{
|
||||
id: 'pSecond',
|
||||
},
|
||||
{
|
||||
id: 'pThird',
|
||||
},
|
||||
{
|
||||
id: 'pFirst',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
await expect(
|
||||
mutate({ mutation: unpushPost, variables: { id: 'pSecond' } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
data: {
|
||||
unpushPost: {
|
||||
id: 'pSecond',
|
||||
},
|
||||
},
|
||||
})
|
||||
await expect(
|
||||
query({ query: Post, variables: { orderBy: ['sortDate_desc'] } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
data: {
|
||||
Post: [
|
||||
{
|
||||
id: 'pThird',
|
||||
},
|
||||
{
|
||||
id: 'pSecond',
|
||||
},
|
||||
{
|
||||
id: 'pFirst',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('pin posts', () => {
|
||||
let author
|
||||
const pinPostMutation = gql`
|
||||
@ -1097,17 +1375,6 @@ describe('pin posts', () => {
|
||||
authenticatedUser = await admin.toJson()
|
||||
})
|
||||
|
||||
const postOrderingQuery = gql`
|
||||
query ($orderBy: [_PostOrdering]) {
|
||||
Post(orderBy: $orderBy) {
|
||||
id
|
||||
pinned
|
||||
createdAt
|
||||
pinnedAt
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
describe('MAX_PINNED_POSTS is 0', () => {
|
||||
beforeEach(async () => {
|
||||
CONFIG.MAX_PINNED_POSTS = 0
|
||||
@ -1451,7 +1718,7 @@ describe('pin posts', () => {
|
||||
})
|
||||
|
||||
it('pinned post appear first even when created before other posts', async () => {
|
||||
await expect(query({ query: postOrderingQuery, variables })).resolves.toMatchObject({
|
||||
await expect(query({ query: Post, variables })).resolves.toMatchObject({
|
||||
data: {
|
||||
Post: [
|
||||
{
|
||||
@ -1658,45 +1925,43 @@ describe('pin posts', () => {
|
||||
})
|
||||
|
||||
it('places the pinned posts first, though they are much older', async () => {
|
||||
await expect(query({ query: postOrderingQuery, variables })).resolves.toMatchObject(
|
||||
{
|
||||
data: {
|
||||
Post: [
|
||||
{
|
||||
id: 'first-post',
|
||||
pinned: true,
|
||||
pinnedAt: expect.any(String),
|
||||
createdAt: '2019-10-22T17:26:29.070Z',
|
||||
},
|
||||
{
|
||||
id: 'second-post',
|
||||
pinned: true,
|
||||
pinnedAt: expect.any(String),
|
||||
createdAt: '2018-10-22T17:26:29.070Z',
|
||||
},
|
||||
{
|
||||
id: 'third-post',
|
||||
pinned: true,
|
||||
pinnedAt: expect.any(String),
|
||||
createdAt: '2017-10-22T17:26:29.070Z',
|
||||
},
|
||||
{
|
||||
id: 'another-post',
|
||||
pinned: null,
|
||||
pinnedAt: null,
|
||||
createdAt: expect.any(String),
|
||||
},
|
||||
{
|
||||
id: 'p9876',
|
||||
pinned: null,
|
||||
pinnedAt: null,
|
||||
createdAt: expect.any(String),
|
||||
},
|
||||
],
|
||||
},
|
||||
errors: undefined,
|
||||
await expect(query({ query: Post, variables })).resolves.toMatchObject({
|
||||
data: {
|
||||
Post: [
|
||||
{
|
||||
id: 'first-post',
|
||||
pinned: true,
|
||||
pinnedAt: expect.any(String),
|
||||
createdAt: '2019-10-22T17:26:29.070Z',
|
||||
},
|
||||
{
|
||||
id: 'second-post',
|
||||
pinned: true,
|
||||
pinnedAt: expect.any(String),
|
||||
createdAt: '2018-10-22T17:26:29.070Z',
|
||||
},
|
||||
{
|
||||
id: 'third-post',
|
||||
pinned: true,
|
||||
pinnedAt: expect.any(String),
|
||||
createdAt: '2017-10-22T17:26:29.070Z',
|
||||
},
|
||||
{
|
||||
id: 'another-post',
|
||||
pinned: null,
|
||||
pinnedAt: null,
|
||||
createdAt: expect.any(String),
|
||||
},
|
||||
{
|
||||
id: 'p9876',
|
||||
pinned: null,
|
||||
pinnedAt: null,
|
||||
createdAt: expect.any(String),
|
||||
},
|
||||
],
|
||||
},
|
||||
)
|
||||
errors: undefined,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -158,6 +158,7 @@ export default {
|
||||
SET post += $params
|
||||
SET post.createdAt = toString(datetime())
|
||||
SET post.updatedAt = toString(datetime())
|
||||
SET post.sortDate = toString(datetime())
|
||||
SET post.clickedCount = 0
|
||||
SET post.viewedTeaserCount = 0
|
||||
SET post:${params.postType}
|
||||
@ -493,6 +494,40 @@ export default {
|
||||
session.close()
|
||||
}
|
||||
},
|
||||
pushPost: async (_parent, params, context: Context, _resolveInfo) => {
|
||||
const posts = (
|
||||
await context.database.write({
|
||||
query: `
|
||||
MATCH (post:Post {id: $id})
|
||||
SET post.sortDate = toString(datetime())
|
||||
RETURN post {.*}`,
|
||||
variables: params,
|
||||
})
|
||||
).records.map((record) => record.get('post'))
|
||||
|
||||
if (posts.length !== 1) {
|
||||
throw new Error('Could not find Post')
|
||||
}
|
||||
|
||||
return posts[0]
|
||||
},
|
||||
unpushPost: async (_parent, params, context: Context, _resolveInfo) => {
|
||||
const posts = (
|
||||
await context.database.write({
|
||||
query: `
|
||||
MATCH (post:Post {id: $id})
|
||||
SET post.sortDate = post.createdAt
|
||||
RETURN post {.*}`,
|
||||
variables: params,
|
||||
})
|
||||
).records.map((record) => record.get('post'))
|
||||
|
||||
if (posts.length !== 1) {
|
||||
throw new Error('Could not find Post')
|
||||
}
|
||||
|
||||
return posts[0]
|
||||
},
|
||||
},
|
||||
Post: {
|
||||
...Resolver('Post', {
|
||||
|
||||
@ -103,6 +103,8 @@ enum _PostOrdering {
|
||||
createdAt_desc
|
||||
updatedAt_asc
|
||||
updatedAt_desc
|
||||
sortDate_asc
|
||||
sortDate_desc
|
||||
language_asc
|
||||
language_desc
|
||||
pinned_asc
|
||||
@ -128,6 +130,7 @@ type Post {
|
||||
pinned: Boolean
|
||||
createdAt: String
|
||||
updatedAt: String
|
||||
sortDate: String
|
||||
language: String
|
||||
pinnedAt: String @cypher(
|
||||
statement: "MATCH (this)<-[pinned:PINNED]-(:User) WHERE NOT this.deleted = true AND NOT this.disabled = true RETURN pinned.createdAt"
|
||||
@ -241,6 +244,8 @@ type Mutation {
|
||||
pinPost(id: ID!): Post
|
||||
unpinPost(id: ID!): Post
|
||||
markTeaserAsViewed(id: ID!): Post
|
||||
pushPost(id: ID!): Post!
|
||||
unpushPost(id: ID!): Post!
|
||||
|
||||
# Shout the given Type and ID
|
||||
shout(id: ID!, type: ShoutTypeEnum!): Boolean!
|
||||
|
||||
@ -9,7 +9,7 @@ const defaultOrderBy = (resolve, root, args, context, resolveInfo) => {
|
||||
const newestFirst = {
|
||||
kind: 'Argument',
|
||||
name: { kind: 'Name', value: 'orderBy' },
|
||||
value: { kind: 'EnumValue', value: 'createdAt_desc' },
|
||||
value: { kind: 'EnumValue', value: 'sortDate_desc' },
|
||||
}
|
||||
const [fieldNode] = copy.fieldNodes
|
||||
if (fieldNode) fieldNode.arguments.push(newestFirst)
|
||||
|
||||
@ -484,6 +484,8 @@ export default shield(
|
||||
VerifyEmailAddress: isAuthenticated,
|
||||
pinPost: isAdmin,
|
||||
unpinPost: isAdmin,
|
||||
pushPost: isAdmin,
|
||||
unpushPost: isAdmin,
|
||||
UpdateDonations: isAdmin,
|
||||
|
||||
// InviteCode
|
||||
|
||||
@ -99,6 +99,82 @@ describe('ContentMenu.vue', () => {
|
||||
})
|
||||
|
||||
describe('admin can', () => {
|
||||
it('push post', async () => {
|
||||
getters['auth/isAdmin'] = () => true
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
sortDate: 'some-date',
|
||||
createdAt: 'some-date',
|
||||
},
|
||||
})
|
||||
expect(
|
||||
wrapper.findAll('.ds-menu-item').filter((item) => item.text() === 'post.menu.push'),
|
||||
).toHaveLength(1)
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.push')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('pushPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
sortDate: 'some-date',
|
||||
createdAt: 'some-date',
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
|
||||
it('not unpush post which was not pushed', async () => {
|
||||
getters['auth/isAdmin'] = () => true
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
sortDate: 'some-date',
|
||||
createdAt: 'some-date',
|
||||
},
|
||||
})
|
||||
expect(
|
||||
wrapper.findAll('.ds-menu-item').filter((item) => item.text() === 'post.menu.unpush'),
|
||||
).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('unpush post which was pushed', async () => {
|
||||
getters['auth/isAdmin'] = () => true
|
||||
const wrapper = await openContentMenu({
|
||||
isOwner: false,
|
||||
resourceType: 'contribution',
|
||||
resource: {
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
sortDate: 'some-date',
|
||||
createdAt: 'some-other-date',
|
||||
},
|
||||
})
|
||||
expect(
|
||||
wrapper.findAll('.ds-menu-item').filter((item) => item.text() === 'post.menu.unpush'),
|
||||
).toHaveLength(1)
|
||||
wrapper
|
||||
.findAll('.ds-menu-item')
|
||||
.filter((item) => item.text() === 'post.menu.unpush')
|
||||
.at(0)
|
||||
.trigger('click')
|
||||
expect(wrapper.emitted('unpushPost')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8',
|
||||
sortDate: 'some-date',
|
||||
createdAt: 'some-other-date',
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
|
||||
describe('when maxPinnedPosts = 0', () => {
|
||||
beforeEach(() => {
|
||||
maxPinnedPostsMock.mockReturnValue(0)
|
||||
|
||||
@ -104,6 +104,26 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isAdmin) {
|
||||
routes.push({
|
||||
label: this.$t(`post.menu.push`),
|
||||
callback: () => {
|
||||
this.$emit('pushPost', this.resource)
|
||||
},
|
||||
icon: 'link',
|
||||
})
|
||||
}
|
||||
|
||||
if (this.isAdmin && this.resource.sortDate !== this.resource.createdAt) {
|
||||
routes.push({
|
||||
label: this.$t(`post.menu.unpush`),
|
||||
callback: () => {
|
||||
this.$emit('unpushPost', this.resource)
|
||||
},
|
||||
icon: 'link',
|
||||
})
|
||||
}
|
||||
|
||||
if (this.resource.isObservedByMe) {
|
||||
routes.push({
|
||||
label: this.$t(`post.menu.unobserve`),
|
||||
|
||||
@ -13,7 +13,7 @@ describe('FilterMenu.vue', () => {
|
||||
const getters = {
|
||||
'posts/isActive': () => false,
|
||||
'posts/filteredPostTypes': () => [],
|
||||
'posts/orderBy': () => 'createdAt_desc',
|
||||
'posts/orderBy': () => 'sortDate_desc',
|
||||
'categories/categoriesActive': () => false,
|
||||
}
|
||||
const actions = {
|
||||
|
||||
@ -13,7 +13,7 @@ describe('OrderByFilter', () => {
|
||||
const getters = {
|
||||
'posts/filteredPostTypes': () => [],
|
||||
'posts/orderedByCreationDate': () => true,
|
||||
'posts/orderBy': () => 'createdAt_desc',
|
||||
'posts/orderBy': () => 'sortDate_desc',
|
||||
}
|
||||
const actions = {
|
||||
'categories/init': jest.fn(),
|
||||
@ -54,7 +54,7 @@ describe('OrderByFilter', () => {
|
||||
|
||||
describe('if ordered by oldest', () => {
|
||||
beforeEach(() => {
|
||||
getters['posts/orderBy'] = jest.fn(() => 'createdAt_asc')
|
||||
getters['posts/orderBy'] = jest.fn(() => 'sortDate_asc')
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
@ -76,20 +76,20 @@ describe('OrderByFilter', () => {
|
||||
})
|
||||
|
||||
describe('click "newest-button"', () => {
|
||||
it('calls TOGGLE_ORDER with "createdAt_desc"', () => {
|
||||
it('calls TOGGLE_ORDER with "sortDate_desc"', () => {
|
||||
wrapper
|
||||
.find('.order-by-filter .filter-list .base-button[data-test="newest-button"]')
|
||||
.trigger('click')
|
||||
expect(mutations['posts/TOGGLE_ORDER']).toHaveBeenCalledWith({}, 'createdAt_desc')
|
||||
expect(mutations['posts/TOGGLE_ORDER']).toHaveBeenCalledWith({}, 'sortDate_desc')
|
||||
})
|
||||
})
|
||||
|
||||
describe('click "oldest-button"', () => {
|
||||
it('calls TOGGLE_ORDER with "createdAt_asc"', () => {
|
||||
it('calls TOGGLE_ORDER with "sortDate_asc"', () => {
|
||||
wrapper
|
||||
.find('.order-by-filter .filter-list .base-button[data-test="oldest-button"]')
|
||||
.trigger('click')
|
||||
expect(mutations['posts/TOGGLE_ORDER']).toHaveBeenCalledWith({}, 'createdAt_asc')
|
||||
expect(mutations['posts/TOGGLE_ORDER']).toHaveBeenCalledWith({}, 'sortDate_asc')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -49,10 +49,10 @@ export default {
|
||||
return !this.filteredPostTypes.includes('Event')
|
||||
},
|
||||
orderedAsc() {
|
||||
return this.orderedByCreationDate ? 'createdAt_asc' : 'eventStart_desc'
|
||||
return this.orderedByCreationDate ? 'sortDate_asc' : 'eventStart_desc'
|
||||
},
|
||||
orderedDesc() {
|
||||
return this.orderedByCreationDate ? 'createdAt_desc' : 'eventStart_asc'
|
||||
return this.orderedByCreationDate ? 'sortDate_desc' : 'eventStart_asc'
|
||||
},
|
||||
sectionTitle() {
|
||||
return this.orderedByCreationDate
|
||||
|
||||
@ -109,6 +109,8 @@
|
||||
:is-owner="isAuthor"
|
||||
@pinPost="pinPost"
|
||||
@unpinPost="unpinPost"
|
||||
@pushPost="pushPost"
|
||||
@unpushPost="unpushPost"
|
||||
@toggleObservePost="toggleObservePost"
|
||||
/>
|
||||
</client-only>
|
||||
@ -222,6 +224,12 @@ export default {
|
||||
unpinPost(post) {
|
||||
this.$emit('unpinPost', post)
|
||||
},
|
||||
pushPost(post) {
|
||||
this.$emit('pushPost', post)
|
||||
},
|
||||
unpushPost(post) {
|
||||
this.$emit('unpushPost', post)
|
||||
},
|
||||
toggleObservePost(postId, value) {
|
||||
this.$emit('toggleObservePost', postId, value)
|
||||
},
|
||||
|
||||
@ -48,6 +48,8 @@
|
||||
@removePostFromList="posts = removePostFromList(post, posts)"
|
||||
@pinPost="pinPost(post, refetchPostList)"
|
||||
@unpinPost="unpinPost(post, refetchPostList)"
|
||||
@pushPost="pushPost(post, refetchPostList)"
|
||||
@unpushPost="unpushPost(post, refetchPostList)"
|
||||
@toggleObservePost="
|
||||
(postId, value) => toggleObservePost(postId, value, refetchPostList)
|
||||
"
|
||||
|
||||
@ -73,6 +73,7 @@ export const postFragment = gql`
|
||||
contentExcerpt
|
||||
createdAt
|
||||
updatedAt
|
||||
sortDate
|
||||
disabled
|
||||
deleted
|
||||
slug
|
||||
|
||||
@ -168,6 +168,40 @@ export default () => {
|
||||
}
|
||||
}
|
||||
`,
|
||||
pushPost: gql`
|
||||
mutation ($id: ID!) {
|
||||
pushPost(id: $id) {
|
||||
id
|
||||
title
|
||||
slug
|
||||
content
|
||||
contentExcerpt
|
||||
language
|
||||
pinnedBy {
|
||||
id
|
||||
name
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
unpushPost: gql`
|
||||
mutation ($id: ID!) {
|
||||
unpushPost(id: $id) {
|
||||
id
|
||||
title
|
||||
slug
|
||||
content
|
||||
contentExcerpt
|
||||
language
|
||||
pinnedBy {
|
||||
id
|
||||
name
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
markTeaserAsViewed: gql`
|
||||
mutation ($id: ID!) {
|
||||
markTeaserAsViewed(id: $id) {
|
||||
|
||||
@ -856,10 +856,14 @@
|
||||
"observedSuccessfully": "Du beobachtest diesen Beitrag!",
|
||||
"pin": "Beitrag anheften",
|
||||
"pinnedSuccessfully": "Beitrag erfolgreich angeheftet!",
|
||||
"push": "Beitrag hochschieben",
|
||||
"pushedSuccessfully": "Beitrag erfolgreich nach oben geschoben!",
|
||||
"unobserve": "Beitrag nicht mehr beobachten",
|
||||
"unobservedSuccessfully": "Du beobachtest diesen Beitrag nicht mehr!",
|
||||
"unpin": "Beitrag loslösen",
|
||||
"unpinnedSuccessfully": "Angehefteten Beitrag erfolgreich losgelöst!"
|
||||
"unpinnedSuccessfully": "Angehefteten Beitrag erfolgreich losgelöst!",
|
||||
"unpush": "Beitrag hochschieben aufheben",
|
||||
"unpushedSuccessfully": "Hochschieben des Beitrags erfolgreich rückgängig gemacht!"
|
||||
},
|
||||
"name": "Beitrag",
|
||||
"pinned": "Meldung",
|
||||
|
||||
@ -856,10 +856,14 @@
|
||||
"observedSuccessfully": "You are now observing this post!",
|
||||
"pin": "Pin post",
|
||||
"pinnedSuccessfully": "Post pinned successfully!",
|
||||
"push": "Push to top",
|
||||
"pushedSuccessfully": "Post pushed to top successfully!",
|
||||
"unobserve": "Stop to observe post",
|
||||
"unobservedSuccessfully": "You are no longer observing this post!",
|
||||
"unpin": "Unpin post",
|
||||
"unpinnedSuccessfully": "Post unpinned successfully!"
|
||||
"unpinnedSuccessfully": "Post unpinned successfully!",
|
||||
"unpush": "Cancel push",
|
||||
"unpushedSuccessfully": "Post push has been canceled!"
|
||||
},
|
||||
"name": "Article",
|
||||
"pinned": "Announcement",
|
||||
|
||||
@ -856,10 +856,14 @@
|
||||
"observedSuccessfully": null,
|
||||
"pin": "Anclar contribución",
|
||||
"pinnedSuccessfully": "¡Contribución anclado con éxito!",
|
||||
"push": null,
|
||||
"pushedSuccessfully": null,
|
||||
"unobserve": "Dejar de observar contribución",
|
||||
"unobservedSuccessfully": null,
|
||||
"unpin": "Desanclar contribución",
|
||||
"unpinnedSuccessfully": "¡Contribución desanclado con éxito!"
|
||||
"unpinnedSuccessfully": "¡Contribución desanclado con éxito!",
|
||||
"unpush": null,
|
||||
"unpushedSuccessfully": null
|
||||
},
|
||||
"name": "Contribución",
|
||||
"pinned": "Anuncio",
|
||||
|
||||
@ -856,10 +856,14 @@
|
||||
"observedSuccessfully": null,
|
||||
"pin": "Épingler le Post",
|
||||
"pinnedSuccessfully": "Poste épinglé avec succès!",
|
||||
"push": null,
|
||||
"pushedSuccessfully": null,
|
||||
"unobserve": "Ne plus observer le Post",
|
||||
"unobservedSuccessfully": null,
|
||||
"unpin": "Retirer l'épingle du poste",
|
||||
"unpinnedSuccessfully": "Épingle retirer du Post avec succès!"
|
||||
"unpinnedSuccessfully": "Épingle retirer du Post avec succès!",
|
||||
"unpush": null,
|
||||
"unpushedSuccessfully": null
|
||||
},
|
||||
"name": "Post",
|
||||
"pinned": "Annonce",
|
||||
|
||||
@ -856,10 +856,14 @@
|
||||
"observedSuccessfully": null,
|
||||
"pin": null,
|
||||
"pinnedSuccessfully": null,
|
||||
"push": null,
|
||||
"pushedSuccessfully": null,
|
||||
"unobserve": null,
|
||||
"unobservedSuccessfully": null,
|
||||
"unpin": null,
|
||||
"unpinnedSuccessfully": null
|
||||
"unpinnedSuccessfully": null,
|
||||
"unpush": null,
|
||||
"unpushedSuccessfully": null
|
||||
},
|
||||
"name": "Messaggio",
|
||||
"pinned": null,
|
||||
|
||||
@ -856,10 +856,14 @@
|
||||
"observedSuccessfully": null,
|
||||
"pin": null,
|
||||
"pinnedSuccessfully": null,
|
||||
"push": null,
|
||||
"pushedSuccessfully": null,
|
||||
"unobserve": null,
|
||||
"unobservedSuccessfully": null,
|
||||
"unpin": null,
|
||||
"unpinnedSuccessfully": null
|
||||
"unpinnedSuccessfully": null,
|
||||
"unpush": null,
|
||||
"unpushedSuccessfully": null
|
||||
},
|
||||
"name": "Post",
|
||||
"pinned": null,
|
||||
|
||||
@ -856,10 +856,14 @@
|
||||
"observedSuccessfully": null,
|
||||
"pin": null,
|
||||
"pinnedSuccessfully": null,
|
||||
"push": null,
|
||||
"pushedSuccessfully": null,
|
||||
"unobserve": null,
|
||||
"unobservedSuccessfully": null,
|
||||
"unpin": null,
|
||||
"unpinnedSuccessfully": null
|
||||
"unpinnedSuccessfully": null,
|
||||
"unpush": null,
|
||||
"unpushedSuccessfully": null
|
||||
},
|
||||
"name": "Poczta",
|
||||
"pinned": null,
|
||||
|
||||
@ -856,10 +856,14 @@
|
||||
"observedSuccessfully": null,
|
||||
"pin": "Fixar publicação",
|
||||
"pinnedSuccessfully": "Publicação fixada com sucesso!",
|
||||
"push": null,
|
||||
"pushedSuccessfully": null,
|
||||
"unobserve": "Deixar de observar publicação",
|
||||
"unobservedSuccessfully": null,
|
||||
"unpin": "Desafixar publicação",
|
||||
"unpinnedSuccessfully": "Publicação desafixada com sucesso!"
|
||||
"unpinnedSuccessfully": "Publicação desafixada com sucesso!",
|
||||
"unpush": null,
|
||||
"unpushedSuccessfully": null
|
||||
},
|
||||
"name": "Postar",
|
||||
"pinned": "Anúncio",
|
||||
|
||||
@ -856,10 +856,14 @@
|
||||
"observedSuccessfully": null,
|
||||
"pin": "Закрепить пост",
|
||||
"pinnedSuccessfully": "Пост больше не закреплен!",
|
||||
"push": null,
|
||||
"pushedSuccessfully": null,
|
||||
"unobserve": null,
|
||||
"unobservedSuccessfully": null,
|
||||
"unpin": "Открепить пост",
|
||||
"unpinnedSuccessfully": "Пост успешно не закреплено!"
|
||||
"unpinnedSuccessfully": "Пост успешно не закреплено!",
|
||||
"unpush": null,
|
||||
"unpushedSuccessfully": null
|
||||
},
|
||||
"name": "Пост",
|
||||
"pinned": "Объявление",
|
||||
|
||||
@ -38,6 +38,34 @@ export default {
|
||||
})
|
||||
.catch((error) => this.$toast.error(error.message))
|
||||
},
|
||||
pushPost(post, refetchPostList = () => {}) {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: PostMutations().pushPost,
|
||||
variables: {
|
||||
id: post.id,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.$toast.success(this.$t('post.menu.pushedSuccessfully'))
|
||||
refetchPostList()
|
||||
})
|
||||
.catch((error) => this.$toast.error(error.message))
|
||||
},
|
||||
unpushPost(post, refetchPostList = () => {}) {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: PostMutations().unpushPost,
|
||||
variables: {
|
||||
id: post.id,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.$toast.success(this.$t('post.menu.unpushedSuccessfully'))
|
||||
refetchPostList()
|
||||
})
|
||||
.catch((error) => this.$toast.error(error.message))
|
||||
},
|
||||
toggleObservePost(postId, value, refetchPostList = () => {}) {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
|
||||
@ -267,6 +267,8 @@
|
||||
@removePostFromList="posts = removePostFromList(post, posts)"
|
||||
@pinPost="pinPost(post, refetchPostList)"
|
||||
@unpinPost="unpinPost(post, refetchPostList)"
|
||||
@pushPost="pushPost(post, refetchPostList)"
|
||||
@unpushPost="unpushPost(post, refetchPostList)"
|
||||
@toggleObservePost="
|
||||
(postId, value) => toggleObservePost(postId, value, refetchPostList)
|
||||
"
|
||||
@ -493,7 +495,7 @@ export default {
|
||||
offset: this.offset,
|
||||
filter: this.filter,
|
||||
first: this.pageSize,
|
||||
orderBy: 'createdAt_desc',
|
||||
orderBy: 'sortDate_desc',
|
||||
},
|
||||
updateQuery: UpdateQuery(this, { $state, pageKey: 'profilePagePosts' }),
|
||||
})
|
||||
@ -602,7 +604,7 @@ export default {
|
||||
filter: this.filter,
|
||||
first: this.pageSize,
|
||||
offset: 0,
|
||||
orderBy: 'createdAt_desc',
|
||||
orderBy: 'sortDate_desc',
|
||||
}
|
||||
},
|
||||
update({ profilePagePosts }) {
|
||||
|
||||
@ -32,7 +32,7 @@ describe('PostIndex', () => {
|
||||
'posts/articleSetInPostTypeFilter': () => false,
|
||||
'posts/eventSetInPostTypeFilter': () => false,
|
||||
'posts/eventsEnded': () => '',
|
||||
'posts/orderBy': () => 'createdAt_desc',
|
||||
'posts/orderBy': () => 'sortDate_desc',
|
||||
'auth/user': () => {
|
||||
return { id: 'u23' }
|
||||
},
|
||||
|
||||
@ -116,6 +116,8 @@
|
||||
@removePostFromList="posts = removePostFromList(post, posts)"
|
||||
@pinPost="pinPost(post, refetchPostList)"
|
||||
@unpinPost="unpinPost(post, refetchPostList)"
|
||||
@pushPost="pushPost(post, refetchPostList)"
|
||||
@unpushPost="unpushPost(post, refetchPostList)"
|
||||
@toggleObservePost="
|
||||
(postId, value) => toggleObservePost(postId, value, refetchPostList)
|
||||
"
|
||||
|
||||
@ -49,6 +49,8 @@
|
||||
:is-owner="isAuthor"
|
||||
@pinPost="pinPost"
|
||||
@unpinPost="unpinPost"
|
||||
@pushPost="pushPost"
|
||||
@unpushPost="unpushPost"
|
||||
@toggleObservePost="toggleObservePost"
|
||||
/>
|
||||
</client-only>
|
||||
|
||||
@ -159,6 +159,8 @@
|
||||
@removePostFromList="posts = removePostFromList(post, posts)"
|
||||
@pinPost="pinPost(post, refetchPostList)"
|
||||
@unpinPost="unpinPost(post, refetchPostList)"
|
||||
@pushPost="pushPost(post, refetchPostList)"
|
||||
@unpushPost="unpushPost(post, refetchPostList)"
|
||||
@toggleObservePost="
|
||||
(postId, value) => toggleObservePost(postId, value, refetchPostList)
|
||||
"
|
||||
@ -329,7 +331,7 @@ export default {
|
||||
offset: this.offset,
|
||||
filter: this.filter,
|
||||
first: this.pageSize,
|
||||
orderBy: 'createdAt_desc',
|
||||
orderBy: 'sortDate_desc',
|
||||
},
|
||||
updateQuery: UpdateQuery(this, { $state, pageKey: 'profilePagePosts' }),
|
||||
})
|
||||
@ -433,7 +435,7 @@ export default {
|
||||
filter: this.filter,
|
||||
first: this.pageSize,
|
||||
offset: 0,
|
||||
orderBy: 'createdAt_desc',
|
||||
orderBy: 'sortDate_desc',
|
||||
}
|
||||
},
|
||||
update({ profilePagePosts }) {
|
||||
|
||||
@ -12,7 +12,7 @@ export const state = () => {
|
||||
filter: {
|
||||
...defaultFilter,
|
||||
},
|
||||
order: 'createdAt_desc',
|
||||
order: 'sortDate_desc',
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@ export const mutations = {
|
||||
const filter = clone(state.filter)
|
||||
delete filter.eventStart_gte
|
||||
delete filter.postType_in
|
||||
state.order = 'createdAt_desc'
|
||||
state.order = 'sortDate_desc'
|
||||
state.filter = filter
|
||||
},
|
||||
TOGGLE_POST_TYPE(state, postType) {
|
||||
@ -101,12 +101,12 @@ export const mutations = {
|
||||
state.order = 'eventStart_asc'
|
||||
} else {
|
||||
delete filter.eventStart_gte
|
||||
state.order = 'createdAt_desc'
|
||||
state.order = 'sortDate_desc'
|
||||
}
|
||||
} else {
|
||||
delete filter.eventStart_gte
|
||||
delete filter.postType_in
|
||||
state.order = 'createdAt_desc'
|
||||
state.order = 'sortDate_desc'
|
||||
}
|
||||
state.filter = filter
|
||||
},
|
||||
|
||||
@ -107,9 +107,9 @@ describe('getters', () => {
|
||||
describe('orderBy', () => {
|
||||
it('returns value for graphql query', () => {
|
||||
state = {
|
||||
order: 'createdAt_desc',
|
||||
order: 'sortDate_desc',
|
||||
}
|
||||
expect(getters.orderBy(state)).toEqual('createdAt_desc')
|
||||
expect(getters.orderBy(state)).toEqual('sortDate_desc')
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -270,7 +270,7 @@ describe('mutations', () => {
|
||||
order: 'eventStart_asc',
|
||||
}
|
||||
expect(testMutation('Article')).toEqual({ postType_in: ['Article'] })
|
||||
expect(getters.orderBy(state)).toEqual('createdAt_desc')
|
||||
expect(getters.orderBy(state)).toEqual('sortDate_desc')
|
||||
})
|
||||
|
||||
it('removes post type filter if same post type is present and sets order', () => {
|
||||
@ -282,7 +282,7 @@ describe('mutations', () => {
|
||||
order: 'eventStart_asc',
|
||||
}
|
||||
expect(testMutation('Event')).toEqual({})
|
||||
expect(getters.orderBy(state)).toEqual('createdAt_desc')
|
||||
expect(getters.orderBy(state)).toEqual('sortDate_desc')
|
||||
})
|
||||
|
||||
it('removes post type filter if called with null', () => {
|
||||
@ -294,7 +294,7 @@ describe('mutations', () => {
|
||||
order: 'eventStart_asc',
|
||||
}
|
||||
expect(testMutation(null)).toEqual({})
|
||||
expect(getters.orderBy(state)).toEqual('createdAt_desc')
|
||||
expect(getters.orderBy(state)).toEqual('sortDate_desc')
|
||||
})
|
||||
|
||||
it('does not get in the way of other filters', () => {
|
||||
@ -325,7 +325,7 @@ describe('mutations', () => {
|
||||
order: 'eventStart_asc',
|
||||
}
|
||||
expect(testMutation()).toEqual({})
|
||||
expect(getters.orderBy(state)).toEqual('createdAt_desc')
|
||||
expect(getters.orderBy(state)).toEqual('sortDate_desc')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user