Refactor LearningContent to its own route

This commit is contained in:
Daniel Egger 2022-09-09 15:09:42 +02:00
parent 8dbec8b699
commit 5d6e94ebd6
9 changed files with 150 additions and 47 deletions

View File

@ -1,32 +1,24 @@
<script setup lang="ts">
import * as log from 'loglevel';
import {computed} from 'vue';
import {useCircleStore} from '@/stores/circle';
import * as log from 'loglevel'
import { computed } from 'vue'
import { useCircleStore } from '@/stores/circle'
log.debug('LearningContent.vue setup');
log.debug('LearningContent.vue setup')
const circleStore = useCircleStore();
const circleStore = useCircleStore()
const learningContent = computed(() => circleStore.currentLearningContent);
const learningContent = computed(() => circleStore.currentLearningContent)
const block = computed(() => {
if (learningContent.value) {
return learningContent.value.contents[0];
return learningContent.value.contents[0]
}
})
</script>
<template>
<div>
<nav
class="
px-4 lg:px-8
py-4
flex justify-between items-center
border-b border-gray-500
"
>
<nav class="px-4 lg:px-8 py-4 flex justify-between items-center border-b border-gray-500">
<button
type="button"
class="btn-text inline-flex items-center px-3 py-2 font-normal"
@ -50,15 +42,9 @@ const block = computed(() => {
</nav>
<div v-if="block.type === 'exercise'" class="h-screen">
<iframe
width="100%"
height="100%"
scrolling="no"
:src="block.value.url"
/>
<iframe width="100%" height="100%" scrolling="no" :src="block.value.url" />
</div>
<div v-else class="mx-auto max-w-5xl px-4 lg:px-8 py-4">
<p>{{ block.value.description }}</p>
@ -69,24 +55,19 @@ const block = computed(() => {
:title="learningContent.title"
frameborder="0"
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen>
allowfullscreen
>
</iframe>
</div>
<div
v-if="block.type === 'podcast'"
>
<div v-if="block.type === 'podcast'">
<iframe width="100%" height="300" scrolling="no" frameborder="no" allow="" :src="block.value.url"></iframe>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
$header-height: 77px;
$footer-height: 57px;

View File

@ -1,4 +1,4 @@
import { createApp } from 'vue'
import { createApp, markRaw } from 'vue'
import { createPinia } from 'pinia'
import * as log from 'loglevel'
@ -7,6 +7,7 @@ import App from './App.vue'
import router from './router'
import '../tailwind.css'
import type { Router } from 'vue-router'
if (window.location.href.indexOf('localhost') >= 0) {
log.setLevel('trace')
@ -21,8 +22,19 @@ const app = createApp(App)
// todo: define lang setup
// await loadLocaleMessages(i18n, 'de')
app.use(createPinia())
app.use(router)
declare module 'pinia' {
export interface PiniaCustomProperties {
router: Router
}
}
const pinia = createPinia();
pinia.use(({ store }) => {
store.router = markRaw(router)
})
app.use(pinia)
// app.use(i18n)
app.mount('#app')

View File

@ -46,6 +46,11 @@ const router = createRouter({
component: () => import('../views/CircleView.vue'),
props: true,
},
{
path: '/learn/:learningPathSlug/:circleSlug/:contentSlug',
component: () => import('../views/LearningContentView.vue'),
props: true,
},
{
path: '/styleguide',
component: () => import('../views/StyleGuideView.vue'),

View File

@ -215,4 +215,9 @@ export class Circle implements LearningWagtailPage {
this.parentLearningPath.calcNextLearningContent(completionData);
}
}
public getUrl(): string {
const shortSlug = this.slug.replace(`${this.parentLearningPath?.slug}-circle-`, '')
return `/learn/${this.parentLearningPath?.slug}/${shortSlug}`;
}
}

View File

@ -6,10 +6,28 @@ export type AppState = {
showMainNavigationBar: boolean
}
const showMainNavigationBarInitialState = () => {
let path = window.location.pathname;
// remove dangling slash
if (path.endsWith('/')) {
path = path.slice(0, -1);
}
const numberOfSlashes = (path.match(/\//g) || []).length;
// it should hide main navigation bar when on learning content page
if (path.startsWith('/learn/') && numberOfSlashes >= 4) {
return false
}
return true;
}
export const useAppStore = defineStore({
id: 'app',
state: () => ({
showMainNavigationBar: true,
showMainNavigationBar: showMainNavigationBarInitialState(),
userLoaded: false,
routingFinished: false,
} as AppState),

View File

@ -28,7 +28,7 @@ export const useCircleStore = defineStore({
getters: {
},
actions: {
async loadCircle(learningPathSlug: string, circleSlug: string) {
async loadCircle(learningPathSlug: string, circleSlug: string): Promise<Circle> {
this.circle = undefined;
const learningPathStore = useLearningPathStore();
await learningPathStore.loadLearningPath(learningPathSlug);
@ -44,6 +44,20 @@ export const useCircleStore = defineStore({
return this.circle
},
async loadLearningContent(learningPathSlug: string, circleSlug: string, learningContentSlug: string) {
const circle = await this.loadCircle(learningPathSlug, circleSlug);
if (circle) {
this.currentLearningContent = circle.flatLearningContents.find((learningContent) => {
return learningContent.slug.endsWith(learningContentSlug);
});
}
if (!this.currentLearningContent) {
throw `No learning content found with slug: ${learningContentSlug}`;
}
return this.currentLearningContent;
},
async markCompletion(page: LearningContent | LearningUnitQuestion, flag = true) {
try {
page.completed = flag;
@ -61,15 +75,15 @@ export const useCircleStore = defineStore({
},
openLearningContent(learningContent: LearningContent) {
this.currentLearningContent = learningContent;
const appStore = useAppStore();
appStore.showMainNavigationBar = false;
this.page = 'LEARNING_CONTENT';
const shortSlug = learningContent.slug.replace(`${this.circle?.slug}-lc-`, '');
this.router.push({
path: `${this.circle?.getUrl()}/${shortSlug}`,
});
},
closeLearningContent() {
this.currentLearningContent = undefined;
const appStore = useAppStore();
appStore.showMainNavigationBar = true;
this.page = 'INDEX';
this.router.push({
path: `${this.circle?.getUrl()}`
});
},
openSelfEvaluation(learningUnit: LearningUnit) {
this.page = 'SELF_EVALUATION';
@ -108,7 +122,8 @@ export const useCircleStore = defineStore({
currentParent.children.length > 0
) {
// go to self evaluation
this.openSelfEvaluation(currentParent);
// this.openSelfEvaluation(currentParent);
this.closeLearningContent();
} else if (this.currentLearningContent.nextLearningContent) {
if (
this.currentLearningContent.parentLearningSequence &&

View File

@ -8,6 +8,7 @@ import LearningContent from '@/components/circle/LearningContent.vue'
import { onMounted } from 'vue'
import { useCircleStore } from '@/stores/circle'
import SelfEvaluation from '@/components/circle/SelfEvaluation.vue'
import { useAppStore } from '@/stores/app'
log.debug('CircleView.vue created')
@ -16,6 +17,9 @@ const props = defineProps<{
circleSlug: string
}>()
const appStore = useAppStore()
appStore.showMainNavigationBar = true
const circleStore = useCircleStore()
onMounted(async () => {

View File

@ -0,0 +1,59 @@
<script setup lang="ts">
import * as log from 'loglevel'
import LearningContent from '@/components/circle/LearningContent.vue'
import { onMounted } from 'vue'
import { useCircleStore } from '@/stores/circle'
import { useAppStore } from '@/stores/app'
log.debug('LearningContentView created')
const props = defineProps<{
learningPathSlug: string
circleSlug: string
contentSlug: string
}>()
const appStore = useAppStore()
appStore.showMainNavigationBar = false
const circleStore = useCircleStore()
onMounted(async () => {
log.debug('LearningContentView mounted', props.learningPathSlug, props.circleSlug, props.contentSlug)
try {
await circleStore.loadLearningContent(props.learningPathSlug, props.circleSlug, props.contentSlug)
} catch (error) {
log.error(error)
}
})
</script>
<template>
<LearningContent
v-if="circleStore.currentLearningContent"
:key="circleStore.currentLearningContent.translation_key"
/>
</template>
<style lang="postcss" scoped>
.circle-container {
background: linear-gradient(to right, white 0%, white 50%, theme(colors.gray.200) 50%, theme(colors.gray.200) 100%);
}
.circle {
max-width: 1440px;
margin: 0 auto;
}
.v-enter-active,
.v-leave-active {
transition: opacity 0.3s ease;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}
</style>

View File

@ -1,9 +1,9 @@
import {fileURLToPath, URL} from 'url'
import { fileURLToPath, URL } from "url";
import {defineConfig, loadEnv} from 'vite'
import vue from '@vitejs/plugin-vue'
import { defineConfig, loadEnv } from "vite";
import vue from "@vitejs/plugin-vue";
// import vueI18n from '@intlify/vite-plugin-vue-i18n'
import alias from '@rollup/plugin-alias'
import alias from "@rollup/plugin-alias";
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
@ -33,6 +33,10 @@ export default defineConfig(({ mode }) => {
// ]
}),
],
server: {
port: 5173,
hmr: { port: 5173 }
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),