Merged in feature/bookmarks-for-modules-and-chapters (pull request #42)
Feature/bookmarks for modules and chapters
This commit is contained in:
commit
388c1f63d7
|
|
@ -0,0 +1,39 @@
|
|||
describe('Survey', () => {
|
||||
beforeEach(() => {
|
||||
cy.exec("python ../server/manage.py prepare_bookmarks_for_cypress");
|
||||
|
||||
cy.viewport('macbook-15');
|
||||
cy.startGraphQLCapture();
|
||||
cy.login('rahel.cueni', 'test', true);
|
||||
cy.get('body').contains('Neues Wissen erwerben');
|
||||
});
|
||||
|
||||
it('should bookmark content block', () => {
|
||||
cy.visit('/module/lohn-und-budget/');
|
||||
|
||||
cy.get('.content-component').contains('Das folgende Interview').parent().parent().as('interviewContent');
|
||||
|
||||
cy.get('@interviewContent').within(() => {
|
||||
cy.get('.bookmark-actions__bookmark').click();
|
||||
cy.get('.bookmark-actions__add-note').click();
|
||||
});
|
||||
|
||||
cy.get('[data-cy=bookmark-note]').within(() => {
|
||||
cy.get('.skillbox-input').type('Hallo Velo');
|
||||
});
|
||||
|
||||
cy.get('[data-cy=modal-save-button]').click();
|
||||
|
||||
cy.get('@interviewContent').within(() => {
|
||||
cy.get('.bookmark-actions__edit-note').click();
|
||||
|
||||
});
|
||||
|
||||
cy.get('[data-cy=bookmark-note]').within(() => {
|
||||
cy.get('.skillbox-input').clear().type('Hello Bike');
|
||||
});
|
||||
|
||||
cy.get('[data-cy=modal-save-button]').click();
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
// -- This is will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
|
||||
// todo: replace with apollo call
|
||||
Cypress.Commands.add("login", (username, password, visitLogin=false) => {
|
||||
if (visitLogin) {
|
||||
cy.visit('/login');
|
||||
|
|
|
|||
|
|
@ -2,6 +2,14 @@
|
|||
<div class="chapter">
|
||||
<h3 :id="'chapter-' + index">{{chapter.title}}</h3>
|
||||
|
||||
<bookmark-actions
|
||||
class="chapter__bookmark-actions"
|
||||
@add-note="addNote"
|
||||
@edit-note="editNote"
|
||||
:bookmarked="chapter.bookmark"
|
||||
@bookmark="bookmark(!chapter.bookmark)"
|
||||
:note="note"
|
||||
></bookmark-actions>
|
||||
<p class="chapter__description">
|
||||
{{chapter.description}}
|
||||
</p>
|
||||
|
|
@ -18,15 +26,20 @@
|
|||
<script>
|
||||
import ContentBlock from '@/components/ContentBlock';
|
||||
import AddContentButton from '@/components/AddContentButton';
|
||||
import BookmarkActions from '@/components/notes/BookmarkActions';
|
||||
|
||||
import {mapGetters} from 'vuex';
|
||||
import {isHidden} from '@/helpers/content-block';
|
||||
import {meQuery} from '@/graphql/queries';
|
||||
|
||||
import UPDATE_CHAPTER_BOOKMARK_MUTATION from '@/graphql/gql/mutations/updateChapterBookmark.gql';
|
||||
import CHAPTER_QUERY from '@/graphql/gql/chapterQuery.gql';
|
||||
|
||||
export default {
|
||||
props: ['chapter', 'index'],
|
||||
|
||||
components: {
|
||||
BookmarkActions,
|
||||
ContentBlock,
|
||||
AddContentButton
|
||||
},
|
||||
|
|
@ -45,6 +58,12 @@
|
|||
schoolClass() {
|
||||
return this.me.selectedClass;
|
||||
},
|
||||
note() {
|
||||
if (!(this.chapter && this.chapter.bookmark)) {
|
||||
return;
|
||||
}
|
||||
return this.chapter.bookmark.note;
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
|
|
@ -53,6 +72,64 @@
|
|||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
bookmark(bookmarked) {
|
||||
const id = this.chapter.id;
|
||||
this.$apollo.mutate({
|
||||
mutation: UPDATE_CHAPTER_BOOKMARK_MUTATION,
|
||||
variables: {
|
||||
input: {
|
||||
chapter: id,
|
||||
bookmarked
|
||||
}
|
||||
},
|
||||
update: (store, response) => {
|
||||
const query = CHAPTER_QUERY;
|
||||
const variables = {id};
|
||||
const data = store.readQuery({
|
||||
query,
|
||||
variables
|
||||
});
|
||||
|
||||
const chapter = data.chapter;
|
||||
|
||||
if (bookmarked) {
|
||||
chapter.bookmark = {
|
||||
__typename: 'ChapterBookmarkNode',
|
||||
note: null
|
||||
}
|
||||
} else {
|
||||
chapter.bookmark = null;
|
||||
}
|
||||
|
||||
data.chapter = chapter;
|
||||
|
||||
store.writeQuery({
|
||||
data,
|
||||
query,
|
||||
variables
|
||||
});
|
||||
},
|
||||
optimisticResponse: {
|
||||
__typename: 'Mutation',
|
||||
updateChapterBookmark: {
|
||||
__typename: 'UpdateChapterBookmarkPayload',
|
||||
success: true
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
addNote(id) {
|
||||
this.$store.dispatch('addNote', {
|
||||
content: id,
|
||||
parent: this.chapter.id
|
||||
});
|
||||
},
|
||||
editNote() {
|
||||
this.$store.dispatch('editNote', this.chapter.bookmark.note);
|
||||
},
|
||||
},
|
||||
|
||||
apollo: {
|
||||
me: meQuery
|
||||
}
|
||||
|
|
@ -63,6 +140,12 @@
|
|||
@import "@/styles/_mixins.scss";
|
||||
|
||||
.chapter {
|
||||
position: relative;
|
||||
|
||||
&__bookmark-actions {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
&__description {
|
||||
@include lead-paragraph;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="content-component" :class="{'content-component--bookmarked': bookmarked}">
|
||||
<bookmark-actions
|
||||
v-if="showBookmarkActions()"
|
||||
v-if="showBookmarkActions"
|
||||
@add-note="addNote(component.id)"
|
||||
@edit-note="editNote"
|
||||
@bookmark="bookmarkContent(component.id, !bookmarked)"
|
||||
|
|
@ -72,6 +72,9 @@
|
|||
note() {
|
||||
const bookmark = this.bookmarks && this.bookmarks.find(bookmark => bookmark.uuid === this.component.id);
|
||||
return bookmark && bookmark.note;
|
||||
},
|
||||
showBookmarkActions() {
|
||||
return this.component.type !== 'content_list' && this.component.type !== 'basic_knowledge' && !this.editModule;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -136,9 +139,6 @@
|
|||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
showBookmarkActions() {
|
||||
return this.component.type !== 'content_list' && this.component.type !== 'basic_knowledge' && !this.editModule;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,7 +5,17 @@
|
|||
<img
|
||||
:src="module.heroImage"
|
||||
alt="" class="module__hero">
|
||||
|
||||
<div class="module__intro-wrapper">
|
||||
<bookmark-actions
|
||||
class="module__bookmark-actions"
|
||||
@add-note="addNote"
|
||||
@edit-note="editNote"
|
||||
:bookmarked="module.bookmark"
|
||||
:note="note"
|
||||
@bookmark="bookmark(!module.bookmark)"></bookmark-actions>
|
||||
<div class="module__intro" v-html="module.intro"></div>
|
||||
</div>
|
||||
|
||||
<h3 id="objectives">Lernziele</h3>
|
||||
|
||||
|
|
@ -26,13 +36,17 @@
|
|||
|
||||
import UPDATE_OBJECTIVE_PROGRESS_MUTATION from '@/graphql/gql/mutations/updateObjectiveProgress.gql';
|
||||
import UPDATE_LAST_MODULE_MUTATION from '@/graphql/gql/mutations/updateLastModule.gql';
|
||||
import UPDATE_MODULE_BOOKMARK_MUTATION from '@/graphql/gql/mutations/updateModuleBookmark.gql';
|
||||
import OBJECTIVE_QUERY from '@/graphql/gql/objectiveQuery.gql';
|
||||
import ME_QUERY from '@/graphql/gql/meQuery.gql';
|
||||
import MODULE_QUERY from '@/graphql/gql/moduleByIdQuery.gql';
|
||||
|
||||
import {withoutOwnerFirst} from '@/helpers/sorting';
|
||||
import BookmarkActions from '@/components/notes/BookmarkActions';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BookmarkActions,
|
||||
ObjectiveGroups,
|
||||
ObjectiveGroupControl,
|
||||
AddObjectiveGroupButton,
|
||||
|
|
@ -67,6 +81,12 @@
|
|||
},
|
||||
isStudent() {
|
||||
return !this.me.permissions.includes('users.can_manage_school_class_content');
|
||||
},
|
||||
note() {
|
||||
if (!(this.module && this.module.bookmark)) {
|
||||
return;
|
||||
}
|
||||
return this.module.bookmark.note;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -112,6 +132,61 @@
|
|||
}
|
||||
})
|
||||
},
|
||||
bookmark(bookmarked) {
|
||||
const id = this.module.id;
|
||||
this.$apollo.mutate({
|
||||
mutation: UPDATE_MODULE_BOOKMARK_MUTATION,
|
||||
variables: {
|
||||
input: {
|
||||
module: id,
|
||||
bookmarked
|
||||
}
|
||||
},
|
||||
update: (store, response) => {
|
||||
const query = MODULE_QUERY;
|
||||
const variables = {id};
|
||||
const data = store.readQuery({
|
||||
query,
|
||||
variables
|
||||
});
|
||||
|
||||
const module = data.module;
|
||||
|
||||
if (bookmarked) {
|
||||
module.bookmark = {
|
||||
__typename: 'ModuleBookmarkNode',
|
||||
note: null
|
||||
}
|
||||
} else {
|
||||
module.bookmark = null;
|
||||
}
|
||||
|
||||
data.module = module;
|
||||
|
||||
store.writeQuery({
|
||||
data,
|
||||
query,
|
||||
variables
|
||||
});
|
||||
},
|
||||
optimisticResponse: {
|
||||
__typename: 'Mutation',
|
||||
updateModuleBookmark: {
|
||||
__typename: 'UpdateModuleBookmarkPayload',
|
||||
success: true
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
addNote(id) {
|
||||
this.$store.dispatch('addNote', {
|
||||
content: id,
|
||||
parent: this.module.id
|
||||
});
|
||||
},
|
||||
editNote() {
|
||||
this.$store.dispatch('editNote', this.module.bookmark.note);
|
||||
},
|
||||
},
|
||||
|
||||
apollo: {
|
||||
|
|
@ -138,6 +213,7 @@
|
|||
.module {
|
||||
display: flex;
|
||||
justify-self: center;
|
||||
|
||||
@include desktop {
|
||||
width: 800px;
|
||||
}
|
||||
|
|
@ -157,6 +233,10 @@
|
|||
@include meta-title;
|
||||
}
|
||||
|
||||
&__intro-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__intro {
|
||||
line-height: 1.5;
|
||||
margin-bottom: 3em;
|
||||
|
|
@ -171,5 +251,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
&__bookmark-actions {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
<template>
|
||||
<div class="bookmark-actions">
|
||||
<a class="bookmark-actions__action" @click="$emit('bookmark')"
|
||||
<a class="bookmark-actions__action bookmark-actions__bookmark" @click="$emit('bookmark')"
|
||||
:class="{'bookmark-actions__action--bookmarked': bookmarked}">
|
||||
<bookmark-icon :bookmarked="bookmarked"></bookmark-icon>
|
||||
</a>
|
||||
<a class="bookmark-actions__action" v-if="bookmarked && !note" @click="$emit('add-note')">
|
||||
<a class="bookmark-actions__action bookmark-actions__add-note" v-if="bookmarked && !note" @click="$emit('add-note')">
|
||||
<add-note-icon></add-note-icon>
|
||||
</a>
|
||||
<a class="bookmark-actions__action bookmark-actions__action--noted" @click="$emit('edit-note')" v-if="note">
|
||||
<a class="bookmark-actions__action bookmark-actions__edit-note bookmark-actions__action--noted" @click="$emit('edit-note')" v-if="note">
|
||||
<note-icon></note-icon>
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,10 +5,8 @@
|
|||
<script>
|
||||
import NoteForm from '@/components/notes/NoteForm';
|
||||
|
||||
import ADD_NOTE_MUTATION from '@/graphql/gql/mutations/addNote.gql';
|
||||
import CONTENT_BLOCK_QUERY from '@/graphql/gql/contentBlockQuery.gql';
|
||||
|
||||
import {mapGetters} from 'vuex';
|
||||
import {constructNoteMutation} from '@/helpers/new-note-mutation.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
@ -22,68 +20,32 @@
|
|||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['currentContent', 'currentContentBlock'])
|
||||
...mapGetters(['currentContent', 'currentContentBlock', 'currentNoteParent'])
|
||||
},
|
||||
|
||||
methods: {
|
||||
addNote(note) {
|
||||
addNote(n) {
|
||||
const content = this.currentContent;
|
||||
const contentBlock = this.currentContentBlock;
|
||||
const text = note.text;
|
||||
|
||||
this.$apollo.mutate({
|
||||
mutation: ADD_NOTE_MUTATION,
|
||||
variables: {
|
||||
input: {
|
||||
note: {
|
||||
const parent = this.currentNoteParent;
|
||||
const text = n.text;
|
||||
let note = {};
|
||||
if (content > '') {
|
||||
note = {
|
||||
content,
|
||||
contentBlock,
|
||||
text
|
||||
}
|
||||
}
|
||||
},
|
||||
update: (store, {data: {addNote: {note}}}) => {
|
||||
const query = CONTENT_BLOCK_QUERY;
|
||||
const variables = {id: contentBlock};
|
||||
const data = store.readQuery({
|
||||
query,
|
||||
variables
|
||||
});
|
||||
|
||||
const bookmarks = data.contentBlock.bookmarks;
|
||||
|
||||
let index = bookmarks.findIndex(element => {
|
||||
return element.uuid === content;
|
||||
});
|
||||
|
||||
if (index > -1) {
|
||||
let el = bookmarks[index];
|
||||
el.note = note;
|
||||
bookmarks.splice(index, 1, el);
|
||||
}
|
||||
|
||||
data.contentBlock.bookmarks = bookmarks;
|
||||
|
||||
store.writeQuery({
|
||||
data,
|
||||
query,
|
||||
variables
|
||||
});
|
||||
},
|
||||
optimisticResponse: {
|
||||
__typename: 'Mutation',
|
||||
addNote: {
|
||||
__typename: 'AddNotePayload',
|
||||
note: {
|
||||
__typename: 'NoteNode',
|
||||
id: -1,
|
||||
text: text
|
||||
} else {
|
||||
note = {
|
||||
parent,
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
}).then(() => {
|
||||
this.$store.dispatch('hideModal');
|
||||
});
|
||||
|
||||
this.$apollo
|
||||
.mutate(constructNoteMutation(note))
|
||||
.then(this.hide);
|
||||
},
|
||||
hide() {
|
||||
this.$store.dispatch('hideModal');
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
<modal :hide-header="true" :small="true">
|
||||
<modal-input v-on:input="localNote.text = $event"
|
||||
placeholder="Notiz erfassen"
|
||||
data-cy="bookmark-note"
|
||||
:value="localNote.text"
|
||||
></modal-input>
|
||||
<div slot="footer">
|
||||
|
|
|
|||
|
|
@ -3,6 +3,12 @@ fragment ChapterParts on ChapterNode {
|
|||
id
|
||||
title
|
||||
description
|
||||
bookmark {
|
||||
note {
|
||||
id
|
||||
text
|
||||
}
|
||||
}
|
||||
contentBlocks {
|
||||
edges {
|
||||
node {
|
||||
|
|
|
|||
|
|
@ -7,4 +7,10 @@ fragment ModuleParts on ModuleNode {
|
|||
slug
|
||||
heroImage
|
||||
solutionsEnabled
|
||||
bookmark {
|
||||
note {
|
||||
id
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
mutation UpdateChapterBookmark($input: UpdateChapterBookmarkInput!) {
|
||||
updateChapterBookmark(input: $input) {
|
||||
success
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
mutation UpdateModuleBookmark($input: UpdateModuleBookmarkInput!) {
|
||||
updateModuleBookmark(input: $input) {
|
||||
success
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
import ADD_NOTE_MUTATION from '@/graphql/gql/mutations/addNote.gql';
|
||||
import CONTENT_BLOCK_QUERY from '@/graphql/gql/contentBlockQuery.gql';
|
||||
import CHAPTER_QUERY from '@/graphql/gql/chapterQuery.gql';
|
||||
import MODULE_QUERY from '@/graphql/gql/moduleByIdQuery.gql';
|
||||
|
||||
const getBlockType = id => atob(id).split(':')[0]
|
||||
|
||||
export const constructNoteMutation = (n) => {
|
||||
let update = () => {
|
||||
};
|
||||
|
||||
if (n.contentBlock) { // has a content block, so it is a content block bookmark
|
||||
update = (store, {data: {addNote: {note}}}) => {
|
||||
const query = CONTENT_BLOCK_QUERY;
|
||||
const variables = {id: n.contentBlock};
|
||||
const data = store.readQuery({
|
||||
query,
|
||||
variables
|
||||
});
|
||||
|
||||
const bookmarks = data.contentBlock.bookmarks;
|
||||
|
||||
let index = bookmarks.findIndex(element => {
|
||||
return element.uuid === n.content;
|
||||
});
|
||||
|
||||
if (index > -1) {
|
||||
let el = bookmarks[index];
|
||||
el.note = note;
|
||||
bookmarks.splice(index, 1, el);
|
||||
}
|
||||
|
||||
data.contentBlock.bookmarks = bookmarks;
|
||||
|
||||
store.writeQuery({
|
||||
data,
|
||||
query,
|
||||
variables
|
||||
});
|
||||
};
|
||||
} else { // it's a chapter bookmark or a module bookmark
|
||||
update = (store, {data: {addNote: {note}}}) => {
|
||||
const type = getBlockType(n.parent) === 'ChapterNode' ? 'chapter' : 'module';
|
||||
const query = type === 'chapter' ? CHAPTER_QUERY : MODULE_QUERY;
|
||||
const variables = {id: n.parent};
|
||||
const data = store.readQuery({
|
||||
query,
|
||||
variables
|
||||
});
|
||||
|
||||
const bookmark = data[type].bookmark;
|
||||
|
||||
bookmark.note = note;
|
||||
|
||||
data[type].bookmark = bookmark;
|
||||
|
||||
store.writeQuery({
|
||||
data,
|
||||
query,
|
||||
variables
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
mutation: ADD_NOTE_MUTATION,
|
||||
variables: {
|
||||
input: {
|
||||
note: n
|
||||
}
|
||||
},
|
||||
update,
|
||||
optimisticResponse: {
|
||||
__typename: 'Mutation',
|
||||
addNote: {
|
||||
__typename: 'AddNotePayload',
|
||||
note: {
|
||||
__typename: 'NoteNode',
|
||||
id: -1,
|
||||
text: n.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -22,6 +22,7 @@ export default new Vuex.Store({
|
|||
parentProject: null,
|
||||
currentNote: null,
|
||||
currentProjectEntry: null,
|
||||
currentNoteParent: '',
|
||||
imageUrl: '',
|
||||
infographic: {
|
||||
id: 0,
|
||||
|
|
@ -46,6 +47,7 @@ export default new Vuex.Store({
|
|||
currentContent: state => state.currentContent,
|
||||
currentContentBlock: state => state.currentContentBlock,
|
||||
currentNote: state => state.currentNote,
|
||||
currentNoteParent: state => state.currentNoteParent,
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
|
@ -61,6 +63,7 @@ export default new Vuex.Store({
|
|||
commit('setCurrentRoomEntry', '');
|
||||
commit('setCurrentContent', '');
|
||||
commit('setCurrentContentBlock', '');
|
||||
commit('setCurrentNoteParent', '');
|
||||
commit('setContentBlockPosition', {});
|
||||
commit('setParentRoom', null);
|
||||
commit('setParentModule', '');
|
||||
|
|
@ -126,8 +129,12 @@ export default new Vuex.Store({
|
|||
dispatch('showModal', 'edit-project-entry-wizard');
|
||||
},
|
||||
addNote({commit, dispatch}, payload) {
|
||||
if (payload.contentBlock) {
|
||||
commit('setCurrentContentBlock', payload.contentBlock);
|
||||
commit('setCurrentContent', payload.content);
|
||||
} else {
|
||||
commit('setCurrentNoteParent', payload.parent);
|
||||
}
|
||||
dispatch('showModal', 'new-note-wizard');
|
||||
},
|
||||
editNote({commit, dispatch}, payload) {
|
||||
|
|
@ -240,6 +247,9 @@ export default new Vuex.Store({
|
|||
},
|
||||
setEditModule(state, payload) {
|
||||
state.editModule = payload;
|
||||
},
|
||||
setCurrentNoteParent(state, payload) {
|
||||
state.currentNoteParent = payload;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 2.0.6 on 2019-11-28 16:01
|
||||
|
||||
from django.db import migrations
|
||||
import wagtail.core.blocks
|
||||
import wagtail.core.fields
|
||||
import wagtail.images.blocks
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('basicknowledge', '0003_auto_20190912_1228'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='basicknowledge',
|
||||
name='contents',
|
||||
field=wagtail.core.fields.StreamField([('text_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['bold', 'ul']))])), ('image_block', wagtail.images.blocks.ImageChooserBlock()), ('link_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('video_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('document_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('section_title', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock())])), ('infogram_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock()), ('title', wagtail.core.blocks.TextBlock())])), ('genially_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock())])), ('thinglink_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock())])), ('subtitle', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock())]))], blank=True, null=True),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# Generated by Django 2.0.6 on 2019-11-28 16:01
|
||||
|
||||
import assignments.models
|
||||
from django.db import migrations
|
||||
import surveys.models
|
||||
import wagtail.core.blocks
|
||||
import wagtail.core.fields
|
||||
import wagtail.images.blocks
|
||||
import wagtail.snippets.blocks
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('books', '0015_contentblock_bookmarks'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='contentblock',
|
||||
name='contents',
|
||||
field=wagtail.core.fields.StreamField([('text_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))])), ('basic_knowledge', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.RichTextBlock(required=False)), ('basic_knowledge', wagtail.core.blocks.PageChooserBlock(required=True, target_model=['basicknowledge.BasicKnowledge']))])), ('assignment', wagtail.core.blocks.StructBlock([('assignment_id', wagtail.snippets.blocks.SnippetChooserBlock(assignments.models.Assignment))])), ('survey', wagtail.core.blocks.StructBlock([('survey_id', wagtail.snippets.blocks.SnippetChooserBlock(surveys.models.Survey))])), ('image_block', wagtail.images.blocks.ImageChooserBlock()), ('image_url_block', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('link_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('solution', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))], icon='tick')), ('video_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('document_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('infogram_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock()), ('title', wagtail.core.blocks.TextBlock())])), ('genially_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock())])), ('thinglink_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock())])), ('subtitle', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock())])), ('module_room_slug', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock())])), ('content_list_item', wagtail.core.blocks.StreamBlock([('text_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))])), ('basic_knowledge', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.RichTextBlock(required=False)), ('basic_knowledge', wagtail.core.blocks.PageChooserBlock(required=True, target_model=['basicknowledge.BasicKnowledge']))])), ('assignment', wagtail.core.blocks.StructBlock([('assignment_id', wagtail.snippets.blocks.SnippetChooserBlock(assignments.models.Assignment))])), ('survey', wagtail.core.blocks.StructBlock([('survey_id', wagtail.snippets.blocks.SnippetChooserBlock(surveys.models.Survey))])), ('image_block', wagtail.images.blocks.ImageChooserBlock()), ('image_url_block', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('link_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('solution', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))], icon='tick')), ('video_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('document_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('infogram_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock()), ('title', wagtail.core.blocks.TextBlock())])), ('genially_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock())])), ('thinglink_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock())])), ('subtitle', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock())])), ('module_room_slug', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock())]))]))], blank=True, null=True),
|
||||
),
|
||||
]
|
||||
|
|
@ -5,8 +5,8 @@ from graphene_django.filter import DjangoFilterConnectionField
|
|||
|
||||
from api.utils import get_object
|
||||
from books.utils import are_solutions_enabled_for
|
||||
from notes.models import ContentBlockBookmark
|
||||
from notes.schema import ContentBlockBookmarkNode
|
||||
from notes.models import ContentBlockBookmark, ChapterBookmark, ModuleBookmark
|
||||
from notes.schema import ContentBlockBookmarkNode, ChapterBookmarkNode, ModuleBookmarkNode
|
||||
from rooms.models import ModuleRoomSlug
|
||||
from ..models import Book, Topic, Module, Chapter, ContentBlock
|
||||
|
||||
|
|
@ -66,6 +66,7 @@ class ContentBlockNode(DjangoObjectType):
|
|||
|
||||
class ChapterNode(DjangoObjectType):
|
||||
content_blocks = DjangoFilterConnectionField(ContentBlockNode)
|
||||
bookmark = graphene.Field(ChapterBookmarkNode)
|
||||
|
||||
class Meta:
|
||||
model = Chapter
|
||||
|
|
@ -96,6 +97,11 @@ class ChapterNode(DjangoObjectType):
|
|||
|
||||
return publisher_content_blocks.union(user_created_content_blocks)
|
||||
|
||||
def resolve_bookmark(self, info, **kwags):
|
||||
return ChapterBookmark.objects.filter(
|
||||
user=info.context.user,
|
||||
chapter=self
|
||||
).first()
|
||||
|
||||
class ModuleNode(DjangoObjectType):
|
||||
pk = graphene.Int()
|
||||
|
|
@ -103,6 +109,7 @@ class ModuleNode(DjangoObjectType):
|
|||
topic = graphene.Field('books.schema.queries.TopicNode')
|
||||
hero_image = graphene.String()
|
||||
solutions_enabled = graphene.Boolean()
|
||||
bookmark = graphene.Field(ModuleBookmarkNode)
|
||||
|
||||
class Meta:
|
||||
model = Module
|
||||
|
|
@ -132,6 +139,12 @@ class ModuleNode(DjangoObjectType):
|
|||
teacher = info.context.user.get_teacher()
|
||||
return self.solutions_enabled_by.filter(pk=teacher.pk).exists() if teacher is not None else False
|
||||
|
||||
def resolve_bookmark(self, info, **kwags):
|
||||
return ModuleBookmark.objects.filter(
|
||||
user=info.context.user,
|
||||
module=self
|
||||
).first()
|
||||
|
||||
|
||||
class TopicNode(DjangoObjectType):
|
||||
pk = graphene.Int()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,139 @@
|
|||
from django.test import TestCase, RequestFactory
|
||||
from graphene.test import Client
|
||||
from graphql_relay import to_global_id
|
||||
|
||||
from api.schema import schema
|
||||
from api.utils import get_object, get_graphql_mutation
|
||||
from books.models import ContentBlock, Chapter
|
||||
from books.factories import ModuleFactory
|
||||
from core.factories import UserFactory
|
||||
from notes.factories import ChapterBookmarkFactory, ModuleBookmarkFactory
|
||||
from notes.models import Note, ModuleBookmark, ChapterBookmark
|
||||
|
||||
|
||||
class NoteMutationTestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.module = ModuleFactory()
|
||||
self.chapter = Chapter(title='Hello')
|
||||
self.module.add_child(instance=self.chapter)
|
||||
self.user = user = UserFactory(username='aschi')
|
||||
content_block = ContentBlock(title='bla', slug='bla')
|
||||
self.chapter.specific.add_child(instance=content_block)
|
||||
ChapterBookmarkFactory.create(chapter=self.chapter, user=user)
|
||||
ModuleBookmarkFactory.create(module=self.module, user=user)
|
||||
|
||||
request = RequestFactory().get('/')
|
||||
request.user = user
|
||||
|
||||
self.client = Client(schema=schema, context_value=request)
|
||||
self.add_mutation = get_graphql_mutation('addNote.gql')
|
||||
self.update_mutation = get_graphql_mutation('updateNote.gql')
|
||||
|
||||
|
||||
class NewNoteMutationTestCase(NoteMutationTestCase):
|
||||
def test_add_chapter_note(self):
|
||||
self.assertEqual(Note.objects.count(), 0)
|
||||
text = 'Hello World'
|
||||
result = self.client.execute(self.add_mutation, variables={
|
||||
'input': {
|
||||
'note': {
|
||||
'parent': to_global_id('ChapterNode', self.chapter.pk),
|
||||
'text': text
|
||||
}
|
||||
}
|
||||
})
|
||||
self.assertIsNone(result.get('errors'))
|
||||
|
||||
self.assertEqual(Note.objects.count(), 1)
|
||||
self.assertEqual(Note.objects.first().text, text)
|
||||
|
||||
def test_add_module_note(self):
|
||||
self.assertEqual(Note.objects.count(), 0)
|
||||
text = 'Hello World'
|
||||
result = self.client.execute(self.add_mutation, variables={
|
||||
'input': {
|
||||
'note': {
|
||||
'parent': to_global_id('ModuleNode', self.module.pk),
|
||||
'text': text
|
||||
}
|
||||
}
|
||||
})
|
||||
self.assertIsNone(result.get('errors'))
|
||||
|
||||
self.assertEqual(Note.objects.count(), 1)
|
||||
self.assertEqual(Note.objects.first().text, text)
|
||||
|
||||
|
||||
class UpdateNoteMutationTestCase(NoteMutationTestCase):
|
||||
def setUp(self):
|
||||
super(UpdateNoteMutationTestCase, self).setUp()
|
||||
|
||||
self.chapter_note = Note.objects.create(text='Hello World')
|
||||
self.module_note = Note.objects.create(text='Hello World')
|
||||
|
||||
self.chapter_bookmark = ChapterBookmark.objects.get(user=self.user, chapter=self.chapter)
|
||||
self.chapter_bookmark.note = self.chapter_note
|
||||
self.chapter_bookmark.save()
|
||||
|
||||
self.module_bookmark = ModuleBookmark.objects.get(user=self.user, module=self.module)
|
||||
self.module_bookmark.note = self.module_note
|
||||
self.module_bookmark.save()
|
||||
|
||||
def test_change_module_note(self):
|
||||
self.assertEqual(Note.objects.count(), 2)
|
||||
self.assertEqual(self.module_bookmark.note.text, 'Hello World')
|
||||
|
||||
new_text = 'Salut monde'
|
||||
result = self.client.execute(self.update_mutation, variables={
|
||||
'input': {
|
||||
'note': {
|
||||
'id': to_global_id('NoteNode', self.module_bookmark.note.pk),
|
||||
'text': new_text
|
||||
}
|
||||
}
|
||||
})
|
||||
self.assertIsNone(result.get('errors'))
|
||||
|
||||
bookmark = ModuleBookmark.objects.get(user=self.user, module=self.module)
|
||||
self.assertEqual(bookmark.note.text, new_text)
|
||||
|
||||
self.assertEqual(Note.objects.count(), 2)
|
||||
|
||||
def test_change_chapter_note(self):
|
||||
self.assertEqual(Note.objects.count(), 2)
|
||||
self.assertEqual(self.chapter_bookmark.note.text, 'Hello World')
|
||||
|
||||
new_text = 'Salut monde'
|
||||
result = self.client.execute(self.update_mutation, variables={
|
||||
'input': {
|
||||
'note': {
|
||||
'id': to_global_id('NoteNode', self.chapter_bookmark.note.pk),
|
||||
'text': new_text
|
||||
}
|
||||
}
|
||||
})
|
||||
self.assertIsNone(result.get('errors'))
|
||||
|
||||
bookmark = ChapterBookmark.objects.get(user=self.user, chapter=self.chapter)
|
||||
self.assertEqual(bookmark.note.text, new_text)
|
||||
|
||||
self.assertEqual(Note.objects.count(), 2)
|
||||
|
||||
def test_update_wrong_user(self):
|
||||
godis_note = Note.objects.create(text='Hello Godi')
|
||||
godi = UserFactory(username='godi')
|
||||
godis_bookmark = ModuleBookmarkFactory(module=self.module, user=godi)
|
||||
|
||||
godis_bookmark.note = godis_note
|
||||
godis_bookmark.save()
|
||||
|
||||
result = self.client.execute(self.update_mutation, variables={
|
||||
'input': {
|
||||
'note': {
|
||||
'id': to_global_id('NoteNode', godis_note.pk),
|
||||
'text': 'Hello Aschi'
|
||||
}
|
||||
}
|
||||
})
|
||||
print(result.get('errors'))
|
||||
self.assertIsNotNone(result.get('errors'))
|
||||
|
|
@ -47,6 +47,7 @@ objective_groups_1 = [
|
|||
|
||||
module_1_chapter_1 = {
|
||||
'title': '1.1 Lehrbeginn',
|
||||
'description': 'Wie sieht Ihr Konsumverhalten aus?',
|
||||
'content_blocks': [
|
||||
{
|
||||
'type': 'task',
|
||||
|
|
@ -186,6 +187,7 @@ module_1_chapter_1 = {
|
|||
}
|
||||
module_1_chapter_2 = {
|
||||
'title': '1.2 Die drei Lernorte',
|
||||
'description': 'Haben Sie sich beim Shoppen schon mal überlegt, aus welchem Beweggrund Sie ein bestimmtes Produkt eigentlich unbedingt haben wollten? Wir gehen im Folgenden anhand Ihres letzten Kleiderkaufs dieser Frage nach.',
|
||||
'content_blocks': [
|
||||
{
|
||||
'type': 'base_society',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
from django.core.management import BaseCommand
|
||||
|
||||
from notes.models import ContentBlockBookmark, ModuleBookmark, ChapterBookmark
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
def handle(self, *args, **options):
|
||||
self.stdout.write("Preparing bookmarks")
|
||||
ContentBlockBookmark.objects.all().delete()
|
||||
ModuleBookmark.objects.all().delete()
|
||||
ChapterBookmark.objects.all().delete()
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
import factory
|
||||
from notes.models import ChapterBookmark, ModuleBookmark
|
||||
|
||||
|
||||
class ChapterBookmarkFactory(factory.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = ChapterBookmark
|
||||
|
||||
class ModuleBookmarkFactory(factory.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = ModuleBookmark
|
||||
|
|
@ -3,8 +3,9 @@ from graphene import InputObjectType
|
|||
|
||||
|
||||
class AddNoteArgument(InputObjectType):
|
||||
content = graphene.UUID(required=True)
|
||||
content_block = graphene.ID(required=True)
|
||||
content = graphene.UUID()
|
||||
content_block = graphene.ID()
|
||||
parent = graphene.ID()
|
||||
text = graphene.String(required=True)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
# Generated by Django 2.0.6 on 2019-11-28 16:01
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('books', '0016_auto_20191128_1601'),
|
||||
('notes', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ChapterBookmark',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('chapter', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='books.Chapter')),
|
||||
('note', models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='notes.Note')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ModuleBookmark',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('module', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='books.Module')),
|
||||
('note', models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='notes.Note')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -10,7 +10,6 @@ class Note(models.Model):
|
|||
|
||||
|
||||
class Bookmark(models.Model):
|
||||
uuid = models.UUIDField(unique=True)
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
note = models.OneToOneField(Note, null=True, on_delete=models.SET_NULL)
|
||||
|
||||
|
|
@ -19,4 +18,13 @@ class Bookmark(models.Model):
|
|||
|
||||
|
||||
class ContentBlockBookmark(Bookmark):
|
||||
uuid = models.UUIDField(unique=True)
|
||||
content_block = models.ForeignKey('books.ContentBlock', on_delete=models.CASCADE)
|
||||
|
||||
|
||||
class ModuleBookmark(Bookmark):
|
||||
module = models.ForeignKey('books.Module', on_delete=models.CASCADE)
|
||||
|
||||
|
||||
class ChapterBookmark(Bookmark):
|
||||
chapter = models.ForeignKey('books.Chapter', on_delete=models.CASCADE)
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@ from builtins import PermissionError
|
|||
import graphene
|
||||
import json
|
||||
from graphene import relay
|
||||
from graphql_relay import from_global_id
|
||||
|
||||
from api.utils import get_object
|
||||
from books.models import ContentBlock
|
||||
from books.models import ContentBlock, Chapter, Module
|
||||
from notes.inputs import AddNoteArgument, UpdateNoteArgument
|
||||
from notes.models import ContentBlockBookmark, Note
|
||||
from notes.models import ContentBlockBookmark, Note, ChapterBookmark, ModuleBookmark
|
||||
from notes.schema import NoteNode
|
||||
|
||||
|
||||
|
|
@ -56,16 +57,31 @@ class AddNote(relay.ClientIDMutation):
|
|||
user = info.context.user
|
||||
|
||||
note = kwargs.get('note')
|
||||
content_uuid = note.get('content')
|
||||
content_block_id = note.get('content_block')
|
||||
content_block = get_object(ContentBlock, content_block_id)
|
||||
content_uuid = note.get('content', '')
|
||||
content_block_id = note.get('content_block', '')
|
||||
parent = note.get('parent')
|
||||
text = note.get('text')
|
||||
|
||||
if content_uuid != '':
|
||||
content_block = get_object(ContentBlock, content_block_id)
|
||||
|
||||
bookmark = ContentBlockBookmark.objects.get(
|
||||
content_block=content_block,
|
||||
uuid=content_uuid,
|
||||
user=user
|
||||
)
|
||||
else:
|
||||
type, id = from_global_id(parent)
|
||||
if type == 'ModuleNode':
|
||||
bookmark = ModuleBookmark.objects.get(
|
||||
module__id=id,
|
||||
user=user
|
||||
)
|
||||
else:
|
||||
bookmark = ChapterBookmark.objects.get(
|
||||
chapter__id=id,
|
||||
user=user
|
||||
)
|
||||
|
||||
bookmark.note = Note.objects.create(text=text)
|
||||
bookmark.save()
|
||||
|
|
@ -88,7 +104,9 @@ class UpdateNote(relay.ClientIDMutation):
|
|||
text = note.get('text')
|
||||
note = get_object(Note, id)
|
||||
|
||||
if note.contentblockbookmark.user != user:
|
||||
if hasattr(note, 'contentblockbookmark') and note.contentblockbookmark.user != user \
|
||||
or hasattr(note, 'chapterbookmark') and note.chapterbookmark.user != user \
|
||||
or hasattr(note, 'modulebookmark') and note.modulebookmark.user != user:
|
||||
raise PermissionError
|
||||
|
||||
note.text = text
|
||||
|
|
@ -96,7 +114,67 @@ class UpdateNote(relay.ClientIDMutation):
|
|||
return cls(note=note)
|
||||
|
||||
|
||||
class UpdateChapterBookmark(relay.ClientIDMutation):
|
||||
class Input:
|
||||
chapter = graphene.ID(required=True)
|
||||
bookmarked = graphene.Boolean(required=True)
|
||||
|
||||
success = graphene.Boolean()
|
||||
|
||||
@classmethod
|
||||
def mutate_and_get_payload(cls, root, info, **kwargs):
|
||||
user = info.context.user
|
||||
chapter_id = kwargs.get('chapter')
|
||||
bookmarked = kwargs.get('bookmarked')
|
||||
|
||||
chapter = get_object(Chapter, chapter_id)
|
||||
|
||||
if bookmarked:
|
||||
ChapterBookmark.objects.create(
|
||||
chapter=chapter,
|
||||
user=user
|
||||
)
|
||||
else:
|
||||
ChapterBookmark.objects.get(
|
||||
chapter=chapter,
|
||||
user=user
|
||||
).delete()
|
||||
|
||||
return cls(success=True)
|
||||
|
||||
|
||||
class UpdateModuleBookmark(relay.ClientIDMutation):
|
||||
class Input:
|
||||
module = graphene.ID(required=True)
|
||||
bookmarked = graphene.Boolean(required=True)
|
||||
|
||||
success = graphene.Boolean()
|
||||
|
||||
@classmethod
|
||||
def mutate_and_get_payload(cls, root, info, **kwargs):
|
||||
user = info.context.user
|
||||
module_id = kwargs.get('module')
|
||||
bookmarked = kwargs.get('bookmarked')
|
||||
|
||||
module = get_object(Module, module_id)
|
||||
|
||||
if bookmarked:
|
||||
ModuleBookmark.objects.create(
|
||||
module=module,
|
||||
user=user
|
||||
)
|
||||
else:
|
||||
ModuleBookmark.objects.get(
|
||||
module=module,
|
||||
user=user
|
||||
).delete()
|
||||
|
||||
return cls(success=True)
|
||||
|
||||
|
||||
class NoteMutations:
|
||||
add_note = AddNote.Field()
|
||||
update_note = UpdateNote.Field()
|
||||
update_content_bookmark = UpdateContentBookmark.Field()
|
||||
update_chapter_bookmark = UpdateChapterBookmark.Field()
|
||||
update_module_bookmark = UpdateModuleBookmark.Field()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import graphene
|
|||
from graphene import relay
|
||||
from graphene_django import DjangoObjectType
|
||||
|
||||
from notes.models import Note, ContentBlockBookmark
|
||||
from notes.models import Note, ContentBlockBookmark, ModuleBookmark, ChapterBookmark
|
||||
|
||||
|
||||
class NoteNode(DjangoObjectType):
|
||||
|
|
@ -23,3 +23,17 @@ class ContentBlockBookmarkNode(DjangoObjectType):
|
|||
|
||||
class Meta:
|
||||
model = ContentBlockBookmark
|
||||
|
||||
|
||||
class ModuleBookmarkNode(DjangoObjectType):
|
||||
note = graphene.Field(NoteNode)
|
||||
|
||||
class Meta:
|
||||
model = ModuleBookmark
|
||||
|
||||
|
||||
class ChapterBookmarkNode(DjangoObjectType):
|
||||
note = graphene.Field(NoteNode)
|
||||
|
||||
class Meta:
|
||||
model = ChapterBookmark
|
||||
|
|
|
|||
Loading…
Reference in New Issue