feat: show and hide discussions in zen mode

This commit is contained in:
Jannat Patel
2025-04-25 17:28:54 +05:30
parent 63319d32e8
commit e513993a0d
5 changed files with 120 additions and 51 deletions

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="h-full"> <div class="">
<div <div
v-if="title && (outline.data?.length || allowEdit)" v-if="title && (outline.data?.length || allowEdit)"
class="flex items-center justify-between space-x-2 mb-4 px-2" class="flex items-center justify-between space-x-2 mb-4 px-2"
@@ -17,9 +17,6 @@
<Button size="sm" v-if="allowEdit" @click="openChapterModal()"> <Button size="sm" v-if="allowEdit" @click="openChapterModal()">
{{ __('Add Chapter') }} {{ __('Add Chapter') }}
</Button> </Button>
<!-- <span class="font-medium cursor-pointer" @click="expandAllChapters()">
{{ expandAll ? __("Collapse all chapters") : __("Expand all chapters") }}
</span> -->
</div> </div>
<div <div
:class="{ :class="{

View File

@@ -653,3 +653,8 @@ const getSubmissionColumns = () => {
] ]
} }
</script> </script>
<style>
p {
line-height: 1.5rem;
}
</style>

View File

@@ -69,7 +69,7 @@
<CourseCardOverlay :course="course" class="md:hidden mb-4" /> <CourseCardOverlay :course="course" class="md:hidden mb-4" />
<div <div
v-html="course.data.description" v-html="course.data.description"
class="ProseMirror prose prose-table:table-fixed prose-td:p-2 prose-th:p-2 prose-td:border prose-th:border prose-td:border-outline-gray-2 prose-th:border-outline-gray-2 prose-td:relative prose-th:relative prose-th:bg-surface-gray-2 prose-sm max-w-none !whitespace-normal mt-4" class="ProseMirror prose prose-table:table-fixed prose-td:p-2 prose-th:p-2 prose-td:border prose-th:border prose-td:border-outline-gray-2 prose-th:border-outline-gray-2 prose-td:relative prose-th:relative prose-th:bg-surface-gray-2 prose-sm max-w-none !whitespace-normal mt-10"
></div> ></div>
<div class="mt-10"> <div class="mt-10">
<CourseOutline <CourseOutline

View File

@@ -57,7 +57,7 @@
</div> </div>
<div <div
v-if="courses.data?.length" v-if="courses.data?.length"
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-5" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-4"
> >
<router-link <router-link
v-for="course in courses.data" v-for="course in courses.data"

View File

