Refactor content block component, me mixin, add scrolling
This commit is contained in:
parent
93be4fc972
commit
07b4701529
|
|
@ -2,12 +2,32 @@ export type numberOrUndefined = number | undefined;
|
|||
|
||||
type types = 'task' | 'normal' | 'base_communication' | 'base_society' | 'base_interdisciplinary';
|
||||
|
||||
export interface InstrumentCategory {
|
||||
id: string;
|
||||
name: string;
|
||||
background: string;
|
||||
foreground: string;
|
||||
types: any[];
|
||||
}
|
||||
|
||||
export interface ContentBlock {
|
||||
title: string;
|
||||
contents: any[];
|
||||
id: string | undefined;
|
||||
id: string;
|
||||
isAssignment: boolean;
|
||||
type: types;
|
||||
notes: any[];
|
||||
bookmarks: any[];
|
||||
indent?: boolean;
|
||||
instrumentCategory: InstrumentCategory;
|
||||
mine: boolean;
|
||||
userCreated: boolean;
|
||||
hiddenFor: any[];
|
||||
visibleFor: any[];
|
||||
root: string;
|
||||
titleHiddenFor: any[];
|
||||
descriptionHiddenFor: any[];
|
||||
hidden: boolean;
|
||||
}
|
||||
|
||||
export interface ActionOptions {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
<div
|
||||
:class="{ 'hideable-element--greyed-out': isHidden }"
|
||||
class="content-block__container hideable-element content-list__parent"
|
||||
ref="wrapper"
|
||||
>
|
||||
<div
|
||||
:class="specialClass"
|
||||
|
|
@ -26,7 +27,7 @@
|
|||
<popover-link
|
||||
data-cy="duplicate-content-block-link"
|
||||
text="Duplizieren"
|
||||
@link-action="duplicateContentBlock(contentBlock)"
|
||||
@link-action="duplicateContentBlock()"
|
||||
/>
|
||||
</li>
|
||||
<li
|
||||
|
|
@ -36,7 +37,7 @@
|
|||
<popover-link
|
||||
data-cy="delete-content-block-link"
|
||||
text="Löschen"
|
||||
@link-action="deleteContentBlock(contentBlock)"
|
||||
@link-action="deleteContentBlock()"
|
||||
/>
|
||||
</li>
|
||||
|
||||
|
|
@ -46,7 +47,7 @@
|
|||
>
|
||||
<popover-link
|
||||
text="Bearbeiten"
|
||||
@link-action="editContentBlock(contentBlock)"
|
||||
@link-action="editContentBlock()"
|
||||
/>
|
||||
</li>
|
||||
</more-options-widget>
|
||||
|
|
@ -93,99 +94,213 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { defineAsyncComponent, inject, onMounted, ref, computed } from 'vue';
|
||||
import { useMutation } from '@vue/apollo-composable';
|
||||
import AddContentButton from '@/components/AddContentButton.vue';
|
||||
import MoreOptionsWidget from '@/components/MoreOptionsWidget.vue';
|
||||
import UserWidget from '@/components/UserWidget.vue';
|
||||
import VisibilityAction from '@/components/visibility/VisibilityAction.vue';
|
||||
import PopoverLink from '@/components/ui/PopoverLink.vue';
|
||||
import CopyLink from '@/components/CopyLink.vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { hidden } from '@/helpers/visibility';
|
||||
import { insertAtIndex, removeAtIndex } from '@/graphql/immutable-operations';
|
||||
import { instrumentCategory } from '@/helpers/instrumentType';
|
||||
import { CONTENT_TYPE } from '@/consts/types';
|
||||
import { EDIT_CONTENT_BLOCK_PAGE } from '@/router/module.names';
|
||||
import { getMe } from '@/mixins/me';
|
||||
|
||||
const ContentComponent = defineAsyncComponent(() =>
|
||||
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/ContentComponent.vue')
|
||||
);
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import DUPLICATE_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/duplicateContentBlock.gql';
|
||||
import CHAPTER_QUERY from '@/graphql/gql/queries/chapterQuery.gql';
|
||||
import DELETE_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/deleteContentBlock.gql';
|
||||
import DUPLICATE_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/duplicateContentBlock.gql';
|
||||
|
||||
import me from '@/mixins/me';
|
||||
import type { ContentBlock } from '@/@types';
|
||||
import type { Modal } from '@/plugins/modal.types';
|
||||
|
||||
import { hidden } from '@/helpers/visibility';
|
||||
import { CONTENT_TYPE } from '@/consts/types';
|
||||
import { insertAtIndex, removeAtIndex } from '@/graphql/immutable-operations';
|
||||
import { EDIT_CONTENT_BLOCK_PAGE } from '@/router/module.names';
|
||||
import { instrumentCategory } from '@/helpers/instrumentType';
|
||||
export interface Props {
|
||||
contentBlock: ContentBlock;
|
||||
parent?: any;
|
||||
editMode: boolean;
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'ContentBlock',
|
||||
props: {
|
||||
contentBlock: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
parent: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
editMode: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
const ContentComponent = defineAsyncComponent(
|
||||
() => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/ContentComponent.vue')
|
||||
);
|
||||
|
||||
const { me, schoolClass } = getMe();
|
||||
|
||||
const wrapper = ref<HTMLElement | null>(null);
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const modal = inject('modal') as Modal;
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
editMode: false,
|
||||
});
|
||||
|
||||
const { mutate: duplicateContentBlock } = useMutation(DUPLICATE_CONTENT_BLOCK_MUTATION, () => ({
|
||||
variables: {
|
||||
input: {
|
||||
id: props.contentBlock.id,
|
||||
},
|
||||
},
|
||||
update: (
|
||||
store,
|
||||
{
|
||||
data: {
|
||||
duplicateContentBlock: { contentBlock },
|
||||
},
|
||||
}
|
||||
) => {
|
||||
const id = props.parent.id;
|
||||
if (contentBlock) {
|
||||
const query = CHAPTER_QUERY;
|
||||
const variables = {
|
||||
id,
|
||||
};
|
||||
const { chapter }: any = store.readQuery({ query, variables });
|
||||
const index = chapter.contentBlocks.findIndex((contentBlock: ContentBlock) => contentBlock.id === id);
|
||||
const contentBlocks = insertAtIndex(chapter.contentBlocks, index, contentBlock);
|
||||
const data = {
|
||||
chapter: {
|
||||
...chapter,
|
||||
contentBlocks,
|
||||
},
|
||||
};
|
||||
store.writeQuery({ query, variables, data });
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
mixins: [me],
|
||||
const { mutate: doDeleteContentBlock } = useMutation(DELETE_CONTENT_BLOCK_MUTATION, () => ({
|
||||
variables: {
|
||||
input: {
|
||||
id: props.contentBlock.id,
|
||||
},
|
||||
},
|
||||
update: (
|
||||
store,
|
||||
{
|
||||
data: {
|
||||
deleteContentBlock: { success },
|
||||
},
|
||||
}
|
||||
) => {
|
||||
if (success) {
|
||||
const query = CHAPTER_QUERY;
|
||||
const variables = {
|
||||
id: props.parent.id,
|
||||
};
|
||||
const { chapter }: any = store.readQuery({ query, variables });
|
||||
const index = chapter.contentBlocks.findIndex(
|
||||
(contentBlock: ContentBlock) => contentBlock.id === props.parent.id
|
||||
);
|
||||
const contentBlocks = removeAtIndex(chapter.contentBlocks, index);
|
||||
const data = {
|
||||
chapter: {
|
||||
...chapter,
|
||||
contentBlocks,
|
||||
},
|
||||
};
|
||||
store.writeQuery({ query, variables, data });
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
computed: {
|
||||
canEditModule() {
|
||||
return !this.contentBlock.indent && this.editMode;
|
||||
const top = ref(0);
|
||||
|
||||
onMounted(() => {
|
||||
const element = wrapper.value;
|
||||
if (element !== null) {
|
||||
if (route.hash === `#${props.contentBlock.id}`) {
|
||||
setTimeout(() => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
window.scrollTo({ top: rect.y, behavior: 'smooth' });
|
||||
top.value = rect.y;
|
||||
console.log(rect.y);
|
||||
console.log(rect);
|
||||
}, 0);
|
||||
console.log(document.readyState);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// methods
|
||||
const createContentListOrBlocks = (contentList: any[]) => {
|
||||
return [
|
||||
{
|
||||
type: 'content_list',
|
||||
contents: contentList,
|
||||
id: contentList[0].id,
|
||||
},
|
||||
specialClass() {
|
||||
return `content-block--${this.contentBlock.type.toLowerCase()}`;
|
||||
];
|
||||
};
|
||||
|
||||
const editContentBlock = () => {
|
||||
const route = {
|
||||
name: EDIT_CONTENT_BLOCK_PAGE,
|
||||
params: {
|
||||
id: props.contentBlock.id,
|
||||
},
|
||||
isInstrumentBlock() {
|
||||
return !!this.contentBlock.instrumentCategory;
|
||||
},
|
||||
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
|
||||
instrumentStyle() {
|
||||
if (this.isInstrumentBlock) {
|
||||
return {
|
||||
backgroundColor: this.contentBlock.instrumentCategory.background,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
},
|
||||
instrumentLabel() {
|
||||
const contentType = this.contentBlock.type.toLowerCase();
|
||||
if (contentType.startsWith('base')) {
|
||||
// all legacy instruments start with `base`
|
||||
return instrumentCategory(contentType);
|
||||
}
|
||||
if (this.isInstrumentBlock) {
|
||||
return instrumentCategory(this.contentBlock.instrumentCategory.name);
|
||||
}
|
||||
return '';
|
||||
},
|
||||
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
|
||||
instrumentLabelStyle() {
|
||||
if (this.isInstrumentBlock) {
|
||||
return {
|
||||
color: this.contentBlock.instrumentCategory.foreground,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
},
|
||||
canEditContentBlock() {
|
||||
return this.isMine && !this.contentBlock.indent;
|
||||
},
|
||||
isMine() {
|
||||
return this.contentBlock.mine;
|
||||
},
|
||||
contentBlocksWithContentLists() {
|
||||
/*
|
||||
};
|
||||
router.push(route);
|
||||
};
|
||||
|
||||
const deleteContentBlock = () => {
|
||||
modal
|
||||
.open('confirm')
|
||||
.then(() => {
|
||||
doDeleteContentBlock();
|
||||
})
|
||||
.catch();
|
||||
};
|
||||
|
||||
// computed properties
|
||||
const canEditModule = computed(() => {
|
||||
return !props.contentBlock.indent && props.editMode;
|
||||
});
|
||||
const specialClass = computed(() => {
|
||||
return `content-block--${props.contentBlock.type.toLowerCase()}`;
|
||||
});
|
||||
const isInstrumentBlock = computed(() => {
|
||||
return !!props.contentBlock.instrumentCategory;
|
||||
});
|
||||
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
|
||||
const instrumentStyle = computed(() => {
|
||||
if (isInstrumentBlock.value) {
|
||||
return {
|
||||
backgroundColor: props.contentBlock.instrumentCategory.background,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
});
|
||||
const instrumentLabel = computed(() => {
|
||||
const contentType = props.contentBlock.type.toLowerCase();
|
||||
if (contentType.startsWith('base')) {
|
||||
// all legacy instruments start with `base`
|
||||
return instrumentCategory(contentType);
|
||||
}
|
||||
if (isInstrumentBlock.value) {
|
||||
return instrumentCategory(props.contentBlock.instrumentCategory.name);
|
||||
}
|
||||
return '';
|
||||
});
|
||||
// todo: use dynamic css class with v-bind once we're on Vue 3: https://vuejs.org/api/sfc-css-features.html#v-bind-in-css
|
||||
const instrumentLabelStyle = computed(() => {
|
||||
if (isInstrumentBlock.value) {
|
||||
return {
|
||||
color: props.contentBlock.instrumentCategory.foreground,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
});
|
||||
const isMine = computed(() => {
|
||||
return props.contentBlock.mine;
|
||||
});
|
||||
const contentBlocksWithContentLists = computed(() => {
|
||||
/*
|
||||
collects all content_list_items in content_lists:
|
||||
{
|
||||
text_block,
|
||||
|
|
@ -199,146 +314,43 @@ export default {
|
|||
text_block
|
||||
}
|
||||
*/
|
||||
let contentList = [];
|
||||
let newContent = this.contentBlock.contents.reduce((newContents, content, index) => {
|
||||
// collect content_list_items
|
||||
if (content.type === 'content_list_item') {
|
||||
contentList = [...contentList, content];
|
||||
if (index === this.contentBlock.contents.length - 1) {
|
||||
// content is last element of contents array
|
||||
let updatedContent = [...newContents, ...this.createContentListOrBlocks(contentList)];
|
||||
return updatedContent;
|
||||
}
|
||||
return newContents;
|
||||
} else {
|
||||
// handle all other items and reset current content_list if necessary
|
||||
if (contentList.length !== 0) {
|
||||
newContents = [...newContents, ...this.createContentListOrBlocks(contentList), content];
|
||||
contentList = [];
|
||||
return newContents;
|
||||
} else {
|
||||
return [...newContents, content];
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
return Object.assign({}, this.contentBlock, {
|
||||
contents: newContent,
|
||||
});
|
||||
},
|
||||
isHidden() {
|
||||
return hidden({
|
||||
block: this.contentBlock,
|
||||
schoolClass: this.schoolClass,
|
||||
type: CONTENT_TYPE,
|
||||
});
|
||||
},
|
||||
root() {
|
||||
// we need the root content block id, not the generated content block if inside a content list block
|
||||
return this.contentBlock.root ? this.contentBlock.root : this.contentBlock.id;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
duplicateContentBlock({ id }) {
|
||||
const parent = this.parent;
|
||||
this.$apollo.mutate({
|
||||
mutation: DUPLICATE_CONTENT_BLOCK_MUTATION,
|
||||
variables: {
|
||||
input: {
|
||||
id,
|
||||
},
|
||||
},
|
||||
update(
|
||||
store,
|
||||
{
|
||||
data: {
|
||||
duplicateContentBlock: { contentBlock },
|
||||
},
|
||||
}
|
||||
) {
|
||||
if (contentBlock) {
|
||||
const query = CHAPTER_QUERY;
|
||||
const variables = {
|
||||
id: parent.id,
|
||||
};
|
||||
const { chapter } = store.readQuery({ query, variables });
|
||||
const index = chapter.contentBlocks.findIndex((contentBlock) => contentBlock.id === id);
|
||||
const contentBlocks = insertAtIndex(chapter.contentBlocks, index, contentBlock);
|
||||
const data = {
|
||||
chapter: {
|
||||
...chapter,
|
||||
contentBlocks,
|
||||
},
|
||||
};
|
||||
store.writeQuery({ query, variables, data });
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
editContentBlock(contentBlock) {
|
||||
const route = {
|
||||
name: EDIT_CONTENT_BLOCK_PAGE,
|
||||
params: {
|
||||
id: contentBlock.id,
|
||||
},
|
||||
};
|
||||
this.$router.push(route);
|
||||
},
|
||||
deleteContentBlock(contentBlock) {
|
||||
this.$modal
|
||||
.open('confirm')
|
||||
.then(() => {
|
||||
this.doDeleteContentBlock(contentBlock);
|
||||
})
|
||||
.catch();
|
||||
},
|
||||
doDeleteContentBlock(contentBlock) {
|
||||
const parent = this.parent;
|
||||
const id = contentBlock.id;
|
||||
this.$apollo.mutate({
|
||||
mutation: DELETE_CONTENT_BLOCK_MUTATION,
|
||||
variables: {
|
||||
input: {
|
||||
id,
|
||||
},
|
||||
},
|
||||
update(
|
||||
store,
|
||||
{
|
||||
data: {
|
||||
deleteContentBlock: { success },
|
||||
},
|
||||
}
|
||||
) {
|
||||
if (success) {
|
||||
const query = CHAPTER_QUERY;
|
||||
const variables = {
|
||||
id: parent.id,
|
||||
};
|
||||
const { chapter } = store.readQuery({ query, variables });
|
||||
const index = chapter.contentBlocks.findIndex((contentBlock) => contentBlock.id === id);
|
||||
const contentBlocks = removeAtIndex(chapter.contentBlocks, index);
|
||||
const data = {
|
||||
chapter: {
|
||||
...chapter,
|
||||
contentBlocks,
|
||||
},
|
||||
};
|
||||
store.writeQuery({ query, variables, data });
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
createContentListOrBlocks(contentList) {
|
||||
return [
|
||||
{
|
||||
type: 'content_list',
|
||||
contents: contentList,
|
||||
id: contentList[0].id,
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
};
|
||||
let contentList: any[] = [];
|
||||
let newContent = props.contentBlock.contents.reduce((newContents, content, index) => {
|
||||
// collect content_list_items
|
||||
if (content.type === 'content_list_item') {
|
||||
contentList = [...contentList, content];
|
||||
if (index === props.contentBlock.contents.length - 1) {
|
||||
// content is last element of contents array
|
||||
let updatedContent = [...newContents, ...createContentListOrBlocks(contentList)];
|
||||
return updatedContent;
|
||||
}
|
||||
return newContents;
|
||||
} else {
|
||||
// handle all other items and reset current content_list if necessary
|
||||
if (contentList.length !== 0) {
|
||||
newContents = [...newContents, ...createContentListOrBlocks(contentList), content];
|
||||
contentList = [];
|
||||
return newContents;
|
||||
} else {
|
||||
return [...newContents, content];
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
return Object.assign({}, props.contentBlock, {
|
||||
contents: newContent,
|
||||
});
|
||||
});
|
||||
const isHidden = computed(() => {
|
||||
return hidden({
|
||||
block: props.contentBlock,
|
||||
schoolClass: schoolClass,
|
||||
type: CONTENT_TYPE,
|
||||
});
|
||||
});
|
||||
const root = computed(() => {
|
||||
// we need the root content block id, not the generated content block if inside a content list block
|
||||
return props.contentBlock.root ? props.contentBlock.root : props.contentBlock.id;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
<template>
|
||||
<div
|
||||
class="copy-link"
|
||||
:class="{ 'copy-link--active': show }"
|
||||
@click="copyLink"
|
||||
>
|
||||
Link kopiert <link-icon class="copy-link__icon" />
|
||||
<Transition name="fade"> <span v-if="show">Link kopiert </span> </Transition>
|
||||
<link-icon class="copy-link__icon" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineAsyncComponent, computed } from 'vue';
|
||||
import { defineAsyncComponent, computed, ref } from 'vue';
|
||||
|
||||
const LinkIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/LinkIcon.vue'));
|
||||
|
||||
|
|
@ -20,8 +22,10 @@ const directLink = computed(() => {
|
|||
const { host, protocol } = window.location;
|
||||
return `${protocol}//${host}/content/${props.id}`;
|
||||
});
|
||||
const show = ref(false);
|
||||
|
||||
const copyLink = () => {
|
||||
show.value = true;
|
||||
navigator.clipboard.writeText(directLink.value).then(
|
||||
() => {
|
||||
console.log('yay!', directLink.value);
|
||||
|
|
@ -30,13 +34,15 @@ const copyLink = () => {
|
|||
console.log('nay!');
|
||||
}
|
||||
);
|
||||
setTimeout(() => {
|
||||
show.value = false;
|
||||
}, 3000);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~styles/helpers';
|
||||
.copy-link {
|
||||
background-color: $color-brand-light;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: $small-spacing;
|
||||
|
|
@ -44,10 +50,34 @@ const copyLink = () => {
|
|||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
@include regular-text;
|
||||
transition: background-color 0.25s ease;
|
||||
|
||||
&--active {
|
||||
background-color: $color-brand-light;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
fill: $color-silver-dark;
|
||||
|
||||
&:hover {
|
||||
fill: $color-charcoal-dark;
|
||||
}
|
||||
}
|
||||
|
||||
&--active &__icon {
|
||||
fill: $color-charcoal-dark;
|
||||
}
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.25s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,51 +1,99 @@
|
|||
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
|
||||
import { computed } from 'vue';
|
||||
import { useQuery } from '@vue/apollo-composable';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
const defaultMe = {
|
||||
me: {
|
||||
selectedClass: {
|
||||
id: '',
|
||||
},
|
||||
permissions: [],
|
||||
schoolClasses: [],
|
||||
isTeacher: false,
|
||||
team: null,
|
||||
},
|
||||
showPopover: false,
|
||||
};
|
||||
|
||||
const getTopicRoute = (me) => {
|
||||
if (me.lastTopic && me.lastTopic.slug) {
|
||||
return {
|
||||
me: {
|
||||
selectedClass: {
|
||||
id: '',
|
||||
},
|
||||
permissions: [],
|
||||
schoolClasses: [],
|
||||
isTeacher: false,
|
||||
team: null,
|
||||
name: 'topic',
|
||||
params: {
|
||||
topicSlug: me.lastTopic.slug,
|
||||
},
|
||||
showPopover: false,
|
||||
};
|
||||
}
|
||||
return '/book/topic/berufliche-grundbildung';
|
||||
};
|
||||
|
||||
const getSelectedClass = (me) => {
|
||||
return me.selectedClass;
|
||||
};
|
||||
|
||||
const getCanManageContent = (me) => {
|
||||
return me.isTeacher;
|
||||
};
|
||||
|
||||
const getIsReadOnly = (me) => {
|
||||
return me.readOnly || me.selectedClass.readOnly;
|
||||
};
|
||||
|
||||
const getCurrentClassName = (me) => {
|
||||
let currentClass = me.schoolClasses.find((schoolClass) => {
|
||||
return schoolClass.id === me.selectedClass.id;
|
||||
});
|
||||
return currentClass ? currentClass.name : me.schoolClasses.length ? me.schoolClasses[0].name : '';
|
||||
};
|
||||
|
||||
const options = {
|
||||
fetchPolicy: 'cache-first',
|
||||
};
|
||||
|
||||
const getMe = () => {
|
||||
const { result } = useQuery(ME_QUERY, null, options);
|
||||
|
||||
const me = computed(() => result.value?.me, defaultMe.me);
|
||||
const topicRoute = computed(() => {
|
||||
return getTopicRoute(me.value);
|
||||
});
|
||||
const schoolClass = computed(() => {
|
||||
return getSelectedClass(me.value);
|
||||
});
|
||||
const canManageContent = computed(() => {
|
||||
return getCanManageContent(me.value);
|
||||
});
|
||||
const isReadOnly = computed(() => {
|
||||
return getIsReadOnly(me.value);
|
||||
});
|
||||
|
||||
const currentClassName = computed(() => {
|
||||
return getCurrentClassName(me.value);
|
||||
});
|
||||
|
||||
return { me, topicRoute, schoolClass, canManageContent, isReadOnly, currentClassName };
|
||||
};
|
||||
|
||||
const meMixin = {
|
||||
data() {
|
||||
return defaultMe;
|
||||
},
|
||||
|
||||
computed: {
|
||||
topicRoute() {
|
||||
if (this.$data.me.lastTopic && this.$data.me.lastTopic.slug) {
|
||||
return {
|
||||
name: 'topic',
|
||||
params: {
|
||||
topicSlug: this.$data.me.lastTopic.slug,
|
||||
},
|
||||
};
|
||||
}
|
||||
return '/book/topic/berufliche-grundbildung';
|
||||
return getTopicRoute(this.$data.me);
|
||||
},
|
||||
schoolClass() {
|
||||
return this.$data.me.selectedClass;
|
||||
return getSelectedClass(this.$data.me);
|
||||
},
|
||||
canManageContent() {
|
||||
return this.$data.me.isTeacher;
|
||||
return getCanManageContent(this.$data.me);
|
||||
},
|
||||
isReadOnly() {
|
||||
return this.$data.me.readOnly || this.$data.me.selectedClass.readOnly;
|
||||
return getIsReadOnly(this.$data.me);
|
||||
},
|
||||
currentClassName() {
|
||||
let currentClass = this.$data.me.schoolClasses.find((schoolClass) => {
|
||||
return schoolClass.id === this.$data.me.selectedClass.id;
|
||||
});
|
||||
return currentClass
|
||||
? currentClass.name
|
||||
: this.$data.me.schoolClasses.length
|
||||
? this.$data.me.schoolClasses[0].name
|
||||
: '';
|
||||
return getCurrentClassName(this.$data.me);
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -56,7 +104,9 @@ export default {
|
|||
// todo: refactor
|
||||
return this.$getRidOfEdges(data).me;
|
||||
},
|
||||
fetchPolicy: 'cache-first',
|
||||
...options,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export { getMe, meMixin as default };
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// adapted from
|
||||
// https://stackoverflow.com/questions/41791193/vuejs-reactive-binding-for-a-plugin-how-to/41801107#41801107
|
||||
import { reactive, App } from 'vue';
|
||||
import { Modal } from './modal.types';
|
||||
|
||||
class ModalStore {
|
||||
data: any;
|
||||
|
|
@ -17,18 +18,7 @@ class ModalStore {
|
|||
}
|
||||
}
|
||||
|
||||
interface Modal {
|
||||
state: any;
|
||||
component: string;
|
||||
payload?: any;
|
||||
confirm: (res: any) => void;
|
||||
open: (component: string, payload?: any) => Promise<(resolve: () => any, reject: () => any) => void>;
|
||||
cancel: () => void;
|
||||
_resolve: (r?: any) => any;
|
||||
_reject: (r?: any) => any;
|
||||
}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
declare module 'vue' {
|
||||
interface ComponentCustomProperties {
|
||||
$modal: Modal;
|
||||
}
|
||||
|
|
@ -44,7 +34,7 @@ const ModalPlugin = {
|
|||
store.state.payload = {};
|
||||
};
|
||||
|
||||
app.config.globalProperties.$modal = {
|
||||
const modal = {
|
||||
state: store.state,
|
||||
component: store.state.component,
|
||||
payload: store.state.payload,
|
||||
|
|
@ -64,9 +54,12 @@ const ModalPlugin = {
|
|||
reset();
|
||||
this._reject();
|
||||
},
|
||||
_resolve: () => {},
|
||||
_reject: () => {},
|
||||
_resolve: (_: any) => { },
|
||||
_reject: () => { },
|
||||
};
|
||||
|
||||
app.config.globalProperties.$modal = modal;
|
||||
app.provide('modal', modal);
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
export interface Modal {
|
||||
state: any;
|
||||
component: string;
|
||||
payload?: any;
|
||||
confirm: (res: any) => void;
|
||||
open: (component: string, payload?: any) => Promise<(resolve: () => any, reject: () => any) => void>;
|
||||
cancel: () => void;
|
||||
_resolve: (r?: any) => any;
|
||||
_reject: (r?: any) => any;
|
||||
}
|
||||
Loading…
Reference in New Issue