Add copy link button to chapters, also generate redirect link
This commit is contained in:
parent
370afd0b0c
commit
a629f6a5e6
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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}") {
|
||||
const decoded = window.atob(props.id);
|
||||
|
||||
let query;
|
||||
|
||||
if (decoded.startsWith('ChapterNode')) {
|
||||
query = gql`
|
||||
query ChapterQuery($id: ID!) {
|
||||
chapter(id: $id) {
|
||||
path
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
onResult(
|
||||
({
|
||||
data: {
|
||||
contentBlock: { path },
|
||||
},
|
||||
}) => {
|
||||
// console.log(queryResult);
|
||||
router.push(`/${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>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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}"
|
||||
|
|
|
|||
Loading…
Reference in New Issue