Merged in feature/save-last-topic (pull request #67)
Save last topic and go there on topic navigation Approved-by: Christian Cueni
This commit is contained in:
commit
da5bd1e439
File diff suppressed because one or more lines are too long
|
|
@ -19,6 +19,11 @@ describe('Current Module', () => {
|
|||
'slug': 'lohn-und-budget',
|
||||
'__typename': 'ModuleNode'
|
||||
},
|
||||
'lastTopic': {
|
||||
'id': 'VG9waWNOb2RlOjU=',
|
||||
'slug': 'geld-und-kauf',
|
||||
'__typename': 'TopicNode'
|
||||
},
|
||||
'__typename': 'UserNode',
|
||||
'permissions': []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<div class="content-navigation__item">
|
||||
<router-link
|
||||
:class="{'content-navigation__link--active': isActive('book')}"
|
||||
to="/book/topic/berufliche-grundbildung"
|
||||
:to="topicRoute"
|
||||
active-class="content-navigation__link--active"
|
||||
class="content-navigation__link"
|
||||
@click.native="close">Themen
|
||||
|
|
@ -73,6 +73,7 @@
|
|||
import BookTopicNavigation from '@/components/book-navigation/BookTopicNavigation';
|
||||
|
||||
import sidebarMixin from '@/mixins/sidebar';
|
||||
import meMixin from '@/mixins/me';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
|
@ -81,13 +82,27 @@
|
|||
}
|
||||
},
|
||||
|
||||
mixins: [sidebarMixin],
|
||||
mixins: [sidebarMixin, meMixin],
|
||||
|
||||
components: {
|
||||
BookTopicNavigation,
|
||||
Logo
|
||||
},
|
||||
|
||||
computed: {
|
||||
topicRoute() {
|
||||
if (this.me.lastTopic && this.me.lastTopic.slug) {
|
||||
return {
|
||||
name: 'topic',
|
||||
params: {
|
||||
topicSlug: this.me.lastTopic.slug
|
||||
}
|
||||
}
|
||||
}
|
||||
return '/book/topic/berufliche-grundbildung'
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
isActive(linkName) {
|
||||
return linkName === 'book' && this.$route.path.indexOf('module') > -1;
|
||||
|
|
|
|||
|
|
@ -57,8 +57,8 @@
|
|||
}
|
||||
},
|
||||
update(store, {data: {updateSchoolClass: {schoolClass: {name}}}}) {
|
||||
let query = MY_SCHOOL_CLASS_QUERY;
|
||||
let data = store.readQuery({query});
|
||||
const query = MY_SCHOOL_CLASS_QUERY;
|
||||
const data = store.readQuery({query});
|
||||
data.me.selectedClass.name = name;
|
||||
store.writeQuery({query, data});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
#import "./moduleParts.gql"
|
||||
fragment TopicParts on TopicNode {
|
||||
id
|
||||
title
|
||||
teaser
|
||||
slug
|
||||
description
|
||||
vimeoId
|
||||
instructions
|
||||
modules {
|
||||
edges {
|
||||
node {
|
||||
...ModuleParts
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,10 @@ fragment UserParts on UserNode {
|
|||
id
|
||||
slug
|
||||
}
|
||||
lastTopic {
|
||||
id
|
||||
slug
|
||||
}
|
||||
selectedClass {
|
||||
id
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
#import "../fragments/topicParts.gql"
|
||||
mutation UpdateLastTopic($input: UpdateLastTopicInput!) {
|
||||
updateLastTopic(input: $input) {
|
||||
topic {
|
||||
...TopicParts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,6 @@
|
|||
#import "./fragments/moduleParts.gql"
|
||||
#import "./fragments/topicParts.gql"
|
||||
query Topic($slug: String!){
|
||||
topic(slug: $slug) {
|
||||
id
|
||||
title
|
||||
teaser
|
||||
description
|
||||
vimeoId
|
||||
instructions
|
||||
modules {
|
||||
edges {
|
||||
node {
|
||||
...ModuleParts
|
||||
}
|
||||
}
|
||||
}
|
||||
...TopicParts
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,10 +63,13 @@
|
|||
import PortfolioIllustration from '@/components/illustrations/PortfolioIllustration';
|
||||
import RoomsIllustration from '@/components/illustrations/RoomsIllustration';
|
||||
|
||||
import {meQuery} from '@/graphql/queries';
|
||||
import MobileHeader from '@/components/MobileHeader';
|
||||
|
||||
import meMixin from '@/mixins/me';
|
||||
|
||||
export default {
|
||||
|
||||
mixins: [meMixin],
|
||||
components: {
|
||||
MobileHeader,
|
||||
HeaderBar,
|
||||
|
|
@ -77,12 +80,6 @@
|
|||
RoomsIllustration
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
me: {}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
moduleRoute() {
|
||||
if (this.me.lastModule && this.me.lastModule.slug) {
|
||||
|
|
@ -98,9 +95,6 @@
|
|||
}
|
||||
},
|
||||
|
||||
apollo: {
|
||||
me: meQuery
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@
|
|||
import me from '@/mixins/me';
|
||||
import BookTopicNavigation from '@/components/book-navigation/BookTopicNavigation';
|
||||
|
||||
import UPDATE_LAST_TOPIC_MUTATION from '@/graphql/gql/mutations/updateLastTopic.gql';
|
||||
import ME_QUERY from '@/graphql/gql/meQuery.gql';
|
||||
|
||||
export default {
|
||||
|
||||
mixins: [me],
|
||||
|
|
@ -64,6 +67,12 @@
|
|||
},
|
||||
update(data) {
|
||||
return this.$getRidOfEdges(data).topic || {};
|
||||
},
|
||||
result(r, key) {
|
||||
if (this.saveMe) {
|
||||
this.saveMe = false;
|
||||
this.updateLastVisitedTopic(this.topic.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -75,7 +84,8 @@
|
|||
modules: {
|
||||
edges: []
|
||||
}
|
||||
}
|
||||
},
|
||||
saveMe: false
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -85,9 +95,37 @@
|
|||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (!this.topic.id) { // component was loaded before topic, apollo not ready yet
|
||||
this.saveMe = true; // needs saving, apollo will do this
|
||||
} else {
|
||||
this.updateLastVisitedTopic(this.topic.id);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
openVideo() {
|
||||
this.$store.dispatch('showFullscreenVideo', this.topic.vimeoId);
|
||||
},
|
||||
updateLastVisitedTopic(topicId) {
|
||||
this.$apollo.mutate({
|
||||
mutation: UPDATE_LAST_TOPIC_MUTATION,
|
||||
variables: {
|
||||
input: {
|
||||
id: topicId
|
||||
}
|
||||
},
|
||||
update(store, {data: {updateLastTopic: {topic}}}) {
|
||||
if (topic) {
|
||||
const query = ME_QUERY;
|
||||
const data = store.readQuery({query});
|
||||
if (data) {
|
||||
data.me.lastTopic = topic;
|
||||
store.writeQuery({query, data})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from books.schema.mutations.contentblock import MutateContentBlock, AddContentBlock, DeleteContentBlock
|
||||
from books.schema.mutations.module import UpdateSolutionVisibility, UpdateLastModule
|
||||
from books.schema.mutations.module import UpdateSolutionVisibility, UpdateLastModule, UpdateLastTopic
|
||||
|
||||
|
||||
class BookMutations(object):
|
||||
|
|
@ -8,3 +8,4 @@ class BookMutations(object):
|
|||
delete_content_block = DeleteContentBlock.Field()
|
||||
update_solution_visibility = UpdateSolutionVisibility.Field()
|
||||
update_last_module = UpdateLastModule.Field()
|
||||
update_last_topic = UpdateLastTopic.Field()
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import graphene
|
|||
from graphene import relay
|
||||
|
||||
from api.utils import get_errors, get_object
|
||||
from books.models import Module
|
||||
from books.schema.queries import ModuleNode
|
||||
from books.models import Module, Topic
|
||||
from books.schema.queries import ModuleNode, TopicNode
|
||||
|
||||
|
||||
class UpdateSolutionVisibility(relay.ClientIDMutation):
|
||||
|
|
@ -71,3 +71,26 @@ class UpdateLastModule(relay.ClientIDMutation):
|
|||
except Exception as e:
|
||||
errors = ['Error: {}'.format(e)]
|
||||
return cls(errors=errors)
|
||||
|
||||
|
||||
class UpdateLastTopic(relay.ClientIDMutation):
|
||||
class Input:
|
||||
# todo: use slug here too
|
||||
id = graphene.ID()
|
||||
|
||||
topic = graphene.Field(TopicNode)
|
||||
|
||||
@classmethod
|
||||
def mutate_and_get_payload(cls, root, info, **args):
|
||||
user = info.context.user
|
||||
id = args.get('id')
|
||||
|
||||
topic = get_object(Topic, id)
|
||||
if not topic:
|
||||
raise Topic.DoesNotExist
|
||||
|
||||
user.last_topic = topic
|
||||
user.save()
|
||||
|
||||
return cls(topic=topic)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
# Generated by Django 2.1.15 on 2020-06-15 14:24
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('books', '0021_auto_20200525_1447'),
|
||||
('users', '0017_auto_20200430_1251'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='last_topic',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='books.Topic'),
|
||||
),
|
||||
]
|
||||
|
|
@ -17,6 +17,7 @@ DEFAULT_SCHOOL_ID = 1
|
|||
|
||||
class User(AbstractUser):
|
||||
last_module = models.ForeignKey('books.Module', related_name='+', on_delete=models.SET_NULL, null=True)
|
||||
last_topic = models.ForeignKey('books.Topic', related_name='+', on_delete=models.SET_NULL, null=True)
|
||||
avatar_url = models.CharField(max_length=254, blank=True, default='')
|
||||
email = models.EmailField(_('email address'), unique=True)
|
||||
hep_id = models.PositiveIntegerField(null=True, blank=False)
|
||||
|
|
|
|||
|
|
@ -50,7 +50,8 @@ class UserNode(DjangoObjectType):
|
|||
class Meta:
|
||||
model = User
|
||||
filter_fields = ['username', 'email']
|
||||
only_fields = ['username', 'email', 'first_name', 'last_name', 'school_classes', 'last_module', 'avatar_url',
|
||||
only_fields = ['username', 'email', 'first_name', 'last_name', 'school_classes', 'last_module',
|
||||
'last_topic', 'avatar_url',
|
||||
'selected_class', 'expiry_date']
|
||||
interfaces = (relay.Node,)
|
||||
|
||||
|
|
@ -64,7 +65,7 @@ class UserNode(DjangoObjectType):
|
|||
return self.selected_class()
|
||||
|
||||
def resolve_expiry_date(self, info):
|
||||
if not self.hep_id: # concerns users that already have an (old) account
|
||||
if not self.hep_id: # concerns users that already have an (old) account
|
||||
return format(datetime(2020, 7, 31), 'U') # just set some expiry date
|
||||
else:
|
||||
return self.license_expiry_date
|
||||
|
|
|
|||
Loading…
Reference in New Issue