Enable scrolling to the content blocks in a module

This commit is contained in:
Ramon Wenger 2019-12-18 16:28:24 +01:00
parent 65a09fb9c7
commit 9fb8e13c1c
9 changed files with 101 additions and 64 deletions

View File

@ -1,5 +1,5 @@
<template>
<div class="chapter">
<div class="chapter" :data-scrollto="chapter.id">
<h3 :id="'chapter-' + index">{{chapter.title}}</h3>
<bookmark-actions

View File

@ -1,5 +1,7 @@
<template>
<div class="content-component" :class="{'content-component--bookmarked': bookmarked}">
<div class="content-component"
:class="{'content-component--bookmarked': bookmarked}"
:data-scrollto="component.id">
<bookmark-actions
v-if="showBookmarkActions"
@add-note="addNote(component.id)"

View File

@ -1,5 +1,5 @@
<template>
<div class="survey-block">
<div class="survey-block" :data-scrollto="value.id">
<router-link class="button button--primary"
:to="{name: 'survey', params: {id:value.id}}">Übung anzeigen
</router-link>

View File

@ -1,5 +1,5 @@
<template>
<div class="assignment" :data-scrollto="assignment.id">
<div class="assignment" :data-scrollto="value.id">
<p class="assignment__assignment-text">
{{assignment.assignment}}
</p>

View File

@ -6,7 +6,7 @@
<div class="module-activity__entry module-activity-entry"
v-for="submission in module.mySubmissions"
:key="submission.id"
@click="goToAssignment(submission.assignment.id)">
@click="goTo(submission.assignment.id)">
<h3 class="module-activity-entry__title">Aufgabe & Ergebnis</h3>
<p class="module-activity-entry__text">
{{submission.text}}
@ -14,7 +14,8 @@
</div>
<div class="module-activity__entry module-activity-entry"
v-for="answer in module.myAnswers"
:key="answer.id">
:key="answer.id"
@click="goTo(answer.survey.id)">
<h3 class="module-activity-entry__title">Übung</h3>
<p class="module-activity-entry__text">
{{answer.survey.title}}
@ -23,12 +24,14 @@
</div>
<div class="module-activity__entry"
v-for="bookmark in module.myContentBookmarks"
:key="bookmark.id">
:key="bookmark.id"
@click="goTo(bookmark.uuid)">
<content-bookmark :bookmark="bookmark"></content-bookmark>
</div>
<div class="module-activity__entry"
v-for="bookmark in module.myChapterBookmarks"
:key="bookmark.id">
:key="bookmark.id"
@click="goTo(bookmark.chapter.id)">
<h3 class="module-activity-entry__title">Lesezeichen</h3>
<p class="module-activity-entry__text">
{{bookmark.chapter.description}}
@ -51,6 +54,8 @@
import {mapActions} from 'vuex'
import ContentBookmark from '@/components/profile/ContentBookmark';
import SCROLL_TO_MUTATION from '@/graphql/gql/local/mutations/scrollTo.gql';
export default {
props: ['module'],
components: {
@ -74,9 +79,14 @@
},
methods: {
...mapActions(['scrollToAssignmentId']),
goToAssignment(id) {
goTo(scrollTo) {
const url = `/module/${this.module.slug}/`;
this.scrollToAssignmentId(id);
this.$apollo.mutate({
mutation: SCROLL_TO_MUTATION,
variables: {
scrollTo
}
});
this.$router.push(url);
}
}

View File

@ -0,0 +1,3 @@
mutation($scrollTo: String!) {
scrollTo(scrollTo: $scrollTo) @client
}

View File

@ -0,0 +1,5 @@
query ScrollPosition {
scrollPosition @client {
scrollTo
}
}

View File

@ -3,9 +3,12 @@
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
import {mapGetters, mapActions} from 'vuex'
import ASSIGNMENTS_QUERY from '@/graphql/gql/assignmentsQuery.gql';
import {moduleQuery} from '@/graphql/queries';
import MODULE_DETAILS_QUERY from '@/graphql/gql/moduleDetailsQuery.gql';
import SCROLL_POSITION from '@/graphql/gql/local/scrollPosition.gql';
import SCROLL_TO_MUTATION from '@/graphql/gql/local/mutations/scrollTo.gql';
import Module from '@/components/modules/Module.vue';
@ -16,23 +19,65 @@
methods: {
...mapActions(['scrollToAssignmentReady', 'scrollingToAssignment']),
scrollTo() {
if (this.scrollPosition && this.scrollPosition.scrollTo) {
let options = {
container: 'body',
easing: 'ease',
offset: -60,
onStart: (element) => {
},
onDone: (element) => {
},
onCancel: function () {
// scrolling has been interrupted
},
x: false,
y: true
};
setTimeout(() => {
this.$scrollTo(`[data-scrollto="${this.scrollPosition.scrollTo}"]`, 1000, options);
this.$apollo.mutate({
mutation: SCROLL_TO_MUTATION,
variables: {
scrollTo: ''
}
})
}, 250); // unfortunately this timeout is needed as it is hard to tell when everything is rendered
}
}
},
computed: {
...mapGetters({
scrollToAssignmentId: 'scrollToAssignmentId',
isScrollingToAssignment: 'scrollingToAssignment',
editModule: 'editModule'
}),
editModule: 'editModule'
}),
},
apollo: {
module: moduleQuery,
module() {
return {
query: MODULE_DETAILS_QUERY,
variables: {
slug: this.$route.params.slug
},
update(data) {
return this.$getRidOfEdges(data).module || {};
},
result() {
// scroll only after the module has been loaded completely
this.scrollTo();
}
}
},
assignments() {
return {
query: ASSIGNMENTS_QUERY
}
}
},
scrollPosition: {
query: SCROLL_POSITION
},
},
data() {
@ -41,34 +86,5 @@
assignments: []
}
},
mounted () {
this.$store.subscribe((mutation, state) => {
if (mutation.type === 'setScrollToAssignmentReady' && mutation.payload && !this.isScrollingToAssignment) {
this.scrollingToAssignment(true);
let options = {
container: 'body',
easing: 'ease',
offset: -60,
onStart: (element) => {
},
onDone: (element) => {
this.scrollToAssignmentReady(false);
this.scrollingToAssignment(false);
},
onCancel: function() {
// scrolling has been interrupted
},
x: false,
y: true
};
setTimeout(() => {
this.$scrollTo(`[data-scrollto="${this.scrollToAssignmentId}"]`, 1000, options);
}, 250) // unfortunately this timeout is needed as it is hard to tell when everything is rendered
}
})
},
beforeDestroy () {
this.scrollToAssignmentReady(false);
}
}
</script>

