Add content block deletion mutation
This commit is contained in:
parent
4d1e156f9f
commit
ce86e45743
|
|
@ -9,6 +9,7 @@
|
||||||
<add-content-block-button :parent="chapter.id"></add-content-block-button>
|
<add-content-block-button :parent="chapter.id"></add-content-block-button>
|
||||||
|
|
||||||
<content-block :contentBlock="contentBlock"
|
<content-block :contentBlock="contentBlock"
|
||||||
|
:parent="chapter.id"
|
||||||
:key="contentBlock.id" v-for="contentBlock in filteredContentBlocks">
|
:key="contentBlock.id" v-for="contentBlock in filteredContentBlocks">
|
||||||
</content-block>
|
</content-block>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="content-block__container">
|
<div class="content-block__container">
|
||||||
<div class="content-block" :class="specialClass">
|
<div class="content-block" :class="specialClass">
|
||||||
<div class="content-block__actions">
|
<div class="content-block__actions">
|
||||||
<a @click="toggleVisibility()" v-if="canManageContent" class="content-block__visibility-button">
|
<a @click="toggleVisibility()" v-if="canManageContent" class="content-block__action-button">
|
||||||
<eye-icon class="content-block__action-icon"></eye-icon>
|
<eye-icon class="content-block__action-icon"></eye-icon>
|
||||||
</a>
|
</a>
|
||||||
<visibility-popover
|
<visibility-popover
|
||||||
|
|
@ -10,9 +10,12 @@
|
||||||
:content-block="contentBlock"
|
:content-block="contentBlock"
|
||||||
class="content-block__visibility-menu"
|
class="content-block__visibility-menu"
|
||||||
></visibility-popover>
|
></visibility-popover>
|
||||||
<a @click="editContentBlock()" v-if="contentBlock.userCreated">
|
<a @click="editContentBlock()" v-if="contentBlock.mine" class="content-block__action-button">
|
||||||
<pen-icon class="content-block__action-icon"></pen-icon>
|
<pen-icon class="content-block__action-icon"></pen-icon>
|
||||||
</a>
|
</a>
|
||||||
|
<a @click="deleteContentBlock(contentBlock.id)" v-if="contentBlock.mine" class="content-block__action-button">
|
||||||
|
<trash-icon class="content-block__action-icon"></trash-icon>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h4 class="content-block__title">{{contentBlock.title}} {{contentBlock.userCreated}}</h4>
|
<h4 class="content-block__title">{{contentBlock.title}} {{contentBlock.userCreated}}</h4>
|
||||||
|
|
@ -45,10 +48,13 @@
|
||||||
import VisibilityPopover from '@/components/VisibilityPopover';
|
import VisibilityPopover from '@/components/VisibilityPopover';
|
||||||
import EyeIcon from '@/components/icons/EyeIcon';
|
import EyeIcon from '@/components/icons/EyeIcon';
|
||||||
import PenIcon from '@/components/icons/PenIcon';
|
import PenIcon from '@/components/icons/PenIcon';
|
||||||
|
import TrashIcon from '@/components/icons/TrashIcon';
|
||||||
import ME_QUERY from '@/graphql/gql/meQuery.gql';
|
import ME_QUERY from '@/graphql/gql/meQuery.gql';
|
||||||
|
import CHAPTER_QUERY from '@/graphql/gql/chapterQuery.gql';
|
||||||
|
import DELETE_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/deleteContentBlock.gql';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['contentBlock'],
|
props: ['contentBlock', 'parent'],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
'text_block': TextBlock,
|
'text_block': TextBlock,
|
||||||
|
|
@ -63,7 +69,8 @@
|
||||||
AddContentBlockButton,
|
AddContentBlockButton,
|
||||||
VisibilityPopover,
|
VisibilityPopover,
|
||||||
EyeIcon,
|
EyeIcon,
|
||||||
PenIcon
|
PenIcon,
|
||||||
|
TrashIcon
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -82,6 +89,32 @@
|
||||||
editContentBlock() {
|
editContentBlock() {
|
||||||
this.$store.dispatch('editContentBlock', this.contentBlock.id);
|
this.$store.dispatch('editContentBlock', this.contentBlock.id);
|
||||||
},
|
},
|
||||||
|
deleteContentBlock(id) {
|
||||||
|
const parent = this.parent;
|
||||||
|
this.$apollo.mutate({
|
||||||
|
mutation: DELETE_CONTENT_BLOCK_MUTATION,
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
id: id
|
||||||
|
}
|
||||||
|
},
|
||||||
|
update(store, {data: {deleteContentBlock: {success}}}) {
|
||||||
|
try {
|
||||||
|
if (success) {
|
||||||
|
const query = CHAPTER_QUERY;
|
||||||
|
const variables = {
|
||||||
|
id: parent
|
||||||
|
};
|
||||||
|
const data = store.readQuery({query, variables});
|
||||||
|
data.chapter.contentBlocks.edges.splice(data.chapter.contentBlocks.edges.findIndex(edge => edge.node.id === id), 1);
|
||||||
|
store.writeQuery({query, variables, data});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Query did not exist in the cache, and apollo throws a generic Error. Do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
apollo: {
|
apollo: {
|
||||||
|
|
@ -119,7 +152,7 @@
|
||||||
display: grid;
|
display: grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__visibility-button {
|
&__action-button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ const cache = new InMemoryCache({
|
||||||
cacheRedirects: {
|
cacheRedirects: {
|
||||||
Query: {
|
Query: {
|
||||||
contentBlock: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ContentBlockNode', id: args.id}),
|
contentBlock: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ContentBlockNode', id: args.id}),
|
||||||
|
chapter: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ChapterNode', id: args.id}),
|
||||||
assignment: (_, args, {getCacheKey}) => getCacheKey({__typename: 'AssignmentNode', id: args.id})
|
assignment: (_, args, {getCacheKey}) => getCacheKey({__typename: 'AssignmentNode', id: args.id})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
# to be read from cache only
|
||||||
|
#import "./fragments/contentBlockParts.gql"
|
||||||
|
query ContentBlockQuery {
|
||||||
|
contentBlocks {
|
||||||
|
...ContentBlockParts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
#import "./fragments/chapterParts.gql"
|
||||||
|
query ChapterQuery($id: ID!) {
|
||||||
|
chapter(id: $id) {
|
||||||
|
...ChapterParts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
#import "./contentBlockParts.gql"
|
||||||
|
fragment ChapterParts on ChapterNode {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
description
|
||||||
|
contentBlocks {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
...ContentBlockParts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ fragment ContentBlockParts on ContentBlockNode {
|
||||||
type
|
type
|
||||||
contents
|
contents
|
||||||
userCreated
|
userCreated
|
||||||
|
mine
|
||||||
hiddenFor {
|
hiddenFor {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#import "./fragments/contentBlockParts.gql"
|
#import "./fragments/chapterParts.gql"
|
||||||
#import "./fragments/assignmentParts.gql"
|
#import "./fragments/assignmentParts.gql"
|
||||||
query ModulesQuery($slug: String!) {
|
query ModulesQuery($slug: String!) {
|
||||||
module(slug: $slug) {
|
module(slug: $slug) {
|
||||||
|
|
@ -34,16 +34,7 @@ query ModulesQuery($slug: String!) {
|
||||||
chapters {
|
chapters {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
...ChapterParts
|
||||||
title
|
|
||||||
description
|
|
||||||
contentBlocks {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
...ContentBlockParts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
mutation DeleteContentBlock($input: DeleteContentBlockInput!) {
|
||||||
|
deleteContentBlock(input: $input) {
|
||||||
|
success
|
||||||
|
errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#{
|
||||||
|
# "input": {
|
||||||
|
# "id": "Um9vbU5vZGU6MjY="
|
||||||
|
# }
|
||||||
|
#}
|
||||||
|
|
@ -3,6 +3,7 @@ import json
|
||||||
import graphene
|
import graphene
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from graphene import relay
|
from graphene import relay
|
||||||
|
from graphql_relay import from_global_id
|
||||||
|
|
||||||
from api.utils import get_object, get_errors
|
from api.utils import get_object, get_errors
|
||||||
from books.models import ContentBlock, Chapter, SchoolClass
|
from books.models import ContentBlock, Chapter, SchoolClass
|
||||||
|
|
@ -115,3 +116,23 @@ class AddContentBlock(relay.ClientIDMutation):
|
||||||
errors = ['Error: {}'.format(e)]
|
errors = ['Error: {}'.format(e)]
|
||||||
|
|
||||||
return cls(new_content_block=None, errors=errors)
|
return cls(new_content_block=None, errors=errors)
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteContentBlock(relay.ClientIDMutation):
|
||||||
|
class Input:
|
||||||
|
id = graphene.ID(required=True)
|
||||||
|
|
||||||
|
success = graphene.Boolean()
|
||||||
|
errors = graphene.String()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def mutate_and_get_payload(cls, root, info, **kwargs):
|
||||||
|
id = from_global_id(kwargs.get('id'))[1]
|
||||||
|
user = info.context.user
|
||||||
|
|
||||||
|
try:
|
||||||
|
content_block = ContentBlock.objects.get(pk=id, owner=user)
|
||||||
|
content_block.delete()
|
||||||
|
return cls(success=True)
|
||||||
|
except ContentBlock.DoesNotExist:
|
||||||
|
return cls(success=False, errors='Content block not found')
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
from books.schema.mutations.contentblock import MutateContentBlock, AddContentBlock
|
from books.schema.mutations.contentblock import MutateContentBlock, AddContentBlock, DeleteContentBlock
|
||||||
|
|
||||||
|
|
||||||
class BookMutations(object):
|
class BookMutations(object):
|
||||||
mutate_content_block = MutateContentBlock.Field()
|
mutate_content_block = MutateContentBlock.Field()
|
||||||
add_content_block = AddContentBlock.Field()
|
add_content_block = AddContentBlock.Field()
|
||||||
|
delete_content_block = DeleteContentBlock.Field()
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ from ..models import Book, Topic, Module, Chapter, ContentBlock
|
||||||
|
|
||||||
|
|
||||||
class ContentBlockNode(DjangoObjectType):
|
class ContentBlockNode(DjangoObjectType):
|
||||||
|
mine = graphene.Boolean()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ContentBlock
|
model = ContentBlock
|
||||||
only_fields = [
|
only_fields = [
|
||||||
|
|
@ -17,6 +19,8 @@ class ContentBlockNode(DjangoObjectType):
|
||||||
]
|
]
|
||||||
interfaces = (relay.Node,)
|
interfaces = (relay.Node,)
|
||||||
|
|
||||||
|
def resolve_mine(self, info, **kwargs):
|
||||||
|
return self.owner is not None and self.owner.pk == info.context.user.pk
|
||||||
|
|
||||||
class ChapterNode(DjangoObjectType):
|
class ChapterNode(DjangoObjectType):
|
||||||
content_blocks = DjangoFilterConnectionField(ContentBlockNode)
|
content_blocks = DjangoFilterConnectionField(ContentBlockNode)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue