Add copy link button to chapters, also generate redirect link

This commit is contained in:
Ramon Wenger 2023-02-09 17:46:57 +01:00
parent 370afd0b0c
commit a629f6a5e6
6 changed files with 119 additions and 71 deletions

View File

@ -9,8 +9,12 @@
class="hideable-element"
v-if="!titleHidden"
>
<h3 :id="'chapter-' + index">
<h3
class="chapter__title"
:id="'chapter-' + index"
>
{{ chapter.title }}
<copy-link :id="chapter.id" />
</h3>
</div>
@ -66,6 +70,7 @@ import ContentBlock from '@/components/ContentBlock';
import AddContentButton from '@/components/AddContentButton';
import BookmarkActions from '@/components/notes/BookmarkActions';
import VisibilityAction from '@/components/visibility/VisibilityAction';
import CopyLink from '@/components/CopyLink.vue';
import { hidden } from '@/helpers/visibility';
import { CHAPTER_DESCRIPTION_TYPE, CHAPTER_TITLE_TYPE, CONTENT_TYPE } from '@/consts/types';
@ -98,6 +103,7 @@ export default {
VisibilityAction,
ContentBlock,
AddContentButton,
CopyLink,
},
computed: {
@ -231,6 +237,12 @@ export default {
position: relative;
}
&__title {
display: flex;
justify-content: space-between;
align-items: center;
}
&__description {
@include lead-paragraph;

View File

@ -219,10 +219,7 @@ onMounted(() => {
const rect = element.getBoundingClientRect();
window.scrollTo({ top: rect.y, behavior: 'smooth' });
top.value = rect.y;
console.log(rect.y);
console.log(rect);
}, 750);
console.log(document.readyState);
}
}
});

View File

