Add note update mutation

This commit is contained in:
Ramon Wenger 2019-10-12 08:54:20 +02:00
parent 62460d58ab
commit 1d5603ed5f
13 changed files with 246 additions and 37 deletions

View File

@ -23,6 +23,7 @@
import EditProjectEntryWizard from '@/components/portfolio/EditProjectEntryWizard'; import EditProjectEntryWizard from '@/components/portfolio/EditProjectEntryWizard';
import NewObjectiveWizard from '@/components/objective-groups/NewObjectiveWizard'; import NewObjectiveWizard from '@/components/objective-groups/NewObjectiveWizard';
import NewNoteWizard from '@/components/notes/NewNoteWizard'; import NewNoteWizard from '@/components/notes/NewNoteWizard';
import EditNoteWizard from '@/components/notes/EditNoteWizard';
import FullscreenImage from '@/components/FullscreenImage'; import FullscreenImage from '@/components/FullscreenImage';
import FullscreenInfographic from '@/components/FullscreenInfographic'; import FullscreenInfographic from '@/components/FullscreenInfographic';
import FullscreenVideo from '@/components/FullscreenVideo'; import FullscreenVideo from '@/components/FullscreenVideo';
@ -50,6 +51,7 @@
EditProjectEntryWizard, EditProjectEntryWizard,
NewObjectiveWizard, NewObjectiveWizard,
NewNoteWizard, NewNoteWizard,
EditNoteWizard,
FullscreenImage, FullscreenImage,
FullscreenInfographic, FullscreenInfographic,
FullscreenVideo FullscreenVideo

View File

@ -22,7 +22,9 @@
:key="component.id" :key="component.id"
:component="component" :component="component"
:parent="contentBlock.id" :parent="contentBlock.id"
:bookmarks="contentBlock.bookmarks"> :bookmarks="contentBlock.bookmarks"
:notes="contentBlock.notes"
>
</content-component> </content-component>
</div> </div>

View File

@ -2,8 +2,10 @@
<div class="content-component" :class="{'content-component--bookmarked': bookmarked}"> <div class="content-component" :class="{'content-component--bookmarked': bookmarked}">
<bookmark-actions <bookmark-actions
@add-note="addNote(component.id)" @add-note="addNote(component.id)"
@edit-note="editNote"
@bookmark="bookmarkContent(component.id, !bookmarked)" @bookmark="bookmarkContent(component.id, !bookmarked)"
:bookmarked="bookmarked"></bookmark-actions> :bookmarked="bookmarked"
:note="note"></bookmark-actions>
<component <component
:is="component.type" :is="component.type"
v-bind="component"> v-bind="component">
@ -33,7 +35,7 @@
import MODULE_DETAILS_QUERY from '@/graphql/gql/moduleDetailsQuery.gql'; import MODULE_DETAILS_QUERY from '@/graphql/gql/moduleDetailsQuery.gql';
export default { export default {
props: ['component', 'parent', 'bookmarks'], props: ['component', 'parent', 'bookmarks', 'notes'],
components: { components: {
'text_block': TextBlock, 'text_block': TextBlock,
@ -58,6 +60,10 @@
computed: { computed: {
bookmarked() { bookmarked() {
return !!this.bookmarks.find(bookmark => bookmark.uuid === this.component.id); return !!this.bookmarks.find(bookmark => bookmark.uuid === this.component.id);
},
note() {
const bookmark = this.bookmarks.find(bookmark => bookmark.uuid === this.component.id);
return bookmark && bookmark.note;
} }
}, },
@ -68,6 +74,9 @@
contentBlock: this.parent contentBlock: this.parent
}); });
}, },
editNote() {
this.$store.dispatch('editNote', this.note);
},
bookmarkContent(uuid, bookmarked) { bookmarkContent(uuid, bookmarked) {
this.$apollo.mutate({ this.$apollo.mutate({
mutation: UPDATE_CONTENT_BOOKMARK, mutation: UPDATE_CONTENT_BOOKMARK,

View File

@ -4,9 +4,11 @@
:class="{'bookmark-actions__action--bookmarked': bookmarked}"> :class="{'bookmark-actions__action--bookmarked': bookmarked}">
<bookmark-icon :bookmarked="bookmarked"></bookmark-icon> <bookmark-icon :bookmarked="bookmarked"></bookmark-icon>
</a> </a>
<a class="bookmark-actions__action" v-if="bookmarked" @click="$emit('add-note')"> <a class="bookmark-actions__action" v-if="bookmarked && !note" @click="$emit('add-note')">
<add-note-icon></add-note-icon> <add-note-icon></add-note-icon>
<note-icon v-if="false"></note-icon> </a>
<a class="bookmark-actions__action bookmark-actions__action--noted" @click="$emit('edit-note')" v-if="note">
<note-icon></note-icon>
</a> </a>
</div> </div>
</template> </template>
@ -17,7 +19,7 @@
import NoteIcon from '@/components/icons/NoteIcon'; import NoteIcon from '@/components/icons/NoteIcon';
export default { export default {
props: ['bookmarked'], props: ['bookmarked', 'note'],
components: { components: {
BookmarkIcon, BookmarkIcon,
AddNoteIcon, AddNoteIcon,
@ -43,8 +45,9 @@
&__action { &__action {
opacity: 0; opacity: 0;
transition: opacity 0.3s; transition: opacity 0.3s;
cursor: pointer;
&--bookmarked { &--bookmarked, &--noted {
opacity: 1; opacity: 1;
} }
} }

View File

@ -0,0 +1,46 @@
<template>
<note-form @save="editNote" @hide="hide" :note="currentNote"></note-form>
</template>
<script>
import NoteForm from '@/components/notes/NoteForm';
import UPDATE_NOTE_MUTATION from '@/graphql/gql/mutations/updateNote.gql';
import MODULE_DETAILS_QUERY from '@/graphql/gql/moduleDetailsQuery.gql';
import {mapGetters} from 'vuex';
export default {
components: {
NoteForm
},
computed: {
...mapGetters(['currentNote'])
},
methods: {
editNote(note) {
this.$apollo.mutate({
mutation: UPDATE_NOTE_MUTATION,
variables: {
input: {
note
}
},
refetchQueries: [{
query: MODULE_DETAILS_QUERY,
variables: {
slug: this.$route.params.slug
}
}]
}).then(() => {
this.$store.dispatch('hideModal');
});
},
hide() {
this.$store.dispatch('hideModal');
}
}
}
</script>

View File

@ -1,35 +1,23 @@
<template> <template>
<modal :hide-header="true" :small="true"> <note-form @save="addNote" @hide="hide" :note="note"></note-form>
<modal-input v-on:input="note = $event"
placeholder="Notiz erfassen"
:value="note"
></modal-input>
<div slot="footer">
<a class="button button--primary" data-cy="modal-save-button"
@click="addNote(note)">Speichern</a>
<a class="button" @click="hide">Löschen</a>
</div>
</modal>
</template> </template>
<script> <script>
import Modal from '@/components/Modal'; import NoteForm from '@/components/notes/NoteForm';
import ModalInput from '@/components/ModalInput';
import ADD_NOTE_MUTATION from '@/graphql/gql/mutations/addNote.gql'; import ADD_NOTE_MUTATION from '@/graphql/gql/mutations/addNote.gql';
import MODULE_DETAILS_QUERY from '@/graphql/gql/moduleDetailsQuery.gql';
import {mapGetters} from 'vuex'; import {mapGetters} from 'vuex';
export default { export default {
components: { components: {
Modal, NoteForm
ModalInput
}, },
data() { data() {
return { return {
note: '' note: {}
} }
}, },
@ -38,7 +26,7 @@
}, },
methods: { methods: {
addNote(text) { addNote(note) {
this.$apollo.mutate({ this.$apollo.mutate({
mutation: ADD_NOTE_MUTATION, mutation: ADD_NOTE_MUTATION,
variables: { variables: {
@ -46,12 +34,19 @@
note: { note: {
content: this.currentContent, content: this.currentContent,
contentBlock: this.currentContentBlock, contentBlock: this.currentContentBlock,
text text: note.text
} }
} }
} },
refetchQueries: [{
query: MODULE_DETAILS_QUERY,
variables: {
slug: this.$route.params.slug
}
}]
}).then(() => {
this.$store.dispatch('hideModal');
}); });
console.log(note);
}, },
hide() { hide() {
this.$store.dispatch('hideModal'); this.$store.dispatch('hideModal');

View File

@ -0,0 +1,38 @@
<template>
<modal :hide-header="true" :small="true">
<modal-input v-on:input="localNote.text = $event"
placeholder="Notiz erfassen"
:value="localNote.text"
></modal-input>
<div slot="footer">
<a class="button button--primary" data-cy="modal-save-button"
@click="$emit('save', localNote)">Speichern</a>
<a class="button" @click="$emit('hide')">Löschen</a>
</div>
</modal>
</template>
<script>
import Modal from '@/components/Modal';
import ModalInput from '@/components/ModalInput';
export default {
props: ['note'],
components: {
Modal,
ModalInput
},
data() {
return {
localNote: Object.assign({},
{
...this.note
}
)
}
}
}
</script>

View File

@ -0,0 +1,8 @@
mutation UpdateNote($input: UpdateNoteInput!) {
updateNote(input: $input) {
note {
id
text
}
}
}

View File

@ -20,6 +20,7 @@ export default new Vuex.Store({
objectiveGroupType: '', objectiveGroupType: '',
currentObjectiveGroup: '', currentObjectiveGroup: '',
parentProject: null, parentProject: null,
currentNote: null,
currentProjectEntry: null, currentProjectEntry: null,
imageUrl: '', imageUrl: '',
infographic: { infographic: {
@ -43,7 +44,8 @@ export default new Vuex.Store({
editModule: state => state.editModule, editModule: state => state.editModule,
currentObjectiveGroup: state => state.currentObjectiveGroup, currentObjectiveGroup: state => state.currentObjectiveGroup,
currentContent: state => state.currentContent, currentContent: state => state.currentContent,
currentContentBlock: state => state.currentContentBlock currentContentBlock: state => state.currentContentBlock,
currentNote: state => state.currentNote,
}, },
actions: { actions: {
@ -73,6 +75,7 @@ export default new Vuex.Store({
type: '' type: ''
}); });
commit('setVimeoId', null); commit('setVimeoId', null);
commit('setCurrentNote', null);
}, },
resetContentBlockPosition({commit}) { resetContentBlockPosition({commit}) {
commit('setContentBlockPosition', {}); commit('setContentBlockPosition', {});
@ -127,6 +130,10 @@ export default new Vuex.Store({
commit('setCurrentContent', payload.content); commit('setCurrentContent', payload.content);
dispatch('showModal', 'new-note-wizard'); dispatch('showModal', 'new-note-wizard');
}, },
editNote({commit, dispatch}, payload) {
commit('setCurrentNote', payload);
dispatch('showModal', 'edit-note-wizard');
},
showFullscreenImage({commit, dispatch}, payload) { showFullscreenImage({commit, dispatch}, payload) {
commit('setImageUrl', payload); commit('setImageUrl', payload);
dispatch('showModal', 'fullscreen-image'); dispatch('showModal', 'fullscreen-image');
@ -152,7 +159,6 @@ export default new Vuex.Store({
if (payload && !state.scrollingToAssignment) { if (payload && !state.scrollingToAssignment) {
commit('setScrollingToAssignment', true); commit('setScrollingToAssignment', true);
} }
;
if (!payload && state.scrollingToAssignment) { if (!payload && state.scrollingToAssignment) {
commit('setScrollingToAssignment', false); commit('setScrollingToAssignment', false);
@ -189,6 +195,9 @@ export default new Vuex.Store({
setParentRoom(state, payload) { setParentRoom(state, payload) {
state.parentRoom = payload; state.parentRoom = payload;
}, },
setCurrentNote(state, payload) {
state.currentNote = payload;
},
setCurrentRoomEntry(state, payload) { setCurrentRoomEntry(state, payload) {
state.currentRoomEntry = payload; state.currentRoomEntry = payload;
}, },

View File

@ -0,0 +1,21 @@
# Generated by Django 2.0.6 on 2019-10-10 09:52
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('notes', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('books', '0014_auto_20190912_1228'),
]
operations = [
migrations.AddField(
model_name='contentblock',
name='bookmarks',
field=models.ManyToManyField(related_name='bookmarked_content_blocks', through='notes.ContentBlockBookmark', to=settings.AUTH_USER_MODEL),
),
]

View File

@ -7,3 +7,7 @@ class AddNoteArgument(InputObjectType):
content_block = graphene.ID(required=True) content_block = graphene.ID(required=True)
text = graphene.String(required=True) text = graphene.String(required=True)
class UpdateNoteArgument(InputObjectType):
id = graphene.ID(required=True)
text = graphene.String(required=True)

View File

@ -0,0 +1,46 @@
# Generated by Django 2.0.6 on 2019-10-10 09:52
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('books', '0014_auto_20190912_1228'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='ContentBlockBookmark',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uuid', models.UUIDField(unique=True)),
('content_block', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='books.ContentBlock')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Note',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('text', models.TextField()),
],
),
migrations.AddField(
model_name='contentblockbookmark',
name='note',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='notes.Note'),
),
migrations.AddField(
model_name='contentblockbookmark',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
]

View File

@ -1,16 +1,19 @@
from builtins import PermissionError
import graphene import graphene
import json import json
from graphene import relay from graphene import relay
from api.utils import get_object from api.utils import get_object
from books.models import ContentBlock from books.models import ContentBlock
from notes.inputs import AddNoteArgument from notes.inputs import AddNoteArgument, UpdateNoteArgument
from notes.models import ContentBlockBookmark, Note from notes.models import ContentBlockBookmark, Note
from notes.schema import NoteNode from notes.schema import NoteNode
class UpdateContentBookmark(relay.ClientIDMutation): class UpdateContentBookmark(relay.ClientIDMutation):
class Input: class Input:
id = graphene.String(required=True) uuid = graphene.UUID(required=True)
content_block = graphene.ID(required=True) content_block = graphene.ID(required=True)
bookmarked = graphene.Boolean(required=True) bookmarked = graphene.Boolean(required=True)
@ -19,7 +22,7 @@ class UpdateContentBookmark(relay.ClientIDMutation):
@classmethod @classmethod
def mutate_and_get_payload(cls, root, info, **kwargs): def mutate_and_get_payload(cls, root, info, **kwargs):
id = kwargs.get('id') uuid = kwargs.get('uuid')
user = info.context.user user = info.context.user
content_block_id = kwargs.get('content_block') content_block_id = kwargs.get('content_block')
bookmarked = kwargs.get('bookmarked') bookmarked = kwargs.get('bookmarked')
@ -29,20 +32,19 @@ class UpdateContentBookmark(relay.ClientIDMutation):
if bookmarked: if bookmarked:
ContentBlockBookmark.objects.create( ContentBlockBookmark.objects.create(
content_block=content_block, content_block=content_block,
id=id, uuid=uuid,
user=user user=user
) )
else: else:
ContentBlockBookmark.objects.get( ContentBlockBookmark.objects.get(
content_block=content_block, content_block=content_block,
id=id, uuid=uuid,
user=user user=user
).delete() ).delete()
return cls(success=True) return cls(success=True)
class AddNote(relay.ClientIDMutation): class AddNote(relay.ClientIDMutation):
class Input: class Input:
note = graphene.Argument(AddNoteArgument) note = graphene.Argument(AddNoteArgument)
@ -61,7 +63,7 @@ class AddNote(relay.ClientIDMutation):
bookmark = ContentBlockBookmark.objects.get( bookmark = ContentBlockBookmark.objects.get(
content_block=content_block, content_block=content_block,
id=content_uuid, uuid=content_uuid,
user=user user=user
) )
@ -71,6 +73,30 @@ class AddNote(relay.ClientIDMutation):
return cls(note=bookmark.note) return cls(note=bookmark.note)
class UpdateNote(relay.ClientIDMutation):
class Input:
note = graphene.Argument(UpdateNoteArgument)
note = graphene.Field(NoteNode)
@classmethod
def mutate_and_get_payload(cls, root, info, **kwargs):
user = info.context.user
note = kwargs.get('note')
id = note.get('id')
text = note.get('text')
note = get_object(Note, id)
if note.contentblockbookmark.user != user:
raise PermissionError
note.text = text
note.save()
return cls(note=note)
class NoteMutations: class NoteMutations:
add_note = AddNote.Field() add_note = AddNote.Field()
update_note = UpdateNote.Field()
update_content_bookmark = UpdateContentBookmark.Field() update_content_bookmark = UpdateContentBookmark.Field()