@@ -45,21 +45,49 @@
<div <div
v-else v-else
ref="lessonContainer" ref="lessonContainer"
class="bg-surface-white overflow-y-auto" class="bg-surface-white"
:class="{
'overflow-y-auto': zenModeEnabled,
}"
> >
<div <div
class="border-r container pt-5 pb-10 px-5" class="border-r container pt-5 pb-10 px-5 h-full"
:class="{ :class="{
'w-3/4 mx-auto border-none': zenModeEnabled, 'w-full md:w-3/4 mx-auto border-none !pt-10': zenModeEnabled,
}" }"
> >
<div <div
class="flex flex-col md:flex-row md:items-center justify-between" class="flex flex-col md:flex-row md:items-center justify-between"
> >
<div class="flex flex-col">
<div class="text-3xl font-semibold text-ink-gray-9"> <div class="text-3xl font-semibold text-ink-gray-9">
{{ lesson.data.title }} {{ lesson.data.title }}
</div> </div>
<div class="flex items-center mt-2 md:mt-0">
<div
v-if="zenModeEnabled"
class="relative flex items-center space-x-2 text-sm mt-1 text-ink-gray-7 group w-fit mt-2"
>
<span>
{{ lesson.data.chapter_title }} -
{{ lesson.data.course_title }}
</span>
<Info class="size-3" />
<div
class="hidden group-hover:block rounded bg-gray-900 px-2 py-1 text-xs text-white shadow-xl absolute left-0 top-full mt-2"
>
{{ Math.ceil(lesson.data.membership.progress) }}%
{{ __('completed') }}
</div>
</div>
</div>
<div class="flex items-center space-x-2 mt-2 md:mt-0">
<Button v-if="zenModeEnabled" @click="showDiscussionsInZenMode">
<template #icon>
<MessageCircleQuestion class="w-4 h-4 stroke-1.5" />
</template>
</Button>
<router-link <router-link
v-if="lesson.data.prev" v-if="lesson.data.prev"
:to="{ :to="{
@@ -71,7 +99,7 @@
}, },
}" }"
> >
<Button class="mr-2"> <Button>
<template #prefix> <template #prefix>
<ChevronLeft class="w-4 h-4 stroke-1" /> <ChevronLeft class="w-4 h-4 stroke-1" />
</template> </template>
@@ -91,7 +119,7 @@
}, },
}" }"
> >
<Button class="mr-2"> <Button>
{{ __('Edit') }} {{ __('Edit') }}
</Button> </Button>
</router-link> </router-link>
@@ -146,9 +174,6 @@
:instructors="lesson.data.instructors" :instructors="lesson.data.instructors"
/> />
</div> </div>
<div v-else class="text-sm mt-1 text-ink-gray-7">
{{ lesson.data.chapter_title }} - {{ lesson.data.course_title }}
</div>
<div <div
v-if=" v-if="
@@ -189,7 +214,7 @@
:quizId="lesson.data.quiz_id" :quizId="lesson.data.quiz_id"
/> />
</div> </div>
<div class="mt-20"> <div class="mt-20" ref="discussionsContainer">
<Discussions <Discussions
v-if="allowDiscussions" v-if="allowDiscussions"
:title="'Questions'" :title="'Questions'"
@@ -234,7 +259,15 @@ import {
Tooltip, Tooltip,
usePageMeta, usePageMeta,
} from 'frappe-ui' } from 'frappe-ui'
import { computed, watch, inject, ref, onMounted, onBeforeUnmount } from 'vue' import {
computed,
watch,
inject,
ref,
onMounted,
onBeforeUnmount,
nextTick,
} from 'vue'
import CourseOutline from '@/components/CourseOutline.vue' import CourseOutline from '@/components/CourseOutline.vue'
import UserAvatar from '@/components/UserAvatar.vue' import UserAvatar from '@/components/UserAvatar.vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
@@ -244,6 +277,8 @@ import {
LockKeyholeIcon, LockKeyholeIcon,
LogIn, LogIn,
Focus, Focus,
Info,
MessageCircleQuestion,
} from 'lucide-vue-next' } from 'lucide-vue-next'
import Discussions from '@/components/Discussions.vue' import Discussions from '@/components/Discussions.vue'
import { getEditorTools } from '../utils' import { getEditorTools } from '../utils'
@@ -265,6 +300,8 @@ const instructorEditor = ref(null)
const lessonProgress = ref(0) const lessonProgress = ref(0)
const lessonContainer = ref(null) const lessonContainer = ref(null)
const zenModeEnabled = ref(false) const zenModeEnabled = ref(false)
const hasQuiz = ref(false)
const discussionsContainer = ref(null)
const timer = ref(0) const timer = ref(0)
const { brand } = sessionStore() const { brand } = sessionStore()
let timerInterval let timerInterval
@@ -293,8 +330,12 @@ onMounted(() => {
const attachFullscreenEvent = () => { const attachFullscreenEvent = () => {
if (document.fullscreenElement) { if (document.fullscreenElement) {
zenModeEnabled.value = true zenModeEnabled.value = true
allowDiscussions.value = false
} else { } else {
zenModeEnabled.value = false zenModeEnabled.value = false
if (!hasQuiz.value) {
allowDiscussions.value = true
}
} }
} }
@@ -335,7 +376,6 @@ const enablePlyr = () => {
const lesson = createResource({ const lesson = createResource({
url: 'lms.lms.utils.get_lesson', url: 'lms.lms.utils.get_lesson',
cache: ['lesson', props.courseName, props.chapterNumber, props.lessonNumber],
makeParams(values) { makeParams(values) {
return { return {
course: props.courseName, course: props.courseName,
@@ -344,7 +384,9 @@ const lesson = createResource({
} }
}, },
auto: true, auto: true,
onSuccess(data) { })
const setupLesson = (data) => {
if (Object.keys(data).length === 0) { if (Object.keys(data).length === 0) {
router.push({ router.push({
name: 'CourseDetail', name: 'CourseDetail',
@@ -368,11 +410,10 @@ const lesson = createResource({
if (!editor.value && data.body) { if (!editor.value && data.body) {
const quizRegex = /\{\{ Quiz\(".*"\) \}\}/ const quizRegex = /\{\{ Quiz\(".*"\) \}\}/
const hasQuiz = quizRegex.test(data.body) hasQuiz.value = quizRegex.test(data.body)
if (!hasQuiz) allowDiscussions.value = true if (!hasQuiz.value) allowDiscussions.value = true
} }
}, }
})
const renderEditor = (holder, content) => { const renderEditor = (holder, content) => {
// empty the holder // empty the holder
@@ -448,6 +489,13 @@ watch(
} }
) )
watch(
() => lesson.data,
(data) => {
setupLesson(data)
}
)
const startTimer = () => { const startTimer = () => {
timerInterval = setInterval(() => { timerInterval = setInterval(() => {
timer.value++ timer.value++
@@ -463,13 +511,13 @@ onBeforeUnmount(() => {
}) })
const checkIfDiscussionsAllowed = () => { const checkIfDiscussionsAllowed = () => {
let quizPresent = false
JSON.parse(lesson.data?.content)?.blocks?.forEach((block) => { JSON.parse(lesson.data?.content)?.blocks?.forEach((block) => {
if (block.type === 'quiz') quizPresent = true if (block.type === 'quiz') hasQuiz.value = true
}) })
if ( if (
!quizPresent && !hasQuiz.value &&
!zenModeEnabled.value &&
(lesson.data?.membership || (lesson.data?.membership ||
user.data?.is_moderator || user.data?.is_moderator ||
user.data?.is_instructor) user.data?.is_instructor)
@@ -525,6 +573,25 @@ const goFullScreen = () => {
} }
} }
const showDiscussionsInZenMode = () => {
if (allowDiscussions.value) {
allowDiscussions.value = false
} else {
allowDiscussions.value = true
scrollDiscussionsIntoView()
}
}
const scrollDiscussionsIntoView = () => {
nextTick(() => {
discussionsContainer.value?.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'nearest',
})
})
}
const redirectToLogin = () => { const redirectToLogin = () => {
window.location.href = `/login?redirect-to=/lms/courses/${props.courseName}` window.location.href = `/login?redirect-to=/lms/courses/${props.courseName}`
} }