Skip to content

Commit

Permalink
feat: discussions
Browse files Browse the repository at this point in the history
  • Loading branch information
pateljannat committed Jan 15, 2024
1 parent bcee74c commit 3a5977a
Show file tree
Hide file tree
Showing 14 changed files with 784 additions and 349 deletions.
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"markdown-it": "^14.0.0",
"pinia": "^2.0.33",
"tailwindcss": "^3.2.7",
"socket.io-client": "^4.7.2",
"vue": "^3.2.25",
"vue-router": "^4.0.12"
},
Expand Down
8 changes: 2 additions & 6 deletions frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,10 @@ import { Toasts } from 'frappe-ui'
import { Dialogs } from '@/utils/dialogs'
import { computed, defineAsyncComponent } from 'vue'
import { useScreenSize } from './utils/composables'
import DesktopLayout from './components/DesktopLayout.vue'
import MobileLayout from './components/MobileLayout.vue'
const screenSize = useScreenSize()
const MobileLayout = defineAsyncComponent(() =>
import('@/components/MobileLayout.vue')
)
const DesktopLayout = defineAsyncComponent(() =>
import('@/components/DesktopLayout.vue')
)
const Layout = computed(() => {
if (screenSize.width < 640) {
Expand Down
110 changes: 110 additions & 0 deletions frontend/src/components/DiscussionReplies.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<template>
<div class="mt-6">
<div class="flex items-center mb-5">
<Button variant="subtle" @click="showTopics = true">
<ChevronLeft class="w-4 h-4 stroke-1.5" />
</Button>
<span class="text-lg font-semibold ml-2">
{{ topic.title }}
</span>
</div>

<div v-for="(reply, index) in replies.data">
<div
class="py-3"
:class="{ 'border-b': index + 1 != replies.data.length }"
>
<div class="flex items-center mb-2">
<UserAvatar :user="reply.user" class="mr-2" />
<span>
{{ reply.user.full_name }}
</span>
<span class="text-sm ml-2">
{{ timeAgo(reply.creation) }}
</span>
</div>
<div v-html="reply.reply"></div>
</div>
</div>
<TextEditor
:content="newReply"
@change="(val) => (newReply = val)"
placeholder="Type your reply here..."
editorClass="prose-sm py-2 px-2 min-h-[100px] border-gray-300 hover:border-gray-400 rounded-md bg-gray-200 w-full mt-5"
/>
<div class="flex justify-between mt-2">
<span> </span>
<Button @click="postReply()">
<span>
{{ __('Post') }}
</span>
</Button>
</div>
</div>
</template>
<script setup>
import { createResource, TextEditor, Button } from 'frappe-ui'
import { timeAgo } from '../utils'
import UserAvatar from '@/components/UserAvatar.vue'
import { ChevronLeft } from 'lucide-vue-next'
import { ref, inject, onMounted } from 'vue'
const showTopics = defineModel('showTopics')
const newReply = ref('')
const socket = inject('$socket')
const props = defineProps({
topic: {
type: Object,
required: true,
},
})
console.log(socket)
socket.on('publish_message', (data) => {
console.log('publish')
console.log(data)
replies.reload()
})
const replies = createResource({
url: 'lms.lms.utils.get_discussion_replies',
cache: ['replies', props.topic],
makeParams(values) {
return {
topic: props.topic.name,
}
},
auto: true,
})
const newReplyResource = createResource({
url: 'frappe.client.insert',
makeParams(values) {
return {
doc: {
doctype: 'Discussion Reply',
reply: newReply.value,
topic: props.topic.name,
},
}
},
})
const postReply = () => {
newReplyResource.submit(
{},
{
validate() {
if (!newReply.value) {
return __('Reply cannot be empty')
}
},
onSuccess() {
newReply.value = ''
replies.reload()
},
}
)
}
</script>
84 changes: 84 additions & 0 deletions frontend/src/components/Discussions.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<template>
<div v-if="topics.data">
<div>
<div class="text-xl font-semibold">
{{ __(title) }}
</div>
</div>
<div v-if="showTopics" v-for="(topic, index) in topics.data">
<div
@click="showReplies(topic)"
class="flex items-center cursor-pointer py-5"
:class="{ 'border-b': index + 1 != topics.data.length }"
>
<UserAvatar :user="topic.user" size="2xl" class="mr-4" />
<div>
<div class="text-lg font-semibold mb-1">
{{ topic.title }}
</div>
<div class="flex items-center">
<span>
{{ topic.user.full_name }}
</span>
<span class="text-sm ml-2">
{{ timeAgo(topic.creation) }}
</span>
</div>
</div>
</div>
</div>
<div v-else>
<DiscussionReplies
:topic="currentTopic"
v-model:showTopics="showTopics"
/>
</div>
</div>
</template>
<script setup>
import { createResource } from 'frappe-ui'
import UserAvatar from '@/components/UserAvatar.vue'
import { timeAgo } from '../utils'
import { ref, onMounted, inject } from 'vue'
import DiscussionReplies from '@/components/DiscussionReplies.vue'
const showTopics = ref(true)
const currentTopic = ref(null)
const socket = inject('$socket')
const props = defineProps({
title: {
type: String,
required: true,
},
doctype: {
type: String,
required: true,
},
docname: {
type: String,
required: true,
},
})
onMounted(() => {
socket.on('new_discussion_topic', (data) => {
topics.refresh()
})
})
const topics = createResource({
url: 'lms.lms.utils.get_discussion_topics',
cache: ['topics', props.doctype, props.docname],
params: {
doctype: props.doctype,
docname: props.docname,
},
auto: true,
})
const showReplies = (topic) => {
showTopics.value = false
currentTopic.value = topic
}
</script>
24 changes: 14 additions & 10 deletions frontend/src/components/MobileLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
<div class="h-full overflow-auto" id="scrollContainer">
<slot />
</div>
{{ tabs }}
<div
v-if="tabs"
class="grid grid-cols-5 border-t border-gray-300 standalone:pb-4"
:style="{ gridTemplateColumns: `repeat(${tabs.length}, minmax(0, 1fr))` }"
>
Expand All @@ -13,26 +15,28 @@
class="flex flex-col items-center justify-center py-3 transition active:scale-95"
@click="handleClick(tab)"
>
<component
:is="tab.icon"
class="h-6 w-6"
:class="[isActive(tab) ? 'text-gray-900' : 'text-gray-600']"
/>
{{ tab.label }}
<component :is="tab.icon" class="h-6 w-6" />
</button>
</div>
</div>
</template>
<script>
import { scrollTo } from '@/utils/scrollContainer'
<script setup>
import { getSidebarLinks } from '../utils'
import { useRouter } from 'vue-router'
import { computed } from 'vue'
const router = useRouter()
const tabs = getSidebarLinks()
const tabs = computed(() => {
return getSidebarLinks()
})
console.log(tabs.value)
let isActive = computed((tab) => {
/* let isActive = computed((tab) => {
console.log(tab);
return router.currentRoute.value.name === tab.to
})
}) */
const handleClick = (tab) => {
router.push({ name: tab.to })
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import dayjs from '@/utils/dayjs'
import translationPlugin from './translation'
import { usersStore } from './stores/user'
import { sessionStore } from './stores/session'
import { initSocket } from './socket'
import {
FrappeUI,
setConfig,
Expand All @@ -26,6 +27,7 @@ app.use(router)
app.use(translationPlugin)
app.use(pageMetaPlugin)
app.provide('$dayjs', dayjs)
app.provide('$socket', initSocket())
app.mount('#app')

const { userResource } = usersStore()
Expand Down
18 changes: 17 additions & 1 deletion frontend/src/pages/Lesson.vue
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,14 @@
</div>
</div>
</div>
<div class="mt-20">
<Discussions
v-if="allowDiscussions()"
:title="'Questions'"
:doctype="'Course Lesson'"
:docname="lesson.data.name"
/>
</div>
</div>
<div class="sticky top-10">
<div class="bg-gray-50 p-5 border-b-2">
Expand Down Expand Up @@ -194,13 +202,13 @@
<script setup>
import { createResource, Breadcrumbs, Button } from 'frappe-ui'
import { computed, watch, onBeforeMount, onUnmounted, inject } from 'vue'
import { useStorage } from '@vueuse/core'
import CourseOutline from '@/components/CourseOutline.vue'
import UserAvatar from '@/components/UserAvatar.vue'
import { useRoute } from 'vue-router'
import MarkdownIt from 'markdown-it'
import { ChevronLeft, ChevronRight } from 'lucide-vue-next'
import Quiz from '@/components/Quiz.vue'
import Discussions from '@/components/Discussions.vue'
const user = inject('$user')
const route = useRoute()
Expand Down Expand Up @@ -327,6 +335,14 @@ const getId = (block) => {
const redirectToLogin = () => {
window.location.href = `/login?redirect_to=/courses/${props.courseName}/learn/${route.params.chapterNumber}-${route.params.lessonNumber}`
}
const allowDiscussions = () => {
return (
course.data?.membership ||
user.data?.is_moderator ||
user.data?.is_instructor
)
}
</script>
<style>
.avatar-group {
Expand Down
Loading

0 comments on commit 3a5977a

Please sign in to comment.