mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Merge pull request #763 from Human-Connection/734-authorization-problem-disabling-post
Authorisation problem when disabling a post
This commit is contained in:
commit
60bbd3f408
@ -25,6 +25,9 @@ describe('Comment.vue', () => {
|
||||
success: jest.fn(),
|
||||
error: jest.fn(),
|
||||
},
|
||||
$filters: {
|
||||
truncate: a => a,
|
||||
},
|
||||
$apollo: {
|
||||
mutate: jest.fn().mockResolvedValue(),
|
||||
},
|
||||
|
||||
@ -9,12 +9,13 @@
|
||||
<ds-space margin-bottom="x-small">
|
||||
<hc-user :user="author" :date-time="comment.createdAt" />
|
||||
</ds-space>
|
||||
<!-- Content Menu (can open Modals) -->
|
||||
<no-ssr>
|
||||
<content-menu
|
||||
placement="bottom-end"
|
||||
resource-type="comment"
|
||||
:resource="comment"
|
||||
:callbacks="{ confirm: deleteCommentCallback, cancel: null }"
|
||||
:modalsData="menuModalsData"
|
||||
style="float-right"
|
||||
:is-owner="isAuthor(author.id)"
|
||||
/>
|
||||
@ -59,6 +60,30 @@ export default {
|
||||
if (this.deleted) return {}
|
||||
return this.comment.author || {}
|
||||
},
|
||||
menuModalsData() {
|
||||
return {
|
||||
delete: {
|
||||
titleIdent: 'delete.comment.title',
|
||||
messageIdent: 'delete.comment.message',
|
||||
messageParams: {
|
||||
name: this.$filters.truncate(this.comment.contentExcerpt, 30),
|
||||
},
|
||||
buttons: {
|
||||
confirm: {
|
||||
danger: true,
|
||||
icon: 'trash',
|
||||
textIdent: 'delete.submit',
|
||||
callback: this.deleteCommentCallback,
|
||||
},
|
||||
cancel: {
|
||||
icon: 'close',
|
||||
textIdent: 'delete.cancel',
|
||||
callback: () => {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isAuthor(id) {
|
||||
|
||||
@ -43,7 +43,13 @@ export default {
|
||||
return value.match(/(contribution|comment|organization|user)/)
|
||||
},
|
||||
},
|
||||
callbacks: { type: Object, required: true },
|
||||
modalsData: {
|
||||
type: Object,
|
||||
required: false,
|
||||
// default: () => {
|
||||
// return {}
|
||||
// },
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
routes() {
|
||||
@ -145,7 +151,7 @@ export default {
|
||||
data: {
|
||||
type: this.resourceType,
|
||||
resource: this.resource,
|
||||
callbacks: this.callbacks,
|
||||
modalsData: this.modalsData,
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
@ -49,6 +49,7 @@ import gql from 'graphql-tag'
|
||||
import HcEditor from '~/components/Editor'
|
||||
import orderBy from 'lodash/orderBy'
|
||||
import locales from '~/locales'
|
||||
import PostMutations from '~/graphql/PostMutations.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -106,11 +107,10 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
const postMutations = require('~/graphql/PostMutations.js').default(this)
|
||||
this.loading = true
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: this.id ? postMutations.UpdatePost : postMutations.CreatePost,
|
||||
mutation: this.id ? PostMutations().UpdatePost : PostMutations().CreatePost,
|
||||
variables: {
|
||||
id: this.id,
|
||||
title: this.form.title,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { shallowMount, createLocalVue } from '@vue/test-utils'
|
||||
import Modal from './Modal.vue'
|
||||
import DeleteModal from './Modal/DeleteModal.vue'
|
||||
import ConfirmModal from './Modal/ConfirmModal.vue'
|
||||
import DisableModal from './Modal/DisableModal.vue'
|
||||
import ReportModal from './Modal/ReportModal.vue'
|
||||
import Vuex from 'vuex'
|
||||
@ -60,7 +60,7 @@ describe('Modal.vue', () => {
|
||||
|
||||
it('initially empty', () => {
|
||||
wrapper = Wrapper()
|
||||
expect(wrapper.contains(DeleteModal)).toBe(false)
|
||||
expect(wrapper.contains(ConfirmModal)).toBe(false)
|
||||
expect(wrapper.contains(DisableModal)).toBe(false)
|
||||
expect(wrapper.contains(ReportModal)).toBe(false)
|
||||
})
|
||||
@ -75,10 +75,6 @@ describe('Modal.vue', () => {
|
||||
id: 'c456',
|
||||
title: 'some title',
|
||||
},
|
||||
callbacks: {
|
||||
confirm: null,
|
||||
cancel: null,
|
||||
},
|
||||
},
|
||||
}
|
||||
wrapper = Wrapper()
|
||||
@ -93,10 +89,6 @@ describe('Modal.vue', () => {
|
||||
type: 'contribution',
|
||||
name: 'some title',
|
||||
id: 'c456',
|
||||
callbacks: {
|
||||
confirm: null,
|
||||
cancel: null,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@ -117,20 +109,12 @@ describe('Modal.vue', () => {
|
||||
name: 'Author name',
|
||||
},
|
||||
},
|
||||
callbacks: {
|
||||
confirm: null,
|
||||
cancel: null,
|
||||
},
|
||||
}
|
||||
wrapper = Wrapper()
|
||||
expect(wrapper.find(DisableModal).props()).toEqual({
|
||||
type: 'comment',
|
||||
name: 'Author name',
|
||||
id: 'c456',
|
||||
callbacks: {
|
||||
confirm: null,
|
||||
cancel: null,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@ -140,20 +124,12 @@ describe('Modal.vue', () => {
|
||||
resource: {
|
||||
id: 'c456',
|
||||
},
|
||||
callbacks: {
|
||||
confirm: null,
|
||||
cancel: null,
|
||||
},
|
||||
}
|
||||
wrapper = Wrapper()
|
||||
expect(wrapper.find(DisableModal).props()).toEqual({
|
||||
type: 'comment',
|
||||
name: '',
|
||||
id: 'c456',
|
||||
callbacks: {
|
||||
confirm: null,
|
||||
cancel: null,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
:id="data.resource.id"
|
||||
:type="data.type"
|
||||
:name="name"
|
||||
:callbacks="data.callbacks"
|
||||
@close="close"
|
||||
/>
|
||||
<release-modal
|
||||
@ -21,22 +20,21 @@
|
||||
:id="data.resource.id"
|
||||
:type="data.type"
|
||||
:name="name"
|
||||
:callbacks="data.callbacks"
|
||||
@close="close"
|
||||
/>
|
||||
<delete-modal
|
||||
<confirm-modal
|
||||
v-if="open === 'delete'"
|
||||
:id="data.resource.id"
|
||||
:type="data.type"
|
||||
:name="name"
|
||||
:callbacks="data.callbacks"
|
||||
:modalData="data.modalsData.delete"
|
||||
@close="close"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DeleteModal from '~/components/Modal/DeleteModal'
|
||||
import ConfirmModal from '~/components/Modal/ConfirmModal'
|
||||
import DisableModal from '~/components/Modal/DisableModal'
|
||||
import ReleaseModal from '~/components/ReleaseModal/ReleaseModal.vue'
|
||||
import ReportModal from '~/components/Modal/ReportModal'
|
||||
@ -48,7 +46,7 @@ export default {
|
||||
DisableModal,
|
||||
ReleaseModal,
|
||||
ReportModal,
|
||||
DeleteModal,
|
||||
ConfirmModal,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
@ -63,7 +61,7 @@ export default {
|
||||
switch (this.data.type) {
|
||||
case 'user':
|
||||
return name
|
||||
case 'contribution':
|
||||
case 'contribution': // REFACTORING: In ConfirmModal – Already replaced "title" by "this.menuModalsData.delete.messageParams".
|
||||
return title
|
||||
case 'comment':
|
||||
return author && author.name
|
||||
|
||||
@ -1,28 +1,29 @@
|
||||
import { shallowMount, mount, createLocalVue } from '@vue/test-utils'
|
||||
import DeleteModal from './DeleteModal.vue'
|
||||
import Vuex from 'vuex'
|
||||
import Styleguide from '@human-connection/styleguide'
|
||||
import ConfirmModal from './ConfirmModal.vue'
|
||||
import { postMenuModalsData } from '~/components/utils/PostHelpers'
|
||||
|
||||
const localVue = createLocalVue()
|
||||
|
||||
localVue.use(Vuex)
|
||||
localVue.use(Styleguide)
|
||||
|
||||
describe('DeleteModal.vue', () => {
|
||||
describe('ConfirmModal.vue', () => {
|
||||
let Wrapper
|
||||
let wrapper
|
||||
let propsData
|
||||
let mocks
|
||||
const postName = 'It is a post'
|
||||
const confirmCallback = jest.fn()
|
||||
const cancelCallback = jest.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
propsData = {
|
||||
type: 'contribution',
|
||||
id: 'p23',
|
||||
name: 'It is a post',
|
||||
callbacks: {
|
||||
confirm: jest.fn(),
|
||||
cancel: jest.fn(),
|
||||
},
|
||||
name: postName,
|
||||
modalData: postMenuModalsData(postName, confirmCallback, cancelCallback).delete,
|
||||
}
|
||||
mocks = {
|
||||
$t: jest.fn(),
|
||||
@ -32,9 +33,13 @@ describe('DeleteModal.vue', () => {
|
||||
}
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('shallowMount', () => {
|
||||
Wrapper = () => {
|
||||
return shallowMount(DeleteModal, {
|
||||
return shallowMount(ConfirmModal, {
|
||||
propsData,
|
||||
mocks,
|
||||
localVue,
|
||||
@ -61,7 +66,7 @@ describe('DeleteModal.vue', () => {
|
||||
...propsData,
|
||||
type: 'contribution',
|
||||
id: 'p23',
|
||||
name: 'It is a post',
|
||||
name: postName,
|
||||
}
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
@ -72,32 +77,7 @@ describe('DeleteModal.vue', () => {
|
||||
[
|
||||
'delete.contribution.message',
|
||||
{
|
||||
name: 'It is a post',
|
||||
},
|
||||
],
|
||||
]
|
||||
expect(calls).toEqual(expect.arrayContaining(expected))
|
||||
})
|
||||
})
|
||||
|
||||
describe('given a comment', () => {
|
||||
beforeEach(() => {
|
||||
propsData = {
|
||||
...propsData,
|
||||
type: 'comment',
|
||||
id: 'c4',
|
||||
name: 'It is the user of the comment',
|
||||
}
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('mentions comments user name', () => {
|
||||
const calls = mocks.$t.mock.calls
|
||||
const expected = [
|
||||
[
|
||||
'delete.comment.message',
|
||||
{
|
||||
name: 'It is the user of the comment',
|
||||
name: postName,
|
||||
},
|
||||
],
|
||||
]
|
||||
@ -108,7 +88,7 @@ describe('DeleteModal.vue', () => {
|
||||
|
||||
describe('mount', () => {
|
||||
Wrapper = () => {
|
||||
return mount(DeleteModal, {
|
||||
return mount(ConfirmModal, {
|
||||
propsData,
|
||||
mocks,
|
||||
localVue,
|
||||
@ -135,7 +115,7 @@ describe('DeleteModal.vue', () => {
|
||||
})
|
||||
|
||||
it('does call the cancel callback', () => {
|
||||
expect(propsData.callbacks.cancel).toHaveBeenCalledTimes(1)
|
||||
expect(cancelCallback).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('emits "close"', () => {
|
||||
@ -161,10 +141,11 @@ describe('DeleteModal.vue', () => {
|
||||
})
|
||||
|
||||
it('does call the confirm callback', () => {
|
||||
expect(propsData.callbacks.confirm).toHaveBeenCalledTimes(1)
|
||||
expect(confirmCallback).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
it('emits close', () => {
|
||||
expect(wrapper.emitted().close).toBeTruthy()
|
||||
|
||||
it('emits "close"', () => {
|
||||
expect(wrapper.emitted().close).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('resets success', () => {
|
||||
@ -10,10 +10,18 @@
|
||||
<p v-html="message" />
|
||||
|
||||
<template slot="footer">
|
||||
<ds-button class="cancel" icon="close" @click="cancel">{{ $t('delete.cancel') }}</ds-button>
|
||||
<ds-button class="cancel" :icon="modalData.buttons.cancel.icon" @click="cancel">
|
||||
{{ $t(modalData.buttons.cancel.textIdent) }}
|
||||
</ds-button>
|
||||
|
||||
<ds-button danger class="confirm" icon="trash" :loading="loading" @click="confirm">
|
||||
{{ $t('delete.submit') }}
|
||||
<ds-button
|
||||
:danger="modalData.buttons.confirm.danger"
|
||||
class="confirm"
|
||||
:icon="modalData.buttons.confirm.icon"
|
||||
:loading="loading"
|
||||
@click="confirm"
|
||||
>
|
||||
{{ $t(modalData.buttons.confirm.textIdent) }}
|
||||
</ds-button>
|
||||
</template>
|
||||
</ds-modal>
|
||||
@ -23,14 +31,14 @@
|
||||
import { SweetalertIcon } from 'vue-sweetalert-icons'
|
||||
|
||||
export default {
|
||||
name: 'DeleteModal',
|
||||
name: 'ConfirmModal',
|
||||
components: {
|
||||
SweetalertIcon,
|
||||
},
|
||||
props: {
|
||||
name: { type: String, default: '' },
|
||||
type: { type: String, required: true },
|
||||
callbacks: { type: Object, required: true },
|
||||
modalData: { type: Object, required: true },
|
||||
id: { type: String, required: true },
|
||||
},
|
||||
data() {
|
||||
@ -42,18 +50,15 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return this.$t(`delete.${this.type}.title`)
|
||||
return this.$t(this.modalData.titleIdent)
|
||||
},
|
||||
message() {
|
||||
const name = this.$filters.truncate(this.name, 30)
|
||||
return this.$t(`delete.${this.type}.message`, { name })
|
||||
return this.$t(this.modalData.messageIdent, this.modalData.messageParams)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async cancel() {
|
||||
if (this.callbacks.cancel) {
|
||||
await this.callbacks.cancel()
|
||||
}
|
||||
await this.modalData.buttons.cancel.callback()
|
||||
this.isOpen = false
|
||||
setTimeout(() => {
|
||||
this.$emit('close')
|
||||
@ -62,9 +67,7 @@ export default {
|
||||
async confirm() {
|
||||
this.loading = true
|
||||
try {
|
||||
if (this.callbacks.confirm) {
|
||||
await this.callbacks.confirm()
|
||||
}
|
||||
await this.modalData.buttons.confirm.callback()
|
||||
this.success = true
|
||||
setTimeout(() => {
|
||||
this.isOpen = false
|
||||
@ -16,10 +16,6 @@ describe('DisableModal.vue', () => {
|
||||
type: 'contribution',
|
||||
id: 'c42',
|
||||
name: 'blah',
|
||||
callbacks: {
|
||||
confirm: jest.fn(),
|
||||
cancel: jest.fn(),
|
||||
},
|
||||
}
|
||||
mocks = {
|
||||
$filters: {
|
||||
@ -33,8 +29,12 @@ describe('DisableModal.vue', () => {
|
||||
$apollo: {
|
||||
mutate: jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ enable: 'u4711' })
|
||||
.mockRejectedValue({ message: 'Not Authorised!' }),
|
||||
.mockResolvedValueOnce({
|
||||
enable: 'u4711',
|
||||
})
|
||||
.mockRejectedValue({
|
||||
message: 'Not Authorised!',
|
||||
}),
|
||||
},
|
||||
location: {
|
||||
reload: jest.fn(),
|
||||
|
||||
@ -21,7 +21,6 @@ export default {
|
||||
props: {
|
||||
name: { type: String, default: '' },
|
||||
type: { type: String, required: true },
|
||||
callbacks: { type: Object, required: true },
|
||||
id: { type: String, required: true },
|
||||
},
|
||||
data() {
|
||||
@ -42,9 +41,8 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async cancel() {
|
||||
if (this.callbacks.cancel) {
|
||||
await this.callbacks.cancel()
|
||||
}
|
||||
// TODO: Use the "modalData" structure introduced in "ConfirmModal" and refactor this here. Be aware that all the Jest tests have to be refactored as well !!!
|
||||
// await this.modalData.buttons.cancel.callback()
|
||||
this.isOpen = false
|
||||
setTimeout(() => {
|
||||
this.$emit('close')
|
||||
@ -52,9 +50,8 @@ export default {
|
||||
},
|
||||
async confirm() {
|
||||
try {
|
||||
if (this.callbacks.confirm) {
|
||||
await this.callbacks.confirm()
|
||||
}
|
||||
// TODO: Use the "modalData" structure introduced in "ConfirmModal" and refactor this here. Be aware that all the Jest tests have to be refactored as well !!!
|
||||
// await this.modalData.buttons.confirm.callback()
|
||||
await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation($id: ID!) {
|
||||
|
||||
@ -17,10 +17,6 @@ describe('ReportModal.vue', () => {
|
||||
propsData = {
|
||||
type: 'contribution',
|
||||
id: 'c43',
|
||||
callbacks: {
|
||||
confirm: jest.fn(),
|
||||
cancel: jest.fn(),
|
||||
},
|
||||
}
|
||||
mocks = {
|
||||
$t: jest.fn(),
|
||||
|
||||
@ -10,9 +10,7 @@
|
||||
<p v-html="message" />
|
||||
|
||||
<template slot="footer">
|
||||
<ds-button class="cancel" icon="close" @click="cancel">
|
||||
{{ $t('report.cancel') }}
|
||||
</ds-button>
|
||||
<ds-button class="cancel" icon="close" @click="cancel">{{ $t('report.cancel') }}</ds-button>
|
||||
|
||||
<ds-button
|
||||
danger
|
||||
@ -39,7 +37,6 @@ export default {
|
||||
props: {
|
||||
name: { type: String, default: '' },
|
||||
type: { type: String, required: true },
|
||||
callbacks: { type: Object, required: true },
|
||||
id: { type: String, required: true },
|
||||
},
|
||||
data() {
|
||||
@ -60,9 +57,8 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async cancel() {
|
||||
if (this.callbacks.cancel) {
|
||||
await this.callbacks.cancel()
|
||||
}
|
||||
// TODO: Use the "modalData" structure introduced in "ConfirmModal" and refactor this here. Be aware that all the Jest tests have to be refactored as well !!!
|
||||
// await this.modalData.buttons.cancel.callback()
|
||||
this.isOpen = false
|
||||
setTimeout(() => {
|
||||
this.$emit('close')
|
||||
@ -71,9 +67,8 @@ export default {
|
||||
async confirm() {
|
||||
this.loading = true
|
||||
try {
|
||||
if (this.callbacks.confirm) {
|
||||
await this.callbacks.confirm()
|
||||
}
|
||||
// TODO: Use the "modalData" structure introduced in "ConfirmModal" and refactor this here. Be aware that all the Jest tests have to be refactored as well !!!
|
||||
// await this.modalData.buttons.confirm.callback()
|
||||
await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation($id: ID!) {
|
||||
|
||||
130
webapp/components/PostCard/index.spec.js
Normal file
130
webapp/components/PostCard/index.spec.js
Normal file
@ -0,0 +1,130 @@
|
||||
import { config, shallowMount, mount, createLocalVue, RouterLinkStub } from '@vue/test-utils'
|
||||
import Styleguide from '@human-connection/styleguide'
|
||||
import Vuex from 'vuex'
|
||||
import Filters from '~/plugins/vue-filters'
|
||||
import PostCard from '.'
|
||||
|
||||
const localVue = createLocalVue()
|
||||
|
||||
localVue.use(Vuex)
|
||||
localVue.use(Styleguide)
|
||||
localVue.use(Filters)
|
||||
|
||||
config.stubs['no-ssr'] = '<span><slot /></span>'
|
||||
config.stubs['v-popover'] = '<span><slot /></span>'
|
||||
|
||||
describe('PostCard', () => {
|
||||
let store
|
||||
let stubs
|
||||
let mocks
|
||||
let propsData
|
||||
let getters
|
||||
let Wrapper
|
||||
let wrapper
|
||||
|
||||
beforeEach(() => {
|
||||
propsData = {
|
||||
post: {
|
||||
id: 'p23',
|
||||
name: 'It is a post',
|
||||
author: {
|
||||
id: 'u1',
|
||||
},
|
||||
disabled: false,
|
||||
},
|
||||
}
|
||||
store = new Vuex.Store({
|
||||
getters: {
|
||||
'auth/user': () => {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
})
|
||||
stubs = {
|
||||
NuxtLink: RouterLinkStub,
|
||||
}
|
||||
mocks = {
|
||||
$t: jest.fn(),
|
||||
$toast: {
|
||||
success: jest.fn(),
|
||||
error: jest.fn(),
|
||||
},
|
||||
$apollo: {
|
||||
mutate: jest.fn().mockResolvedValue(),
|
||||
},
|
||||
}
|
||||
getters = {
|
||||
'auth/user': () => {
|
||||
return {}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
describe('shallowMount', () => {
|
||||
Wrapper = () => {
|
||||
return shallowMount(PostCard, {
|
||||
store,
|
||||
propsData,
|
||||
mocks,
|
||||
localVue,
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(jest.useFakeTimers)
|
||||
|
||||
describe('test Post callbacks', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
describe('deletion of Post from Page by invoking "deletePostCallback()"', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.vm.deletePostCallback()
|
||||
})
|
||||
|
||||
describe('after timeout', () => {
|
||||
beforeEach(jest.runAllTimers)
|
||||
|
||||
it('does call mutation', () => {
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('mutation is successful', () => {
|
||||
expect(mocks.$toast.success).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('emits "removePostFromList"', () => {
|
||||
expect(wrapper.emitted().removePostFromList).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('mount', () => {
|
||||
Wrapper = () => {
|
||||
const store = new Vuex.Store({
|
||||
getters,
|
||||
})
|
||||
return mount(PostCard, {
|
||||
stubs,
|
||||
mocks,
|
||||
propsData,
|
||||
store,
|
||||
localVue,
|
||||
})
|
||||
}
|
||||
|
||||
describe('given a post', () => {
|
||||
beforeEach(() => {
|
||||
propsData.post = {
|
||||
title: "It's a title",
|
||||
}
|
||||
})
|
||||
|
||||
it('renders title', () => {
|
||||
expect(Wrapper().text()).toContain("It's a title")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -21,9 +21,7 @@
|
||||
</div>
|
||||
<ds-space margin-bottom="small" />
|
||||
<!-- Post Title -->
|
||||
<ds-heading tag="h3" no-margin>
|
||||
{{ post.title }}
|
||||
</ds-heading>
|
||||
<ds-heading tag="h3" no-margin>{{ post.title }}</ds-heading>
|
||||
<ds-space margin-bottom="small" />
|
||||
<!-- Post Content Excerpt -->
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
@ -58,7 +56,7 @@
|
||||
<content-menu
|
||||
resource-type="contribution"
|
||||
:resource="post"
|
||||
:callbacks="{ confirm: deletePostCallback, cancel: null }"
|
||||
:modalsData="menuModalsData"
|
||||
:is-owner="isAuthor"
|
||||
/>
|
||||
</div>
|
||||
@ -75,7 +73,7 @@ import HcCategory from '~/components/Category'
|
||||
import HcRibbon from '~/components/Ribbon'
|
||||
// import { randomBytes } from 'crypto'
|
||||
import { mapGetters } from 'vuex'
|
||||
import PostMutationHelpers from '~/mixins/PostMutationHelpers'
|
||||
import { postMenuModalsData, deletePostMutation } from '~/components/utils/PostHelpers'
|
||||
|
||||
export default {
|
||||
name: 'HcPostCard',
|
||||
@ -85,7 +83,6 @@ export default {
|
||||
HcRibbon,
|
||||
ContentMenu,
|
||||
},
|
||||
mixins: [PostMutationHelpers],
|
||||
props: {
|
||||
post: {
|
||||
type: Object,
|
||||
@ -108,6 +105,24 @@ export default {
|
||||
if (!author) return false
|
||||
return this.user.id === this.post.author.id
|
||||
},
|
||||
menuModalsData() {
|
||||
return postMenuModalsData(
|
||||
// "this.post" may not always be defined at the beginning …
|
||||
this.post ? this.$filters.truncate(this.post.title, 30) : '',
|
||||
this.deletePostCallback,
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async deletePostCallback() {
|
||||
try {
|
||||
await this.$apollo.mutate(deletePostMutation(this.post.id))
|
||||
this.$toast.success(this.$t('delete.contribution.success'))
|
||||
this.$emit('removePostFromList')
|
||||
} catch (err) {
|
||||
this.$toast.error(err.message)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -1,61 +0,0 @@
|
||||
import { config, mount, createLocalVue, RouterLinkStub } from '@vue/test-utils'
|
||||
import PostCard from '.'
|
||||
import Styleguide from '@human-connection/styleguide'
|
||||
import Vuex from 'vuex'
|
||||
import Filters from '~/plugins/vue-filters'
|
||||
|
||||
const localVue = createLocalVue()
|
||||
|
||||
localVue.use(Vuex)
|
||||
localVue.use(Styleguide)
|
||||
localVue.use(Filters)
|
||||
|
||||
config.stubs['no-ssr'] = '<span><slot /></span>'
|
||||
config.stubs['v-popover'] = '<span><slot /></span>'
|
||||
|
||||
describe('PostCard', () => {
|
||||
let stubs
|
||||
let mocks
|
||||
let propsData
|
||||
let getters
|
||||
|
||||
beforeEach(() => {
|
||||
propsData = {}
|
||||
stubs = {
|
||||
NuxtLink: RouterLinkStub,
|
||||
}
|
||||
mocks = {
|
||||
$t: jest.fn(),
|
||||
}
|
||||
getters = {
|
||||
'auth/user': () => {
|
||||
return {}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const Wrapper = () => {
|
||||
const store = new Vuex.Store({
|
||||
getters,
|
||||
})
|
||||
return mount(PostCard, {
|
||||
stubs,
|
||||
mocks,
|
||||
propsData,
|
||||
store,
|
||||
localVue,
|
||||
})
|
||||
}
|
||||
|
||||
describe('given a post', () => {
|
||||
beforeEach(() => {
|
||||
propsData.post = {
|
||||
title: "It's a title",
|
||||
}
|
||||
})
|
||||
|
||||
it('renders title', () => {
|
||||
expect(Wrapper().text()).toContain("It's a title")
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -40,6 +40,8 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
cancel() {
|
||||
// TODO: Use the "modalData" structure introduced in "ConfirmModal" and refactor this here. Be aware that all the Jest tests have to be refactored as well !!!
|
||||
// await this.modalData.buttons.cancel.callback()
|
||||
this.isOpen = false
|
||||
setTimeout(() => {
|
||||
this.$emit('close')
|
||||
@ -47,6 +49,8 @@ export default {
|
||||
},
|
||||
async confirm() {
|
||||
try {
|
||||
// TODO: Use the "modalData" structure introduced in "ConfirmModal" and refactor this here. Be aware that all the Jest tests have to be refactored as well !!!
|
||||
// await this.modalData.buttons.confirm.callback()
|
||||
await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation($id: ID!) {
|
||||
|
||||
@ -24,7 +24,9 @@ describe('CommentList.vue', () => {
|
||||
let data
|
||||
|
||||
propsData = {
|
||||
post: { id: 1 },
|
||||
post: {
|
||||
id: 1,
|
||||
},
|
||||
}
|
||||
store = new Vuex.Store({
|
||||
getters: {
|
||||
@ -35,6 +37,9 @@ describe('CommentList.vue', () => {
|
||||
})
|
||||
mocks = {
|
||||
$t: jest.fn(),
|
||||
$filters: {
|
||||
truncate: a => a,
|
||||
},
|
||||
$apollo: {
|
||||
queries: {
|
||||
Post: {
|
||||
@ -51,13 +56,24 @@ describe('CommentList.vue', () => {
|
||||
|
||||
describe('shallowMount', () => {
|
||||
const Wrapper = () => {
|
||||
return mount(CommentList, { store, mocks, localVue, propsData, data })
|
||||
return mount(CommentList, {
|
||||
store,
|
||||
mocks,
|
||||
localVue,
|
||||
propsData,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
wrapper.setData({
|
||||
comments: [{ id: 'c1', contentExcerpt: 'this is a comment' }],
|
||||
comments: [
|
||||
{
|
||||
id: 'c1',
|
||||
contentExcerpt: 'this is a comment',
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
35
webapp/components/utils/PostHelpers.js
Normal file
35
webapp/components/utils/PostHelpers.js
Normal file
@ -0,0 +1,35 @@
|
||||
import PostMutations from '~/graphql/PostMutations.js'
|
||||
|
||||
export function postMenuModalsData(truncatedPostName, confirmCallback, cancelCallback = () => {}) {
|
||||
return {
|
||||
delete: {
|
||||
titleIdent: 'delete.contribution.title',
|
||||
messageIdent: 'delete.contribution.message',
|
||||
messageParams: {
|
||||
name: truncatedPostName,
|
||||
},
|
||||
buttons: {
|
||||
confirm: {
|
||||
danger: true,
|
||||
icon: 'trash',
|
||||
textIdent: 'delete.submit',
|
||||
callback: confirmCallback,
|
||||
},
|
||||
cancel: {
|
||||
icon: 'close',
|
||||
textIdent: 'delete.cancel',
|
||||
callback: cancelCallback,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function deletePostMutation(postId) {
|
||||
return {
|
||||
mutation: PostMutations().DeletePost,
|
||||
variables: {
|
||||
id: postId,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export default app => {
|
||||
export default () => {
|
||||
return {
|
||||
CreatePost: gql(`
|
||||
CreatePost: gql`
|
||||
mutation($title: String!, $content: String!, $language: String) {
|
||||
CreatePost(title: $title, content: $content, language: $language) {
|
||||
id
|
||||
@ -13,8 +13,8 @@ export default app => {
|
||||
language
|
||||
}
|
||||
}
|
||||
`),
|
||||
UpdatePost: gql(`
|
||||
`,
|
||||
UpdatePost: gql`
|
||||
mutation($id: ID!, $title: String!, $content: String!, $language: String) {
|
||||
UpdatePost(id: $id, title: $title, content: $content, language: $language) {
|
||||
id
|
||||
@ -25,6 +25,13 @@ export default app => {
|
||||
language
|
||||
}
|
||||
}
|
||||
`),
|
||||
`,
|
||||
DeletePost: gql`
|
||||
mutation($id: ID!) {
|
||||
DeletePost(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,7 +245,7 @@
|
||||
"comment": {
|
||||
"title": "Lösche Kommentar",
|
||||
"type": "Comment",
|
||||
"message": "Bist du sicher, dass du den Kommentar von \"<b>{name}</b>\" löschen möchtest?",
|
||||
"message": "Bist du sicher, dass du den Kommentar \"<b>{name}</b>\" löschen möchtest?",
|
||||
"success": "Kommentar erfolgreich gelöscht!"
|
||||
}
|
||||
},
|
||||
|
||||
@ -245,7 +245,7 @@
|
||||
"comment": {
|
||||
"title": "Delete Comment",
|
||||
"type": "Comment",
|
||||
"message": "Do you really want to delete the comment from \"<b>{name}</b>\"?",
|
||||
"message": "Do you really want to delete the comment \"<b>{name}</b>\"?",
|
||||
"success": "Comment successfully deleted!"
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
async deletePostCallback(postDisplayType = 'list') {
|
||||
// console.log('inside "deletePostCallback" !!! ', this.post)
|
||||
try {
|
||||
var gqlMutation = gql`
|
||||
mutation($id: ID!) {
|
||||
DeletePost(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
await this.$apollo.mutate({
|
||||
mutation: gqlMutation,
|
||||
variables: {
|
||||
id: this.post.id,
|
||||
},
|
||||
})
|
||||
this.$toast.success(this.$t('delete.contribution.success'))
|
||||
// console.log('called "this.$t" !!!')
|
||||
switch (postDisplayType) {
|
||||
case 'list':
|
||||
this.$emit('deletePost')
|
||||
// console.log('emitted "deletePost" !!!')
|
||||
break
|
||||
default:
|
||||
// case 'page'
|
||||
// console.log('called "this.$router.history.push" !!!')
|
||||
this.$router.history.push('/') // Single page type: Redirect to index (main) page
|
||||
break
|
||||
}
|
||||
} catch (err) {
|
||||
this.$toast.error(err.message)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -9,7 +9,7 @@
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
:width="{ base: '100%', xs: '100%', md: '50%', xl: '33%' }"
|
||||
@deletePost="deletePost(index, post.id)"
|
||||
@removePostFromList="deletePost(index, post.id)"
|
||||
/>
|
||||
</ds-flex>
|
||||
<no-ssr>
|
||||
|
||||
@ -56,7 +56,7 @@ describe('PostSlug', () => {
|
||||
|
||||
beforeEach(jest.useFakeTimers)
|
||||
|
||||
describe('test mixin "PostMutationHelpers"', () => {
|
||||
describe('test Post callbacks', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
wrapper.setData({
|
||||
@ -70,22 +70,14 @@ describe('PostSlug', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('deletion of Post from Page by invoking "deletePostCallback(`page`)"', () => {
|
||||
describe('deletion of Post from Page by invoking "deletePostCallback()"', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.vm.deletePostCallback('page')
|
||||
wrapper.vm.deletePostCallback()
|
||||
})
|
||||
|
||||
describe('after timeout', () => {
|
||||
beforeEach(jest.runAllTimers)
|
||||
|
||||
it('not emits "deletePost"', () => {
|
||||
expect(wrapper.emitted().deletePost).toBeFalsy()
|
||||
})
|
||||
|
||||
it('does go to index (main) page', () => {
|
||||
expect(mocks.$router.history.push).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('does call mutation', () => {
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
@ -93,6 +85,10 @@ describe('PostSlug', () => {
|
||||
it('mutation is successful', () => {
|
||||
expect(mocks.$toast.success).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('does go to index (main) page', () => {
|
||||
expect(mocks.$router.history.push).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -7,12 +7,13 @@
|
||||
>
|
||||
<ds-space margin-bottom="small" />
|
||||
<hc-user :user="post.author" :date-time="post.createdAt" />
|
||||
<!-- Content Menu (can open Modals) -->
|
||||
<no-ssr>
|
||||
<content-menu
|
||||
placement="bottom-end"
|
||||
resource-type="contribution"
|
||||
:resource="post"
|
||||
:callbacks="{ confirm: () => deletePostCallback('page'), cancel: null }"
|
||||
:modalsData="menuModalsData"
|
||||
:is-owner="isAuthor(post.author ? post.author.id : null)"
|
||||
/>
|
||||
</no-ssr>
|
||||
@ -70,7 +71,7 @@ import HcUser from '~/components/User'
|
||||
import HcShoutButton from '~/components/ShoutButton.vue'
|
||||
import HcCommentForm from '~/components/comments/CommentForm'
|
||||
import HcCommentList from '~/components/comments/CommentList'
|
||||
import PostMutationHelpers from '~/mixins/PostMutationHelpers'
|
||||
import { postMenuModalsData, deletePostMutation } from '~/components/utils/PostHelpers'
|
||||
|
||||
export default {
|
||||
name: 'PostSlug',
|
||||
@ -87,7 +88,6 @@ export default {
|
||||
HcCommentForm,
|
||||
HcCommentList,
|
||||
},
|
||||
mixins: [PostMutationHelpers],
|
||||
head() {
|
||||
return {
|
||||
title: this.title,
|
||||
@ -209,10 +209,28 @@ export default {
|
||||
this.ready = true
|
||||
}, 50)
|
||||
},
|
||||
computed: {
|
||||
menuModalsData() {
|
||||
return postMenuModalsData(
|
||||
// "this.post" may not always be defined at the beginning …
|
||||
this.post ? this.$filters.truncate(this.post.title, 30) : '',
|
||||
this.deletePostCallback,
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isAuthor(id) {
|
||||
return this.$store.getters['auth/user'].id === id
|
||||
},
|
||||
async deletePostCallback() {
|
||||
try {
|
||||
await this.$apollo.mutate(deletePostMutation(this.post.id))
|
||||
this.$toast.success(this.$t('delete.contribution.success'))
|
||||
this.$router.history.push('/') // Redirect to index (main) page
|
||||
} catch (err) {
|
||||
this.$toast.error(err.message)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
<template>
|
||||
<ds-card>
|
||||
<h2 style="margin-bottom: .2em;">
|
||||
Mehr Informationen
|
||||
</h2>
|
||||
<h2 style="margin-bottom: .2em;">Mehr Informationen</h2>
|
||||
<p>Hier findest du weitere infos zum Thema.</p>
|
||||
<ds-space />
|
||||
<h3>
|
||||
@ -42,7 +40,7 @@
|
||||
:key="relatedPost.id"
|
||||
:post="relatedPost"
|
||||
:width="{ base: '100%', lg: 1 }"
|
||||
@deletePost="post.relatedContributions.splice(index, 1)"
|
||||
@removePostFromList="post.relatedContributions.splice(index, 1)"
|
||||
/>
|
||||
</ds-flex>
|
||||
<hc-empty v-else margin="large" icon="file" message="No related Posts" />
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { config, mount, shallowMount, createLocalVue } from '@vue/test-utils'
|
||||
import { config, mount, createLocalVue } from '@vue/test-utils'
|
||||
import ProfileSlug from './_slug.vue'
|
||||
import Vuex from 'vuex'
|
||||
import Styleguide from '@human-connection/styleguide'
|
||||
@ -50,48 +50,6 @@ describe('ProfileSlug', () => {
|
||||
}
|
||||
})
|
||||
|
||||
describe('shallowMount', () => {
|
||||
Wrapper = () => {
|
||||
return shallowMount(ProfileSlug, {
|
||||
mocks,
|
||||
localVue,
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(jest.useFakeTimers)
|
||||
|
||||
describe('test mixin "PostMutationHelpers"', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
describe('deletion of Post from List by invoking "deletePostCallback(`list`)"', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.vm.deletePostCallback('list')
|
||||
})
|
||||
|
||||
describe('after timeout', () => {
|
||||
beforeEach(jest.runAllTimers)
|
||||
|
||||
it('emits "deletePost"', () => {
|
||||
expect(wrapper.emitted().deletePost).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('does not go to index (main) page', () => {
|
||||
expect(mocks.$router.history.push).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does call mutation', () => {
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('mutation is successful', () => {
|
||||
expect(mocks.$toast.success).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
describe('mount', () => {
|
||||
Wrapper = () => {
|
||||
return mount(ProfileSlug, {
|
||||
@ -104,6 +62,7 @@ describe('ProfileSlug', () => {
|
||||
beforeEach(() => {
|
||||
mocks.$filters = {
|
||||
removeLinks: c => c,
|
||||
truncate: a => a,
|
||||
}
|
||||
mocks.$store = {
|
||||
getters: {
|
||||
@ -170,7 +129,10 @@ describe('ProfileSlug', () => {
|
||||
describe('pagination returned less posts than available', () => {
|
||||
beforeEach(() => {
|
||||
const posts = [1, 2, 3, 4, 5].map(id => {
|
||||
return { ...aPost, id }
|
||||
return {
|
||||
...aPost,
|
||||
id,
|
||||
}
|
||||
})
|
||||
|
||||
wrapper.setData({
|
||||
@ -200,7 +162,10 @@ describe('ProfileSlug', () => {
|
||||
describe('pagination returned as many posts as available', () => {
|
||||
beforeEach(() => {
|
||||
const posts = [1, 2, 3, 4, 5, 6].map(id => {
|
||||
return { ...aPost, id }
|
||||
return {
|
||||
...aPost,
|
||||
id,
|
||||
}
|
||||
})
|
||||
|
||||
wrapper.setData({
|
||||
|
||||
@ -12,12 +12,12 @@
|
||||
>
|
||||
<hc-upload v-if="myProfile" :user="user" />
|
||||
<hc-avatar v-else :user="user" class="profile-avatar" size="x-large" />
|
||||
<!-- Menu -->
|
||||
<no-ssr>
|
||||
<content-menu
|
||||
placement="bottom-end"
|
||||
resource-type="user"
|
||||
:resource="user"
|
||||
:callbacks="{ confirm: deletePostCallback, cancel: null }"
|
||||
:is-owner="myProfile"
|
||||
class="user-content-menu"
|
||||
/>
|
||||
@ -204,7 +204,7 @@
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
:width="{ base: '100%', md: '100%', xl: '50%' }"
|
||||
@deletePost="Post.splice(index, 1)"
|
||||
@removePostFromList="activePosts.splice(index, 1)"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="$apollo.loading">
|
||||
@ -238,9 +238,9 @@ import HcEmpty from '~/components/Empty.vue'
|
||||
import ContentMenu from '~/components/ContentMenu'
|
||||
import HcUpload from '~/components/Upload'
|
||||
import HcAvatar from '~/components/Avatar/Avatar.vue'
|
||||
import PostMutationHelpers from '~/mixins/PostMutationHelpers'
|
||||
import PostQuery from '~/graphql/UserProfile/Post.js'
|
||||
import UserQuery from '~/graphql/UserProfile/User.js'
|
||||
|
||||
const tabToFilterMapping = ({ tab, id }) => {
|
||||
return {
|
||||
post: { author: { id } },
|
||||
@ -248,7 +248,9 @@ const tabToFilterMapping = ({ tab, id }) => {
|
||||
shout: { shoutedBy_some: { id } },
|
||||
}[tab]
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'HcUserProfile',
|
||||
components: {
|
||||
User,
|
||||
HcPostCard,
|
||||
@ -261,7 +263,6 @@ export default {
|
||||
ContentMenu,
|
||||
HcUpload,
|
||||
},
|
||||
mixins: [PostMutationHelpers],
|
||||
transition: {
|
||||
name: 'slide-up',
|
||||
mode: 'out-in',
|
||||
@ -271,6 +272,7 @@ export default {
|
||||
return {
|
||||
User: [],
|
||||
Post: [],
|
||||
activePosts: [],
|
||||
voted: false,
|
||||
page: 1,
|
||||
pageSize: 6,
|
||||
@ -300,12 +302,6 @@ export default {
|
||||
offset() {
|
||||
return (this.page - 1) * this.pageSize
|
||||
},
|
||||
activePosts() {
|
||||
if (!this.Post) {
|
||||
return []
|
||||
}
|
||||
return this.uniq(this.Post.filter(post => !post.deleted))
|
||||
},
|
||||
socialMediaLinks() {
|
||||
const { socialMedia = [] } = this.user
|
||||
return socialMedia.map(socialMedia => {
|
||||
@ -328,6 +324,9 @@ export default {
|
||||
throw new Error('User not found!')
|
||||
}
|
||||
},
|
||||
Post(val) {
|
||||
this.activePosts = this.setActivePosts()
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleTab(tab) {
|
||||
@ -361,6 +360,12 @@ export default {
|
||||
fetchPolicy: 'cache-and-network',
|
||||
})
|
||||
},
|
||||
setActivePosts() {
|
||||
if (!this.Post) {
|
||||
return []
|
||||
}
|
||||
return this.uniq(this.Post.filter(post => !post.deleted))
|
||||
},
|
||||
},
|
||||
apollo: {
|
||||
Post: {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user