Add instrument activities

This commit is contained in:
Ramon Wenger 2024-02-27 18:00:04 +01:00
parent cca1f8f1ac
commit 0e5eb0af0e
12 changed files with 122 additions and 123 deletions

View File

@ -58,3 +58,4 @@ export interface ActionOptions {
} }
export type ActivityProperty = 'myHighlights' | 'myBookmarks' | 'mySubmissions' | 'myAnswers'; export type ActivityProperty = 'myHighlights' | 'myBookmarks' | 'mySubmissions' | 'myAnswers';
export type InstrumentActivityProperty = 'highlights' | 'bookmarks';

View File

@ -36,7 +36,7 @@ const documents = {
"\n fragment ContentBlockHighlightsWithIdOnlyFragment on ContentBlockNode {\n highlights {\n id\n }\n }\n ": types.ContentBlockHighlightsWithIdOnlyFragmentFragmentDoc, "\n fragment ContentBlockHighlightsWithIdOnlyFragment on ContentBlockNode {\n highlights {\n id\n }\n }\n ": types.ContentBlockHighlightsWithIdOnlyFragmentFragmentDoc,
"\n mutation UpdateInstrumentBookmark($input: UpdateInstrumentBookmarkInput!) {\n updateInstrumentBookmark(input: $input) {\n success\n }\n }\n ": types.UpdateInstrumentBookmarkDocument, "\n mutation UpdateInstrumentBookmark($input: UpdateInstrumentBookmarkInput!) {\n updateInstrumentBookmark(input: $input) {\n success\n }\n }\n ": types.UpdateInstrumentBookmarkDocument,
"\n mutation UpdateContentBookmark($input: UpdateContentBookmarkInput!) {\n updateContentBookmark(input: $input) {\n success\n }\n }\n ": types.UpdateContentBookmarkDocument, "\n mutation UpdateContentBookmark($input: UpdateContentBookmarkInput!) {\n updateContentBookmark(input: $input) {\n success\n }\n }\n ": types.UpdateContentBookmarkDocument,
"\n query MyActivitiesQuery {\n myActivities {\n topics {\n id\n title\n modules {\n id\n slug\n title\n metaTitle\n myHighlights {\n ...HighlightParts\n }\n myBookmarks {\n ... on ChapterBookmarkNode {\n chapter {\n path\n }\n path\n note {\n id\n text\n }\n }\n ... on ContentBlockBookmarkNode {\n id\n uuid\n path\n contentBlock {\n id\n path\n }\n note {\n id\n text\n }\n }\n ... on ModuleBookmarkNode {\n path\n note {\n id\n text\n }\n }\n }\n mySubmissions {\n id\n text\n assignment {\n id\n title\n path\n module {\n slug\n }\n }\n }\n myAnswers {\n id\n survey {\n path\n id\n title\n }\n }\n }\n }\n }\n }\n ": types.MyActivitiesQueryDocument, "\n query MyActivitiesQuery {\n myActivities {\n instruments {\n id\n slug\n title\n path\n highlights {\n ...HighlightParts\n }\n bookmarks {\n ... on InstrumentBookmarkNode {\n path\n }\n }\n }\n topics {\n id\n title\n modules {\n id\n slug\n title\n metaTitle\n myHighlights {\n ...HighlightParts\n }\n myBookmarks {\n ... on ChapterBookmarkNode {\n chapter {\n path\n }\n path\n note {\n id\n text\n }\n }\n ... on ContentBlockBookmarkNode {\n id\n uuid\n path\n contentBlock {\n id\n path\n }\n note {\n id\n text\n }\n }\n ... on ModuleBookmarkNode {\n path\n note {\n id\n text\n }\n }\n }\n mySubmissions {\n id\n text\n assignment {\n id\n title\n path\n module {\n slug\n }\n }\n }\n myAnswers {\n id\n survey {\n path\n id\n title\n }\n }\n }\n }\n }\n }\n ": types.MyActivitiesQueryDocument,
"\n query ChapterQuery($id: ID!) {\n chapter(id: $id) {\n path\n }\n }\n ": types.ChapterQueryDocument, "\n query ChapterQuery($id: ID!) {\n chapter(id: $id) {\n path\n }\n }\n ": types.ChapterQueryDocument,
"\n query ContentBlockQuery($id: ID!) {\n contentBlock(id: $id) {\n path\n }\n }\n ": types.ContentBlockQueryDocument, "\n query ContentBlockQuery($id: ID!) {\n contentBlock(id: $id) {\n path\n }\n }\n ": types.ContentBlockQueryDocument,
"\n fragment InstrumentParts on InstrumentNode {\n id\n title\n intro\n slug\n language\n bookmarks {\n uuid\n note {\n id\n text\n }\n }\n type {\n id\n name\n category {\n id\n name\n foreground\n background\n }\n type\n }\n contents\n highlights {\n ...HighlightParts\n }\n }\n": types.InstrumentPartsFragmentDoc, "\n fragment InstrumentParts on InstrumentNode {\n id\n title\n intro\n slug\n language\n bookmarks {\n uuid\n note {\n id\n text\n }\n }\n type {\n id\n name\n category {\n id\n name\n foreground\n background\n }\n type\n }\n contents\n highlights {\n ...HighlightParts\n }\n }\n": types.InstrumentPartsFragmentDoc,
@ -156,7 +156,7 @@ export function graphql(source: "\n mutation UpdateContentBookmark($input
/** /**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */
export function graphql(source: "\n query MyActivitiesQuery {\n myActivities {\n topics {\n id\n title\n modules {\n id\n slug\n title\n metaTitle\n myHighlights {\n ...HighlightParts\n }\n myBookmarks {\n ... on ChapterBookmarkNode {\n chapter {\n path\n }\n path\n note {\n id\n text\n }\n }\n ... on ContentBlockBookmarkNode {\n id\n uuid\n path\n contentBlock {\n id\n path\n }\n note {\n id\n text\n }\n }\n ... on ModuleBookmarkNode {\n path\n note {\n id\n text\n }\n }\n }\n mySubmissions {\n id\n text\n assignment {\n id\n title\n path\n module {\n slug\n }\n }\n }\n myAnswers {\n id\n survey {\n path\n id\n title\n }\n }\n }\n }\n }\n }\n "): (typeof documents)["\n query MyActivitiesQuery {\n myActivities {\n topics {\n id\n title\n modules {\n id\n slug\n title\n metaTitle\n myHighlights {\n ...HighlightParts\n }\n myBookmarks {\n ... on ChapterBookmarkNode {\n chapter {\n path\n }\n path\n note {\n id\n text\n }\n }\n ... on ContentBlockBookmarkNode {\n id\n uuid\n path\n contentBlock {\n id\n path\n }\n note {\n id\n text\n }\n }\n ... on ModuleBookmarkNode {\n path\n note {\n id\n text\n }\n }\n }\n mySubmissions {\n id\n text\n assignment {\n id\n title\n path\n module {\n slug\n }\n }\n }\n myAnswers {\n id\n survey {\n path\n id\n title\n }\n }\n }\n }\n }\n }\n "]; export function graphql(source: "\n query MyActivitiesQuery {\n myActivities {\n instruments {\n id\n slug\n title\n path\n highlights {\n ...HighlightParts\n }\n bookmarks {\n ... on InstrumentBookmarkNode {\n path\n }\n }\n }\n topics {\n id\n title\n modules {\n id\n slug\n title\n metaTitle\n myHighlights {\n ...HighlightParts\n }\n myBookmarks {\n ... on ChapterBookmarkNode {\n chapter {\n path\n }\n path\n note {\n id\n text\n }\n }\n ... on ContentBlockBookmarkNode {\n id\n uuid\n path\n contentBlock {\n id\n path\n }\n note {\n id\n text\n }\n }\n ... on ModuleBookmarkNode {\n path\n note {\n id\n text\n }\n }\n }\n mySubmissions {\n id\n text\n assignment {\n id\n title\n path\n module {\n slug\n }\n }\n }\n myAnswers {\n id\n survey {\n path\n id\n title\n }\n }\n }\n }\n }\n }\n "): (typeof documents)["\n query MyActivitiesQuery {\n myActivities {\n instruments {\n id\n slug\n title\n path\n highlights {\n ...HighlightParts\n }\n bookmarks {\n ... on InstrumentBookmarkNode {\n path\n }\n }\n }\n topics {\n id\n title\n modules {\n id\n slug\n title\n metaTitle\n myHighlights {\n ...HighlightParts\n }\n myBookmarks {\n ... on ChapterBookmarkNode {\n chapter {\n path\n }\n path\n note {\n id\n text\n }\n }\n ... on ContentBlockBookmarkNode {\n id\n uuid\n path\n contentBlock {\n id\n path\n }\n note {\n id\n text\n }\n }\n ... on ModuleBookmarkNode {\n path\n note {\n id\n text\n }\n }\n }\n mySubmissions {\n id\n text\n assignment {\n id\n title\n path\n module {\n slug\n }\n }\n }\n myAnswers {\n id\n survey {\n path\n id\n title\n }\n }\n }\n }\n }\n }\n "];
/** /**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */

File diff suppressed because one or more lines are too long

View File

@ -1,9 +1,6 @@
<template> <template>
<div> <div class="activity-list">
<div <template v-for="topic in topics">
v-for="topic in topics"
:key="topic.id"
>
<template v-for="module in topic.modules"> <template v-for="module in topic.modules">
<div <div
class="activity__module" class="activity__module"
@ -11,17 +8,16 @@
:key="module.id" :key="module.id"
> >
<h4 class="heading-4">Thema: {{ topic.title }} - {{ module.metaTitle }}: {{ module.title }}</h4> <h4 class="heading-4">Thema: {{ topic.title }} - {{ module.metaTitle }}: {{ module.title }}</h4>
<div <activity-entry
:to="`/${path(item)}`"
v-for="item in module[property]" v-for="item in module[property]"
:key="item.id" :key="item.id"
> >
<activity-entry :to="`/${path(item)}`"> <slot :item="item"></slot>
<slot :item="item"></slot> </activity-entry>
</activity-entry>
</div>
</div> </div>
</template> </template>
</div> </template>
</div> </div>
</template> </template>

View File

@ -0,0 +1,31 @@
<template>
<div class="activity-list">
<template v-for="instrument in instruments">
<div
class="activity__module"
v-if="instrument[property].length"
:key="instrument.id"
>
<h4 class="heading-4">{{ instrument.title }}</h4>
<activity-entry
:to="`/${instrument.path}`"
v-for="item in instrument[property]"
:key="item.id"
><slot :item="item"></slot
></activity-entry>
</div>
</template>
</div>
</template>
<script setup lang="ts">
import { InstrumentActivityProperty } from '@/@types';
import { InstrumentNode } from '@/__generated__/graphql';
import ActivityEntry from '@/components/profile/ActivityEntry.vue';
export interface Props {
instruments: InstrumentNode[];
property: InstrumentActivityProperty;
}
defineProps<Props>();
</script>

View File

@ -32,7 +32,19 @@
> >
</div> </div>
<div class="activity__activities"> <div class="activity__activities">
<h2>Activities go here</h2> <instrument-activity-list
property="highlights"
:instruments="instruments"
v-if="selectedCategory === HIGHLIGHTS"
v-slot="{ item }"
>
<mark
class="highlight"
:class="[`highlight--${item?.color}`]"
>{{ item?.text }}</mark
>
<div v-if="item.note">Notiz: {{ item.note.text }}</div>
</instrument-activity-list>
<activity-list <activity-list
:topics="highlightTopics" :topics="highlightTopics"
property="myHighlights" property="myHighlights"
@ -46,6 +58,15 @@
> >
<div v-if="item.note">Notiz: {{ item.note.text }}</div> <div v-if="item.note">Notiz: {{ item.note.text }}</div>
</activity-list> </activity-list>
<instrument-activity-list
property="bookmarks"
:instruments="instruments"
v-if="selectedCategory === BOOKMARKS"
v-slot="{ item }"
>
<div>Lesezeichen: {{ item.id }}</div>
<div v-if="item.note">Notiz: {{ item.note.text }}</div>
</instrument-activity-list>
<activity-list <activity-list
:topics="bookmarkTopics" :topics="bookmarkTopics"
property="myBookmarks" property="myBookmarks"
@ -71,105 +92,6 @@
> >
Übung: {{ item.survey.title }} Übung: {{ item.survey.title }}
</activity-list> </activity-list>
<!-- <div -->
<!-- v-for="topic in highlightTopics" -->
<!-- :key="topic?.id" -->
<!-- > -->
<!-- <h4>{{ topic?.title }}</h4> -->
<!-- <template v-for="module in topic?.modules"> -->
<!-- <div -->
<!-- class="activity__module" -->
<!-- v-if="module?.myHighlights?.length" -->
<!-- :key="module?.id" -->
<!-- > -->
<!-- <div -->
<!-- v-for="highlight in module.myHighlights" -->
<!-- :key="highlight.id" -->
<!-- > -->
<!-- <activity-entry :to="`/${highlight.page.path}`"> -->
<!-- <mark -->
<!-- class="highlight" -->
<!-- :class="[`highlight--${highlight?.color}`]" -->
<!-- >{{ highlight?.text }}</mark -->
<!-- > -->
<!-- </activity-entry> -->
<!-- </div> -->
<!-- </div> -->
<!-- </template> -->
<!-- </div> -->
<!-- <div v-if="selectedCategory === BOOKMARKS"> -->
<!-- <div -->
<!-- v-for="topic in bookmarkTopics" -->
<!-- :key="topic?.id" -->
<!-- > -->
<!-- <h4>{{ topic?.title }}</h4> -->
<!-- <template v-for="module in topic?.modules"> -->
<!-- <div -->
<!-- class="activity__module" -->
<!-- v-if="module?.myBookmarks?.length" -->
<!-- :key="module?.id" -->
<!-- > -->
<!-- <div -->
<!-- v-for="bookmark in module?.myBookmarks" -->
<!-- :key="bookmark?.id" -->
<!-- > -->
<!-- <pre>{{ bookmark }}</pre> -->
<!-- <activity-entry :to="`/${bookmark.path}`"> > Lesezeichen: {{ bookmark.id }} </activity-entry> -->
<!-- </div> -->
<!-- </div> -->
<!-- </template> -->
<!-- </div> -->
<!-- </div> -->
<!-- <div v-if="selectedCategory === ASSIGNMENTS"> -->
<!-- <h3>ASSIGNMENTS</h3> -->
<!-- <div -->
<!-- v-for="topic in submissionsTopics" -->
<!-- :key="topic?.id" -->
<!-- > -->
<!-- <h4>{{ topic?.title }}</h4> -->
<!-- <template v-for="module in topic?.modules"> -->
<!-- <div -->
<!-- class="activity__module" -->
<!-- v-if="module?.mySubmissions?.length" -->
<!-- :key="module.id" -->
<!-- > -->
<!-- <div -->
<!-- v-for="submission in module?.mySubmissions" -->
<!-- :key="submission?.id" -->
<!-- > -->
<!-- <pre>{{ submission }}</pre> -->
<!-- <activity-entry :to="`/module/${submission.assignment.module.slug}#${submission.assignment.id}`"> -->
<!-- Submission {{ submission?.text }}</activity-entry -->
<!-- > -->
<!-- </div> -->
<!-- </div> -->
<!-- </template> -->
<!-- </div> -->
<!-- </div> -->
<!-- <div v-if="selectedCategory === SURVEYS"> -->
<!-- <h3>SURVEYS</h3> -->
<!-- <div -->
<!-- v-for="topic in answersTopics" -->
<!-- :key="topic?.id" -->
<!-- > -->
<!-- <h4>{{ topic?.title }}</h4> -->
<!-- <template v-for="module in topic?.modules"> -->
<!-- <div -->
<!-- v-if="module?.myAnswers?.length" -->
<!-- :key="module.id" -->
<!-- > -->
<!-- <div -->
<!-- class="activity__module" -->
<!-- v-for="answer in module?.myAnswers" -->
<!-- :key="answer.id" -->
<!-- > -->
<!-- <pre>{{ answer }}</pre> -->
<!-- <activity-entry :to="{ name: 'profile' }"> Übung: {{ answer?.survey.title }}</activity-entry> -->
<!-- </div> -->
<!-- </div> -->
<!-- </template> -->
<!-- </div> -->
<!-- </div> -->
</div> </div>
</div> </div>
</template> </template>
@ -179,6 +101,7 @@ import { graphql } from '@/__generated__';
import { useQuery } from '@vue/apollo-composable'; import { useQuery } from '@vue/apollo-composable';
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import ActivityList from '@/components/profile/ActivityList.vue'; import ActivityList from '@/components/profile/ActivityList.vue';
import InstrumentActivityList from '@/components/profile/InstrumentActivityList.vue';
import { TopicNode } from '@/__generated__/graphql'; import { TopicNode } from '@/__generated__/graphql';
const HIGHLIGHTS = 'highlights'; const HIGHLIGHTS = 'highlights';
@ -188,10 +111,25 @@ const SURVEYS = 'surveys';
const selectedCategory = ref(HIGHLIGHTS); const selectedCategory = ref(HIGHLIGHTS);
// todo: use fragments to simplify cache updates
const { result } = useQuery( const { result } = useQuery(
graphql(` graphql(`
query MyActivitiesQuery { query MyActivitiesQuery {
myActivities { myActivities {
instruments {
id
slug
title
path
highlights {
...HighlightParts
}
bookmarks {
... on InstrumentBookmarkNode {
path
}
}
}
topics { topics {
id id
title title
@ -262,6 +200,7 @@ const { result } = useQuery(
`) `)
); );
const topics = computed(() => result.value?.myActivities?.topics || []); const topics = computed(() => result.value?.myActivities?.topics || []);
const instruments = computed(() => result.value?.myActivities?.instruments || []);
const highlightTopics = computed(() => { const highlightTopics = computed(() => {
return topics.value.filter((topic: TopicNode) => topic.modules?.some((module) => module?.myHighlights?.length)); return topics.value.filter((topic: TopicNode) => topic.modules?.some((module) => module?.myHighlights?.length));
}); });
@ -278,6 +217,10 @@ const answersTopics = computed(() => {
<style lang="postcss"> <style lang="postcss">
.activity { .activity {
&__activities {
padding-top: var(--section-spacing);
}
&__heading { &__heading {
margin-bottom: var(--section-spacing); margin-bottom: var(--section-spacing);
} }

View File

@ -80,8 +80,7 @@ class BasicKnowledge(StrictHierarchyPage):
parent_page_types = ["books.book"] parent_page_types = ["books.book"]
intro = RichTextField( intro = RichTextField(features=DEFAULT_RICH_TEXT_FEATURES, default="", blank=True)
features=DEFAULT_RICH_TEXT_FEATURES, default="", blank=True)
contents = StreamField( contents = StreamField(
[ [
@ -116,3 +115,7 @@ class BasicKnowledge(StrictHierarchyPage):
FieldPanel("intro"), FieldPanel("intro"),
FieldPanel("contents"), FieldPanel("contents"),
] ]
@property
def route(self):
return f"instrument/{self.slug}"

View File

@ -45,6 +45,7 @@ class InstrumentNode(DjangoObjectType):
contents = GenericStreamFieldType() contents = GenericStreamFieldType()
language = graphene.String() language = graphene.String()
highlights = graphene.List("notes.schema.HighlightNode") highlights = graphene.List("notes.schema.HighlightNode")
path = graphene.String(required=True)
class Meta: class Meta:
model = BasicKnowledge model = BasicKnowledge
@ -73,6 +74,10 @@ class InstrumentNode(DjangoObjectType):
def resolve_highlights(root: BasicKnowledge, info, **kwargs): def resolve_highlights(root: BasicKnowledge, info, **kwargs):
return root.highlights.filter(user=info.context.user) return root.highlights.filter(user=info.context.user)
@staticmethod
def resolve_path(root: BasicKnowledge, info, **kwargs):
return root.route
class InstrumentQuery(object): class InstrumentQuery(object):
instrument = graphene.Field( instrument = graphene.Field(

View File

@ -76,6 +76,7 @@ class ChapterBookmarkNode(DjangoObjectType):
class InstrumentBookmarkNode(DjangoObjectType): class InstrumentBookmarkNode(DjangoObjectType):
uuid = graphene.UUID() uuid = graphene.UUID()
note = graphene.Field(NoteNode) note = graphene.Field(NoteNode)
path = graphene.String(required=True)
class Meta: class Meta:
model = InstrumentBookmark model = InstrumentBookmark
@ -83,6 +84,10 @@ class InstrumentBookmarkNode(DjangoObjectType):
filter_fields = [] filter_fields = []
interfaces = (relay.Node,) interfaces = (relay.Node,)
@staticmethod
def resolve_path(root: InstrumentBookmark, info, **kwargs):
return root.instrument.route
class BookmarkNode(graphene.Union): class BookmarkNode(graphene.Union):
class Meta: class Meta:

View File

@ -425,6 +425,7 @@ type InstrumentBookmarkNode implements Node {
note: NoteNode note: NoteNode
uuid: UUID uuid: UUID
instrument: InstrumentNode! instrument: InstrumentNode!
path: String!
} }
""" """
@ -450,6 +451,7 @@ type InstrumentNode implements Node {
type: InstrumentTypeNode type: InstrumentTypeNode
language: String language: String
highlights: [HighlightNode] highlights: [HighlightNode]
path: String!
} }
type InstrumentTypeNode implements Node { type InstrumentTypeNode implements Node {
@ -969,7 +971,8 @@ type InstrumentNodeEdge {
} }
type ActivityNode { type ActivityNode {
topics: [TopicNode!] topics: [TopicNode!]!
instruments: [InstrumentNode!]!
} }
"""Debugging information for the current query.""" """Debugging information for the current query."""

View File

@ -33,8 +33,9 @@ class UsersQuery(object):
return BasicKnowledge.objects.filter(instrumentbookmark__user=info.context.user) return BasicKnowledge.objects.filter(instrumentbookmark__user=info.context.user)
def resolve_my_activities(self, info, **kwargs): def resolve_my_activities(self, info, **kwargs):
topics = Topic.objects.all() topics = Topic.objects.live()
return {"topics": topics} instruments = BasicKnowledge.objects.live()
return {"topics": topics, "instruments": instruments}
class AllUsersQuery(object): class AllUsersQuery(object):

View File

@ -242,4 +242,9 @@ class UpdateError(graphene.ObjectType):
class ActivityNode(graphene.ObjectType): class ActivityNode(graphene.ObjectType):
topics = graphene.List(graphene.NonNull("books.schema.nodes.TopicNode")) topics = graphene.List(
graphene.NonNull("books.schema.nodes.TopicNode"), required=True
)
instruments = graphene.List(
graphene.NonNull("basicknowledge.queries.InstrumentNode"), required=True
)