Start setting up frontend pinning/unpinning

- Add pin/unpin post to content menu
- Update apollo cache to reactively unpin
- Update apollo cache in root path to re-order Posts
- Order with pinned post first
- Start setting up filters, so that the pinned post is always the first
post visible
This commit is contained in:
mattwr18 2019-10-16 23:03:45 +02:00
parent 36f6be9e36
commit f871df02ae
9 changed files with 170 additions and 30 deletions

View File

@ -55,24 +55,46 @@ export default {
routes() {
let routes = []
if (this.isOwner && this.resourceType === 'contribution') {
routes.push({
name: this.$t(`post.menu.edit`),
path: this.$router.resolve({
name: 'post-edit-id',
params: {
id: this.resource.id,
if (this.resourceType === 'contribution') {
if (this.isOwner) {
routes.push({
name: this.$t(`post.menu.edit`),
path: this.$router.resolve({
name: 'post-edit-id',
params: {
id: this.resource.id,
},
}).href,
icon: 'edit',
})
routes.push({
name: this.$t(`post.menu.delete`),
callback: () => {
this.openModal('delete')
},
}).href,
icon: 'edit',
})
routes.push({
name: this.$t(`post.menu.delete`),
callback: () => {
this.openModal('delete')
},
icon: 'trash',
})
icon: 'trash',
})
}
if (this.isAdmin) {
if (!this.resource.pinnedBy) {
routes.push({
name: this.$t(`post.menu.pin`),
callback: () => {
this.$emit('pinPost', this.resource)
},
icon: 'link',
})
} else {
routes.push({
name: this.$t(`post.menu.unpin`),
callback: () => {
this.$emit('unpinPost', this.resource)
},
icon: 'unlink',
})
}
}
}
if (this.isOwner && this.resourceType === 'comment') {
@ -155,6 +177,9 @@ export default {
isModerator() {
return this.$store.getters['auth/isModerator']
},
isAdmin() {
return this.$store.getters['auth/isAdmin']
},
},
methods: {
openItem(route, toggleMenu) {
@ -175,6 +200,7 @@ export default {
},
})
},
pinPost() {},
},
}
</script>

View File

@ -61,6 +61,8 @@
:resource="post"
:modalsData="menuModalsData"
:is-owner="isAuthor"
@pinPost="pinPost"
@unpinPost="unpinPost"
/>
</div>
</client-only>
@ -127,6 +129,12 @@ export default {
this.$toast.error(err.message)
}
},
pinPost(post) {
this.$emit('pinPost', post)
},
unpinPost(post) {
this.$emit('unpinPost', post)
},
},
}
</script>

View File

@ -57,6 +57,12 @@ export const postFragment = lang => gql`
name
icon
}
pinnedBy {
id
name
role
}
pinnedAt
}
`
export const commentFragment = lang => gql`

View File

@ -34,6 +34,8 @@ export default () => {
$imageUpload: Upload
$categoryIds: [ID]
$image: String
$pinned: Boolean
$unpinned: Boolean
) {
UpdatePost(
id: $id
@ -43,6 +45,8 @@ export default () => {
imageUpload: $imageUpload
categoryIds: $categoryIds
image: $image
pinned: $pinned
unpinned: $unpinned
) {
id
title
@ -50,6 +54,11 @@ export default () => {
content
contentExcerpt
language
pinnedBy {
id
name
role
}
}
}
`,

View File