View File

@ -225,34 +225,35 @@ class BookNode(DjangoObjectType):
return Topic.get_by_parent(self)
class FilteredChapterNode(DjangoObjectType):
content_blocks = DjangoFilterConnectionField(ContentBlockNode)
class Meta:
model = Chapter
only_fields = [
'slug', 'title', 'description',
]
filter_fields = [
'slug', 'title',
]
interfaces = (relay.Node,)
def resolve_content_blocks(self, *args, **kwargs):
return ContentBlock.get_by_parent(self)
# todo: do we need this?
# class FilteredChapterNode(DjangoObjectType):
# content_blocks = DjangoFilterConnectionField(ContentBlockNode)
#
# class Meta:
# model = Chapter
# only_fields = [
# 'slug', 'title', 'description',
# ]
# filter_fields = [
# 'slug', 'title',
# ]
# interfaces = (relay.Node,)
#
# def resolve_content_blocks(self, *args, **kwargs):
# return ContentBlock.get_by_parent(self)
class BookQuery(object):
book = relay.Node.Field(BookNode)
topic = graphene.Field(TopicNode, slug=graphene.String())
module = graphene.Field(ModuleNode, slug=graphene.String(), id=graphene.ID())
chapter = relay.Node.Field(FilteredChapterNode)
chapter = relay.Node.Field(ChapterNode)
content_block = relay.Node.Field(ContentBlockNode)
books = DjangoFilterConnectionField(BookNode)
topics = DjangoFilterConnectionField(TopicNode)
modules = DjangoFilterConnectionField(ModuleNode)
chapters = DjangoFilterConnectionField(FilteredChapterNode)
chapters = DjangoFilterConnectionField(ChapterNode)
def resolve_books(self, *args, **kwargs):
return Book.objects.filter(**kwargs).live()