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> <template>
<div class="chapter"> <div class="chapter" :data-scrollto="chapter.id">
<h3 :id="'chapter-' + index">{{chapter.title}}</h3> <h3 :id="'chapter-' + index">{{chapter.title}}</h3>
<bookmark-actions <bookmark-actions

View File

@ -1,5 +1,7 @@
<template> <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 <bookmark-actions
v-if="showBookmarkActions" v-if="showBookmarkActions"
@add-note="addNote(component.id)" @add-note="addNote(component.id)"

View File

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

View File

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

View File

@ -6,7 +6,7 @@
<div class="module-activity__entry module-activity-entry" <div class="module-activity__entry module-activity-entry"
v-for="submission in module.mySubmissions" v-for="submission in module.mySubmissions"
:key="submission.id" :key="submission.id"
@click="goToAssignment(submission.assignment.id)"> @click="goTo(submission.assignment.id)">
<h3 class="module-activity-entry__title">Aufgabe & Ergebnis</h3> <h3 class="module-activity-entry__title">Aufgabe & Ergebnis</h3>
<p class="module-activity-entry__text"> <p class="module-activity-entry__text">
{{submission.text}} {{submission.text}}
@ -14,7 +14,8 @@
</div> </div>
<div class="module-activity__entry module-activity-entry" <div class="module-activity__entry module-activity-entry"
v-for="answer in module.myAnswers" 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> <h3 class="module-activity-entry__title">Übung</h3>
<p class="module-activity-entry__text"> <p class="module-activity-entry__text">
{{answer.survey.title}} {{answer.survey.title}}
@ -23,12 +24,14 @@
</div> </div>
<div class="module-activity__entry" <div class="module-activity__entry"
v-for="bookmark in module.myContentBookmarks" v-for="bookmark in module.myContentBookmarks"
:key="bookmark.id"> :key="bookmark.id"
@click="goTo(bookmark.uuid)">
<content-bookmark :bookmark="bookmark"></content-bookmark> <content-bookmark :bookmark="bookmark"></content-bookmark>
</div> </div>
<div class="module-activity__entry" <div class="module-activity__entry"
v-for="bookmark in module.myChapterBookmarks" 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> <h3 class="module-activity-entry__title">Lesezeichen</h3>
<p class="module-activity-entry__text"> <p class="module-activity-entry__text">
{{bookmark.chapter.description}} {{bookmark.chapter.description}}
@ -51,6 +54,8 @@
import {mapActions} from 'vuex' import {mapActions} from 'vuex'
import ContentBookmark from '@/components/profile/ContentBookmark'; import ContentBookmark from '@/components/profile/ContentBookmark';
import SCROLL_TO_MUTATION from '@/graphql/gql/local/mutations/scrollTo.gql';
export default { export default {
props: ['module'], props: ['module'],
components: { components: {
@ -74,9 +79,14 @@
}, },
methods: { methods: {
...mapActions(['scrollToAssignmentId']), ...mapActions(['scrollToAssignmentId']),
goToAssignment(id) { goTo(scrollTo) {
const url = `/module/${this.module.slug}/`; const url = `/module/${this.module.slug}/`;
this.scrollToAssignmentId(id); this.$apollo.mutate({
mutation: SCROLL_TO_MUTATION,
variables: {
scrollTo
}
});
this.$router.push(url); 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> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex' import {mapGetters, mapActions} from 'vuex'
import ASSIGNMENTS_QUERY from '@/graphql/gql/assignmentsQuery.gql'; 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'; import Module from '@/components/modules/Module.vue';
@ -16,23 +19,65 @@
methods: { methods: {
...mapActions(['scrollToAssignmentReady', 'scrollingToAssignment']), ...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: { computed: {
...mapGetters({ ...mapGetters({
scrollToAssignmentId: 'scrollToAssignmentId', editModule: 'editModule'
isScrollingToAssignment: 'scrollingToAssignment', }),
editModule: 'editModule'
}),
}, },
apollo: { 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() { assignments() {
return { return {
query: ASSIGNMENTS_QUERY query: ASSIGNMENTS_QUERY
} }
} },
scrollPosition: {
query: SCROLL_POSITION
},
}, },
data() { data() {
@ -41,34 +86,5 @@
assignments: [] 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> </script>

View File

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