@ -368,7 +368,11 @@
},
"menu": {
"edit": "Edit Post",
"delete": "Delete Post"
"delete": "Delete Post",
"pin": "Pin post",
"pinnedSuccessfully": "Post pinned successfully!",
"unpin": "Unpin post",
"unpinnedSuccessfully": "Post unpinned successfully!"
},
"comment": {
"submit": "Comment",

View File

@ -21,6 +21,8 @@
:post="post"
:width="{ base: '100%', xs: '100%', md: '50%', xl: '33%' }"
@removePostFromList="deletePost"
@pinPost="pinPost"
@unpinPost="unpinPost"
/>
</masonry-grid-item>
</template>
@ -64,6 +66,7 @@ import MasonryGrid from '~/components/MasonryGrid/MasonryGrid.vue'
import MasonryGridItem from '~/components/MasonryGrid/MasonryGridItem.vue'
import { mapGetters } from 'vuex'
import { filterPosts } from '~/graphql/PostQuery.js'
import PostMutations from '~/graphql/PostMutations'
export default {
components: {
@ -166,6 +169,37 @@ export default {
return post.id !== deletedPost.id
})
},
resetPostList() {
this.offset = 0
this.posts = []
this.hasMore = true
},
pinPost(post) {
this.$apollo
.mutate({
mutation: PostMutations().UpdatePost,
variables: { id: post.id, title: post.title, content: post.content, pinned: true },
})
.then(() => {
this.$toast.success(this.$t('post.menu.pinnedSuccessfully'))
this.resetPostList()
this.$apollo.queries.Post.refetch()
})
.catch(error => this.$toast.error(error.message))
},
unpinPost(post) {
this.$apollo
.mutate({
mutation: PostMutations().UpdatePost,
variables: { id: post.id, title: post.title, content: post.content, unpinned: true },
})
.then(() => {
this.$toast.success(this.$t('post.menu.unpinnedSuccessfully'))
this.resetPostList()
this.$apollo.queries.Post.refetch()
})
.catch(error => this.$toast.error(error.message))
},
},
apollo: {
Post: {
@ -176,7 +210,7 @@ export default {
return {
filter: this.finalFilters,
first: this.pageSize,
orderBy: this.sorting,
orderBy: ['pinnedAt_asc', this.sorting],
offset: 0,
}
},

View File

@ -18,6 +18,8 @@
:resource="post"
:modalsData="menuModalsData"
:is-owner="isAuthor(post.author ? post.author.id : null)"
@pinPost="pinPost"
@unpinPost="unpinPost"
/>
</client-only>
<ds-space margin-bottom="small" />
@ -77,6 +79,7 @@
</template>
<script>
import { mapMutations } from 'vuex'
import ContentViewer from '~/components/Editor/ContentViewer'
import HcCategory from '~/components/Category'
import HcHashtag from '~/components/Hashtag/Hashtag'
@ -88,6 +91,7 @@ import HcCommentList from '~/components/CommentList/CommentList'
import { postMenuModalsData, deletePostMutation } from '~/components/utils/PostHelpers'
import PostQuery from '~/graphql/PostQuery'
import HcEmotions from '~/components/Emotions/Emotions'
import PostMutations from '~/graphql/PostMutations'
export default {
name: 'PostSlug',
@ -141,6 +145,9 @@ export default {
},
},
methods: {
...mapMutations({
setCurrentUser: 'auth/SET_USER',
}),
isAuthor(id) {
return this.$store.getters['auth/user'].id === id
},
@ -156,6 +163,33 @@ export default {
async createComment(comment) {
this.post.comments.push(comment)
},
resetPostList() {
this.offset = 0
this.posts = []
this.hasMore = true
},
pinPost(post) {
this.$apollo
.mutate({
mutation: PostMutations().UpdatePost,
variables: { id: post.id, title: post.title, content: post.content, pinned: true },
})
.then(() => {
this.$toast.success(this.$t('post.menu.pinnedSuccessfully'))
})
.catch(error => this.$toast.error(error.message))
},
unpinPost(post) {
this.$apollo
.mutate({
mutation: PostMutations().UpdatePost,
variables: { id: post.id, title: post.title, content: post.content, unpinned: true },
})
.then(() => {
this.$toast.success(this.$t('post.menu.unpinnedSuccessfully'))
})
.catch(error => this.$toast.error(error.message))
},
},
apollo: {
Post: {

View File

@ -444,7 +444,7 @@ export default {
filter: this.filter,
first: this.pageSize,
offset: 0,
orderBy: 'createdAt_desc',
orderBy: ['pinnedAt_asc', 'createdAt_desc'],
}
},
update({ Post }) {

View File

@ -10,23 +10,31 @@ const defaultFilter = {}
export const state = () => {
return {
filter: {
...defaultFilter,
OR: [{ pinnedBy_in: { role_in: ['admin'] } }, {}],
},
}
}
export const mutations = {
TOGGLE_FILTER_BY_FOLLOWED(state, currentUserId) {
const filter = clone(state.filter)
const id = get(filter, 'author.followedBy_some.id')
let filter = clone(state.filter)
const id = filter.OR.find(object => object.author)
if (id) {
delete filter.author
filter.OR.forEach(object => delete object.author)
state.filter = filter
} else {
state.filter = {
...filter,
if (isEmpty(filter.OR[-1])) filter.OR.pop()
filter.OR.map(object => {
for (let key in object) {
if (object.hasOwnProperty(key)) {
object = { key: object[key] }
}
}
})
filter.OR.unshift({
author: { followedBy_some: { id: currentUserId } },
}
})
state.filter = filter
}
},
RESET_CATEGORIES(state) {
@ -35,9 +43,20 @@ export const mutations = {
state.filter = filter
},
TOGGLE_CATEGORY(state, categoryId) {
const filter = clone(state.filter)
update(filter, 'categories_some.id_in', categoryIds => xor(categoryIds, [categoryId]))
if (isEmpty(get(filter, 'categories_some.id_in'))) delete filter.categories_some
let filter = clone(state.filter)
if (isEmpty(filter.OR[-1])) filter.OR.pop()
filter.OR.map(object => {
for (let key in object) {
if (object.hasOwnProperty(key)) {
object = { key: object[key] }
}
}
})
filter.OR.unshift({
categories_some: { id_in: [categoryId] },
})
// update(filter, 'categories_some.id_in', categoryIds => xor(categoryIds, [categoryId]))
// if (isEmpty(get(filter.OR[0], 'categories_some.id_in'))) delete filter.OR[0].categories_some
state.filter = filter
},
TOGGLE_EMOTION(state, emotion) {