@ -11,6 +11,7 @@
<script setup lang="ts">
import { defineAsyncComponent, computed, ref } from 'vue';
import log from 'loglevel';
const LinkIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/LinkIcon.vue'));
@ -26,14 +27,9 @@ const show = ref(false);
const copyLink = () => {
show.value = true;
navigator.clipboard.writeText(directLink.value).then(
() => {
console.log('yay!', directLink.value);
},
() => {
console.log('nay!');
}
);
navigator.clipboard.writeText(directLink.value).then(() => {
log.debug('copied link', directLink.value);
});
setTimeout(() => {
show.value = false;
}, 3000);

View File

@ -1,9 +1,4 @@
<template>
<div>
<h1>ContentBlockLocator {{ props.id }}</h1>
<pre>{{ result }}</pre>
</div>
</template>
<template><div /></template>
<script setup lang="ts">
import { useQuery } from '@vue/apollo-composable';
@ -14,22 +9,37 @@ const props = defineProps<{
id: string;
}>();
const { result, onResult } = useQuery(gql`
query {
contentBlock(id: "${props.id}") {
path
}
}
`);
const decoded = window.atob(props.id);
onResult(
({
data: {
contentBlock: { path },
},
}) => {
// console.log(queryResult);
router.push(`/${path}`);
let query;
if (decoded.startsWith('ChapterNode')) {
query = gql`
query ChapterQuery($id: ID!) {
chapter(id: $id) {
path
}
}
`;
} else {
query = gql`
query ContentBlockQuery($id: ID!) {
contentBlock(id: $id) {
path
}
}
`;
}
const { onResult } = useQuery(query, () => ({ id: props.id }));
onResult(({ data }) => {
let path;
try {
path = data.chapter.path;
} catch (e) {
path = data.contentBlock.path;
}
);
router.push(`/${path}`);
});
</script>

View File

@ -5,51 +5,67 @@ from wagtail.admin.edit_handlers import FieldPanel, TabbedInterface, ObjectList
from core.wagtail_utils import StrictHierarchyPage, get_default_settings
from users.models import SchoolClass
from core.mixins import GraphqlNodeMixin
logger = logging.getLogger(__name__)
class Chapter(StrictHierarchyPage):
class Chapter(StrictHierarchyPage, GraphqlNodeMixin):
class Meta:
verbose_name = 'Kapitel'
verbose_name_plural = 'Kapitel'
verbose_name = "Kapitel"
verbose_name_plural = "Kapitel"
description = models.TextField(blank=True)
content_panels = [
FieldPanel('title', classname="full title"),
FieldPanel('description', classname="full description"),
FieldPanel("title", classname="full title"),
FieldPanel("description", classname="full description"),
]
edit_handler = TabbedInterface([
ObjectList(content_panels, heading='Content'),
get_default_settings()
])
edit_handler = TabbedInterface(
[ObjectList(content_panels, heading="Content"), get_default_settings()]
)
template = 'generic_page.html'
template = "generic_page.html"
parent_page_types = ['books.Module']
subpage_types = ['books.ContentBlock']
parent_page_types = ["books.Module"]
subpage_types = ["books.ContentBlock"]
title_hidden_for = models.ManyToManyField(SchoolClass, related_name='hidden_chapter_titles')
description_hidden_for = models.ManyToManyField(SchoolClass, related_name='hidden_chapter_descriptions')
title_hidden_for = models.ManyToManyField(
SchoolClass, related_name="hidden_chapter_titles"
)
description_hidden_for = models.ManyToManyField(
SchoolClass, related_name="hidden_chapter_descriptions"
)
def sync_title_visibility(self, school_class_template, school_class_to_sync):
if self.title_hidden_for.filter(id=school_class_template.id).exists() \
and not self.title_hidden_for.filter(id=school_class_to_sync.id).exists():
if (
self.title_hidden_for.filter(id=school_class_template.id).exists()
and not self.title_hidden_for.filter(id=school_class_to_sync.id).exists()
):
self.title_hidden_for.add(school_class_to_sync)
if self.title_hidden_for.filter(
id=school_class_to_sync.id).exists() \
and not self.title_hidden_for.filter(id=school_class_template.id).exists():
if (
self.title_hidden_for.filter(id=school_class_to_sync.id).exists()
and not self.title_hidden_for.filter(id=school_class_template.id).exists()
):
self.title_hidden_for.remove(school_class_to_sync)
def sync_description_visibility(self, school_class_template, school_class_to_sync):
if self.description_hidden_for.filter(id=school_class_template.id).exists() \
and not self.description_hidden_for.filter(id=school_class_to_sync.id).exists():
if (
self.description_hidden_for.filter(
id=school_class_template.id).exists()
and not self.description_hidden_for.filter(
id=school_class_to_sync.id
).exists()
):
self.description_hidden_for.add(school_class_to_sync)
if self.description_hidden_for.filter(
id=school_class_to_sync.id).exists() \
and not self.description_hidden_for.filter(id=school_class_template.id).exists():
if (
self.description_hidden_for.filter(
id=school_class_to_sync.id).exists()
and not self.description_hidden_for.filter(
id=school_class_template.id
).exists()
):
self.description_hidden_for.remove(school_class_to_sync)

View File

@ -11,43 +11,55 @@ from notes.schema import ChapterBookmarkNode
class ChapterNode(DjangoObjectType):
bookmark = graphene.Field(ChapterBookmarkNode)
content_blocks = graphene.List('books.schema.nodes.ContentBlockNode')
title_hidden_for = graphene.List('users.schema.SchoolClassNode')
description_hidden_for = graphene.List('users.schema.SchoolClassNode')
content_blocks = graphene.List("books.schema.nodes.ContentBlockNode")
title_hidden_for = graphene.List("users.schema.SchoolClassNode")
description_hidden_for = graphene.List("users.schema.SchoolClassNode")
path = graphene.String()
class Meta:
model = Chapter
only_fields = [
'slug', 'title', 'description', 'title_hidden_for', 'description_hidden_for'
"slug",
"title",
"description",
"title_hidden_for",
"description_hidden_for",
]
filter_fields = [
'slug', 'title',
"slug",
"title",
]
interfaces = (relay.Node, ChapterInterface)
def resolve_content_blocks(self, info, **kwargs):
user = info.context.user
school_classes = user.school_classes.values_list('pk', flat=True)
school_classes = user.school_classes.values_list("pk", flat=True)
by_parent = ContentBlock.get_by_parent(self) \
.filter(contentblocksnapshot__isnull=True) # exclude snapshot
by_parent = ContentBlock.get_by_parent(self).filter(
contentblocksnapshot__isnull=True
) # exclude snapshot
# .prefetch_related('visible_for') \
# .prefetch_related('hidden_for')
# don't filter the hidden blocks on the server any more, we do this on the client now, as they are not secret
default_blocks = Q(user_created=False)
owned_by_user = Q(user_created=True, owner=user)
teacher_created_and_visible = Q(Q(user_created=True) & Q(visible_for__pk__in=school_classes))
teacher_created_and_visible = Q(
Q(user_created=True) & Q(visible_for__pk__in=school_classes)
)
if user.can_manage_school_class_content: # teacher
return by_parent.filter(default_blocks | owned_by_user | teacher_created_and_visible).distinct()
return by_parent.filter(
default_blocks | owned_by_user | teacher_created_and_visible
).distinct()
else: # student
return by_parent.filter(default_blocks | teacher_created_and_visible).distinct()
return by_parent.filter(
default_blocks | teacher_created_and_visible
).distinct()
def resolve_bookmark(self, info, **kwargs):
return ChapterBookmark.objects.filter(
user=info.context.user,
chapter=self
user=info.context.user, chapter=self
).first()
@staticmethod
@ -57,3 +69,8 @@ class ChapterNode(DjangoObjectType):
@staticmethod
def resolve_description_hidden_for(parent: Chapter, info, **kwargs):
return parent.description_hidden_for.all()
@staticmethod
def resolve_path(root: Chapter, info, **kwargs):
module = root.get_parent()
return f"module/{module.slug}#{root.graphql_id}"