From ef48f5afb614ecd78cdf5f20b5ab24fe6087e791 Mon Sep 17 00:00:00 2001 From: Ramon Wenger Date: Wed, 19 Sep 2018 15:40:21 +0200 Subject: [PATCH] Add content block edit modal Also clean up the state store. Also change the properties of the content block contents on the server --- client/src/App.vue | 4 +- client/src/components/ContentBlock.vue | 7 +- .../content-block-form/ContentBlockForm.vue | 39 ++++++---- .../EditContentBlockWizard.vue | 75 +++++++++++++++++++ .../NewContentBlockWizard.vue | 6 +- .../components/content-forms/DocumentForm.vue | 4 +- .../src/components/content-forms/LinkForm.vue | 6 +- .../src/components/content-forms/TextForm.vue | 8 +- .../components/content-forms/VideoForm.vue | 12 +-- client/src/graphql/client.js | 10 ++- client/src/graphql/gql/contentBlockQuery.gql | 6 ++ client/src/pages/module.vue | 17 +---- client/src/store/index.js | 25 ++++--- server/book/schema/inputs.py | 8 +- server/book/schema/mutations.py | 10 +-- 15 files changed, 171 insertions(+), 66 deletions(-) create mode 100644 client/src/components/content-block-form/EditContentBlockWizard.vue create mode 100644 client/src/graphql/gql/contentBlockQuery.gql diff --git a/client/src/App.vue b/client/src/App.vue index 82a9ad2b..6120ab55 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -10,6 +10,7 @@ import SimpleLayout from '@/layouts/SimpleLayout'; import Modal from '@/components/Modal'; import NewContentBlockWizard from '@/components/content-block-form/NewContentBlockWizard'; + import EditContentBlockWizard from '@/components/content-block-form/EditContentBlockWizard'; export default { name: 'App', @@ -18,7 +19,8 @@ DefaultLayout, SimpleLayout, Modal, - NewContentBlockWizard + NewContentBlockWizard, + EditContentBlockWizard }, computed: { diff --git a/client/src/components/ContentBlock.vue b/client/src/components/ContentBlock.vue index 405667ac..2fcdc62a 100644 --- a/client/src/components/ContentBlock.vue +++ b/client/src/components/ContentBlock.vue @@ -10,7 +10,9 @@ :content-block="contentBlock" class="content-block__visibility-menu" > - + + +

{{contentBlock.title}}

@@ -70,6 +72,9 @@ methods: { toggleVisibility() { this.showVisibility = !this.showVisibility; + }, + editContentBlock() { + this.$store.dispatch('editContentBlock', this.contentBlock.id); } }, diff --git a/client/src/components/content-block-form/ContentBlockForm.vue b/client/src/components/content-block-form/ContentBlockForm.vue index a2ede60b..eb9a5058 100644 --- a/client/src/components/content-block-form/ContentBlockForm.vue +++ b/client/src/components/content-block-form/ContentBlockForm.vue @@ -6,7 +6,7 @@ v-on:add-element="addElement" :index="-1" > -
+
+ + + + diff --git a/client/src/components/content-block-form/NewContentBlockWizard.vue b/client/src/components/content-block-form/NewContentBlockWizard.vue index 945874af..b9a67a86 100644 --- a/client/src/components/content-block-form/NewContentBlockWizard.vue +++ b/client/src/components/content-block-form/NewContentBlockWizard.vue @@ -21,7 +21,7 @@ methods: { hideModal() { - this.$store.dispatch('resetContentBlock'); + this.$store.dispatch('resetContentBlockPosition'); this.$store.dispatch('hideModal'); }, saveContentBlock(contentBlock) { @@ -31,7 +31,7 @@ input: { contentBlock: { title: contentBlock.title, - contents: contentBlock.elements.filter(value => Object.keys(value).length > 0) + contents: contentBlock.contents.filter(value => Object.keys(value).length > 0) }, after: this.$store.state.contentBlockPosition.after, parent: this.$store.state.contentBlockPosition.parent @@ -53,7 +53,7 @@ return { contentBlock: { title: '', - elements: [ + contents: [ {} ] } diff --git a/client/src/components/content-forms/DocumentForm.vue b/client/src/components/content-forms/DocumentForm.vue index 296b19c1..dd034555 100644 --- a/client/src/components/content-forms/DocumentForm.vue +++ b/client/src/components/content-forms/DocumentForm.vue @@ -8,7 +8,7 @@

@@ -18,7 +18,7 @@ import InfoIcon from '@/components/icons/InfoIcon'; export default { - props: ['url', 'index'], + props: ['value', 'index'], components: { InfoIcon diff --git a/client/src/components/content-forms/LinkForm.vue b/client/src/components/content-forms/LinkForm.vue index 5503b25f..c8191897 100644 --- a/client/src/components/content-forms/LinkForm.vue +++ b/client/src/components/content-forms/LinkForm.vue @@ -1,14 +1,14 @@ diff --git a/client/src/components/content-forms/TextForm.vue b/client/src/components/content-forms/TextForm.vue index 2aab7b2d..cd5b242b 100644 --- a/client/src/components/content-forms/TextForm.vue +++ b/client/src/components/content-forms/TextForm.vue @@ -9,7 +9,13 @@ diff --git a/client/src/components/content-forms/VideoForm.vue b/client/src/components/content-forms/VideoForm.vue index 9bbadb4c..f6758cbf 100644 --- a/client/src/components/content-forms/VideoForm.vue +++ b/client/src/components/content-forms/VideoForm.vue @@ -11,14 +11,14 @@ + :value="value.url" v-on:input="$emit('video-change-url', $event.target.value, index)">
- +
- +
@@ -31,7 +31,7 @@ import {isVimeoUrl, isYoutubeUrl} from '@/helpers/video'; export default { - props: ['url', 'index'], + props: ['value', 'index'], components: { InfoIcon, @@ -41,10 +41,10 @@ computed: { isYoutube() { - return isYoutubeUrl(this.url); + return isYoutubeUrl(this.value.url); }, isVimeo() { - return isVimeoUrl(this.url); + return isVimeoUrl(this.value.url); } } } diff --git a/client/src/graphql/client.js b/client/src/graphql/client.js index f9178bb1..03ab4e39 100644 --- a/client/src/graphql/client.js +++ b/client/src/graphql/client.js @@ -25,10 +25,18 @@ const consoleLink = new ApolloLink((operation, forward) => { const composedLink = ApolloLink.from([consoleLink, httpLink]); +const cache = new InMemoryCache({ + cacheRedirects: { + Query: { + contentBlock: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ContentBlockNode', id: args.id}) + } + } +}); + // Create the apollo client export default new ApolloClient({ link: composedLink, // link: httpLink, - cache: new InMemoryCache(), + cache: cache, connectToDevTools: true }) diff --git a/client/src/graphql/gql/contentBlockQuery.gql b/client/src/graphql/gql/contentBlockQuery.gql new file mode 100644 index 00000000..1649d68f --- /dev/null +++ b/client/src/graphql/gql/contentBlockQuery.gql @@ -0,0 +1,6 @@ +#import "./fragments/contentBlockParts.gql" +query ContentBlockQuery($id: ID!) { + contentBlock(id: $id) { + ...ContentBlockParts + } +} diff --git a/client/src/pages/module.vue b/client/src/pages/module.vue index ef5687b0..45ecb83a 100644 --- a/client/src/pages/module.vue +++ b/client/src/pages/module.vue @@ -29,7 +29,7 @@ manual: true, result({data, loading, networkStatus}) { if (!loading) { - const cleanedData = this.$getRidOfEdges(data) + const cleanedData = this.$getRidOfEdges(data); this.module = cleanedData.modules[0] || {}; } } @@ -37,21 +37,6 @@ } }, - created() { - this.$store.subscribe((mutation, state) => { - if (mutation.type === 'updateContentBlocks' && state.updateContentBlocks) { - this.updateQuery(); - } - }) - }, - - methods: { - updateQuery() { - this.$apollo.queries.moduleQuery.refetch(); - this.$store.dispatch('resetUpdateContentBlocksFlag'); - } - }, - data() { return { module: { diff --git a/client/src/store/index.js b/client/src/store/index.js index 9f815cd1..c5d666d4 100644 --- a/client/src/store/index.js +++ b/client/src/store/index.js @@ -12,8 +12,8 @@ export default new Vuex.Store({ contentBlockPosition: {}, scrollPosition: 0, moduleSlug: 'mein-neues-umfeld', - updateContentBlocks: false, - filterForGroup: false + filterForGroup: false, + currentContentBlock: '' }, getters: {}, @@ -26,9 +26,16 @@ export default new Vuex.Store({ document.body.classList.remove('no-scroll'); // won't get at the body any other way commit('setModal', false); }, - resetContentBlock({commit}) { + resetContentBlockPosition({commit}) { commit('setContentBlockPosition', {}); }, + resetCurrentContentBlock({commit}) { + commit('setCurrentContentBlock', ''); + }, + editContentBlock({commit, dispatch}, payload) { + commit('setCurrentContentBlock', payload); + dispatch('showModal', 'edit-content-block-wizard'); + }, addContentBlock({commit, dispatch}, payload) { commit('setContentBlockPosition', payload); dispatch('showModal', 'new-content-block-wizard'); @@ -37,12 +44,6 @@ export default new Vuex.Store({ document.body.classList.add('no-scroll'); // won't get at the body any other way commit('setModal', payload); }, - updateContentBlocks({commit}) { - commit('updateContentBlocks', true); - }, - resetUpdateContentBlocksFlag({commit}) { - commit('updateContentBlocks', false); - }, setFilterForGroup({commit}, payload) { commit('setFilterForGroup', payload); } @@ -61,12 +62,12 @@ export default new Vuex.Store({ setNewContentBlock(state, payload) { state.newContentBlock = payload; }, - updateContentBlocks(state, payload) { - state.updateContentBlocks = payload; - }, setContentBlockPosition(state, payload) { state.contentBlockPosition = payload; }, + setCurrentContentBlock(state, payload) { + state.currentContentBlock = payload; + }, setFilterForGroup(state, payload) { state.filterForGroup = payload; } diff --git a/server/book/schema/inputs.py b/server/book/schema/inputs.py index b0719f8e..67c77d94 100644 --- a/server/book/schema/inputs.py +++ b/server/book/schema/inputs.py @@ -13,10 +13,9 @@ class InputTypes(graphene.Enum): document_block = 'document_block' -class ContentElementInput(InputObjectType): +class ContentElementValueInput(InputObjectType): # we'll handle this with a single input, even tho it would be nice to have a type for every different possibility # see discussion at https://github.com/graphql/graphql-js/issues/207 - type = InputTypes(required=True) text = graphene.String(description='To be used for link_block, text_block types') url = graphene.String(description='To be used for link, basic_knowledge, image_block types') description = graphene.String(description='To be used for basic_knowledge type') @@ -24,6 +23,11 @@ class ContentElementInput(InputObjectType): task_text = graphene.String(description='To be used for task type') +class ContentElementInput(InputObjectType): + type = InputTypes(required=True) + value = ContentElementValueInput() + + class ContentBlockInput(InputObjectType): title = graphene.String(required=True) type = graphene.String() diff --git a/server/book/schema/mutations.py b/server/book/schema/mutations.py index c8fcbc6c..7c519876 100644 --- a/server/book/schema/mutations.py +++ b/server/book/schema/mutations.py @@ -21,7 +21,7 @@ def handle_content_blocks(content_data): new_contents.append({ 'type': 'text_block', 'value': { - 'text': '

{}

'.format(bleach.clean(content['text'])) + 'text': '

{}

'.format(bleach.clean(content['value']['text'])) }}) elif content['type'] == 'student_entry': pass @@ -31,8 +31,8 @@ def handle_content_blocks(content_data): new_contents.append({ 'type': 'link_block', 'value': { - 'text': bleach.clean(content['text']), - 'url': bleach.clean(content['url']) + 'text': bleach.clean(content['value']['text']), + 'url': bleach.clean(content['value']['url']) } }) elif content['type'] == 'task': @@ -41,13 +41,13 @@ def handle_content_blocks(content_data): new_contents.append({ 'type': 'video_block', 'value': { - 'url': bleach.clean(content['url']) + 'url': bleach.clean(content['value']['url']) }}) elif content['type'] == 'document_block': new_contents.append({ 'type': 'document_block', 'value': { - 'url': bleach.clean(content['url']) + 'url': bleach.clean(content['value']['url']) }}) return new_contents