feat: batch notifications
This commit is contained in:
Submodule frappe-ui updated: 5c0513c2df...38728b80aa
@@ -21,7 +21,7 @@
|
|||||||
"chart.js": "^4.4.1",
|
"chart.js": "^4.4.1",
|
||||||
"dayjs": "^1.11.6",
|
"dayjs": "^1.11.6",
|
||||||
"feather-icons": "^4.28.0",
|
"feather-icons": "^4.28.0",
|
||||||
"frappe-ui": "^0.1.54",
|
"frappe-ui": "^0.1.56",
|
||||||
"lucide-vue-next": "^0.309.0",
|
"lucide-vue-next": "^0.309.0",
|
||||||
"markdown-it": "^14.0.0",
|
"markdown-it": "^14.0.0",
|
||||||
"pinia": "^2.0.33",
|
"pinia": "^2.0.33",
|
||||||
|
|||||||
@@ -141,7 +141,6 @@ function enrollStudent() {
|
|||||||
const enrollStudentResource = createResource({
|
const enrollStudentResource = createResource({
|
||||||
url: 'lms.lms.doctype.lms_enrollment.lms_enrollment.create_membership',
|
url: 'lms.lms.doctype.lms_enrollment.lms_enrollment.create_membership',
|
||||||
})
|
})
|
||||||
console.log(props.course)
|
|
||||||
enrollStudentResource
|
enrollStudentResource
|
||||||
.submit({
|
.submit({
|
||||||
course: props.course.data.name,
|
course: props.course.data.name,
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TextEditor
|
<TextEditor
|
||||||
class="mt-5"
|
class="mt-5 reply-editor"
|
||||||
:content="newReply"
|
:content="newReply"
|
||||||
:mentions="mentionUsers"
|
:mentions="mentionUsers"
|
||||||
@change="(val) => (newReply = val)"
|
@change="(val) => (newReply = val)"
|
||||||
@@ -151,7 +151,6 @@ const newReplyResource = createResource({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const mentionUsers = computed(() => {
|
const mentionUsers = computed(() => {
|
||||||
console.log(allUsers.data['jannat@frappe.io'])
|
|
||||||
let users = Object.values(allUsers.data).map((user) => {
|
let users = Object.values(allUsers.data).map((user) => {
|
||||||
return {
|
return {
|
||||||
value: user.name,
|
value: user.name,
|
||||||
|
|||||||
@@ -63,13 +63,14 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { createResource, Button, TextEditor } from 'frappe-ui'
|
import { createResource, Button } from 'frappe-ui'
|
||||||
import UserAvatar from '@/components/UserAvatar.vue'
|
import UserAvatar from '@/components/UserAvatar.vue'
|
||||||
import { timeAgo } from '../utils'
|
import { timeAgo } from '../utils'
|
||||||
import { ref, onMounted, inject } from 'vue'
|
import { ref, onMounted, inject } from 'vue'
|
||||||
import DiscussionReplies from '@/components/DiscussionReplies.vue'
|
import DiscussionReplies from '@/components/DiscussionReplies.vue'
|
||||||
import DiscussionModal from '@/components/Modals/DiscussionModal.vue'
|
import DiscussionModal from '@/components/Modals/DiscussionModal.vue'
|
||||||
import { MessageSquareText } from 'lucide-vue-next'
|
import { MessageSquareText } from 'lucide-vue-next'
|
||||||
|
import { getScrollContainer } from '@/utils/scrollContainer'
|
||||||
|
|
||||||
const showTopics = ref(true)
|
const showTopics = ref(true)
|
||||||
const currentTopic = ref(null)
|
const currentTopic = ref(null)
|
||||||
@@ -102,6 +103,10 @@ const props = defineProps({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
scrollToBottom: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -110,8 +115,19 @@ onMounted(() => {
|
|||||||
socket.on('new_discussion_topic', (data) => {
|
socket.on('new_discussion_topic', (data) => {
|
||||||
topics.refresh()
|
topics.refresh()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (props.scrollToBottom) {
|
||||||
|
setTimeout(() => {
|
||||||
|
scrollToEnd()
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const scrollToEnd = () => {
|
||||||
|
let scrollContainer = getScrollContainer()
|
||||||
|
scrollContainer.scrollTop = scrollContainer.scrollHeight
|
||||||
|
}
|
||||||
|
|
||||||
const topics = createResource({
|
const topics = createResource({
|
||||||
url: 'lms.lms.utils.get_discussion_topics',
|
url: 'lms.lms.utils.get_discussion_topics',
|
||||||
cache: ['topics', props.doctype, props.docname],
|
cache: ['topics', props.doctype, props.docname],
|
||||||
|
|||||||
@@ -34,9 +34,7 @@ const props = defineProps({
|
|||||||
default: 'Tags',
|
default: 'Tags',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
console.log(props.modelValue)
|
|
||||||
let tags = ref(props.modelValue)
|
let tags = ref(props.modelValue)
|
||||||
console.log(tags.value)
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
let newTag = ref('')
|
let newTag = ref('')
|
||||||
|
|
||||||
|
|||||||
@@ -66,9 +66,10 @@
|
|||||||
<Discussions
|
<Discussions
|
||||||
doctype="LMS Batch"
|
doctype="LMS Batch"
|
||||||
:docname="batch.data.name"
|
:docname="batch.data.name"
|
||||||
title="Discussions"
|
:title="__('Discussions')"
|
||||||
:key="batch.data.name"
|
:key="batch.data.name"
|
||||||
:singleThread="true"
|
:singleThread="true"
|
||||||
|
:scrollToBottom="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -85,7 +85,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, inject } from 'vue'
|
import { computed, inject } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { BookOpen, Calendar, Clock } from 'lucide-vue-next'
|
import { BookOpen, Clock } from 'lucide-vue-next'
|
||||||
import { formatTime } from '@/utils'
|
import { formatTime } from '@/utils'
|
||||||
import { Breadcrumbs, createResource } from 'frappe-ui'
|
import { Breadcrumbs, createResource } from 'frappe-ui'
|
||||||
import CourseCard from '@/components/CourseCard.vue'
|
import CourseCard from '@/components/CourseCard.vue'
|
||||||
|
|||||||
@@ -115,10 +115,10 @@ const readNotifications = createListResource({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const markAsRead = createResource({
|
const markAsRead = createResource({
|
||||||
url: 'frappe.desk.doctype.notification_log.notification_log.mark_as_read',
|
url: 'lms.lms.api.mark_as_read',
|
||||||
makeParams(values) {
|
makeParams(values) {
|
||||||
return {
|
return {
|
||||||
docname: values.name,
|
name: values.name,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
@@ -128,7 +128,7 @@ const markAsRead = createResource({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const markAllAsRead = createResource({
|
const markAllAsRead = createResource({
|
||||||
url: 'frappe.desk.doctype.notification_log.notification_log.mark_all_as_read',
|
url: 'lms.lms.api.mark_all_as_read',
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
unReadNotifications.reload()
|
unReadNotifications.reload()
|
||||||
readNotifications.reload()
|
readNotifications.reload()
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ export class Quiz {
|
|||||||
}
|
}
|
||||||
|
|
||||||
save(blockContent) {
|
save(blockContent) {
|
||||||
console.log(blockContent)
|
|
||||||
return {
|
return {
|
||||||
quiz: this.data.quiz,
|
quiz: this.data.quiz,
|
||||||
}
|
}
|
||||||
|
|||||||
11
frontend/src/utils/scrollContainer.js
Normal file
11
frontend/src/utils/scrollContainer.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export function scrollTo(...options) {
|
||||||
|
if (!options || options.length === 0) return
|
||||||
|
const container = getScrollContainer()
|
||||||
|
if (!container) return
|
||||||
|
container.scrollTo(...options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getScrollContainer() {
|
||||||
|
// window.scrollContainer is reference to the scroll container in DesktopLayout.vue and MobileLayout.vue
|
||||||
|
return window.scrollContainer
|
||||||
|
}
|
||||||
@@ -103,7 +103,7 @@ doc_events = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Discussion Reply": {"after_insert": "lms.lms.utils.handle_notifications"},
|
"Discussion Reply": {"after_insert": "lms.lms.utils.handle_notifications"},
|
||||||
"Notification Log": {"after_insert": "lms.lms.utils.publish_notifications"},
|
"Notification Log": {"on_change": "lms.lms.utils.publish_notifications"},
|
||||||
}
|
}
|
||||||
|
|
||||||
# Scheduled Tasks
|
# Scheduled Tasks
|
||||||
|
|||||||
@@ -398,3 +398,20 @@ def get_all_users():
|
|||||||
)
|
)
|
||||||
|
|
||||||
return {user.name: user for user in users}
|
return {user.name: user for user in users}
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def mark_as_read(name):
|
||||||
|
doc = frappe.get_doc("Notification Log", name)
|
||||||
|
doc.read = 1
|
||||||
|
doc.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def mark_all_as_read():
|
||||||
|
notifications = frappe.get_all(
|
||||||
|
"Notification Log", {"for_user": frappe.session.user, "read": 0}, pluck="name"
|
||||||
|
)
|
||||||
|
|
||||||
|
for notification in notifications:
|
||||||
|
mark_as_read(notification)
|
||||||
|
|||||||
@@ -645,33 +645,40 @@ def handle_notifications(doc, method):
|
|||||||
|
|
||||||
|
|
||||||
def create_notification_log(doc, topic):
|
def create_notification_log(doc, topic):
|
||||||
course = frappe.db.get_value("Course Lesson", topic.reference_docname, "course")
|
users = []
|
||||||
course_title = frappe.db.get_value("LMS Course", course, "title")
|
if topic.reference_doctype == "LMS Course":
|
||||||
instructors = frappe.db.get_all(
|
course = frappe.db.get_value("Course Lesson", topic.reference_docname, "course")
|
||||||
"Course Instructor", {"parent": course}, pluck="instructor"
|
course_title = frappe.db.get_value("LMS Course", course, "title")
|
||||||
)
|
instructors = frappe.db.get_all(
|
||||||
|
"Course Instructor", {"parent": course}, pluck="instructor"
|
||||||
|
)
|
||||||
|
users.append(topic.owner)
|
||||||
|
users += instructors
|
||||||
|
subject = _("New reply on the topic {0} in course {1}").format(
|
||||||
|
topic.title, course_title
|
||||||
|
)
|
||||||
|
link = get_lesson_url(course, get_lesson_index(topic.reference_docname))
|
||||||
|
|
||||||
|
else:
|
||||||
|
batch_title = frappe.db.get_value("LMS Batch", topic.reference_docname, "title")
|
||||||
|
subject = _("New comment in batch {0}").format(batch_title)
|
||||||
|
link = f"/batches/{topic.reference_docname}"
|
||||||
|
moderators = frappe.get_all("Has Role", {"role": "Moderator"}, pluck="parent")
|
||||||
|
users += moderators
|
||||||
|
|
||||||
notification = frappe._dict(
|
notification = frappe._dict(
|
||||||
{
|
{
|
||||||
"subject": _("New reply on the topic {0} in course {1}").format(
|
"subject": subject,
|
||||||
topic.title, course_title
|
|
||||||
),
|
|
||||||
"email_content": doc.reply,
|
"email_content": doc.reply,
|
||||||
"document_type": topic.reference_doctype,
|
"document_type": topic.reference_doctype,
|
||||||
"document_name": topic.reference_docname,
|
"document_name": topic.reference_docname,
|
||||||
"for_user": topic.owner,
|
"for_user": topic.owner,
|
||||||
"from_user": doc.owner,
|
"from_user": doc.owner,
|
||||||
"type": "Alert",
|
"type": "Alert",
|
||||||
"link": get_lesson_url(course, get_lesson_index(topic.reference_docname)),
|
"link": link,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
users = []
|
|
||||||
if doc.owner != topic.owner:
|
|
||||||
users.append(topic.owner)
|
|
||||||
|
|
||||||
if doc.owner not in instructors:
|
|
||||||
users += instructors
|
|
||||||
make_notification_logs(notification, users)
|
make_notification_logs(notification, users)
|
||||||
|
|
||||||
|
|
||||||
@@ -681,21 +688,31 @@ def notify_mentions_on_portal(doc, topic):
|
|||||||
return
|
return
|
||||||
|
|
||||||
from_user_name = get_fullname(doc.owner)
|
from_user_name = get_fullname(doc.owner)
|
||||||
course = frappe.db.get_value("Course Lesson", topic.reference_docname, "course")
|
|
||||||
|
if topic.reference_doctype == "LMS Course":
|
||||||
|
course = frappe.db.get_value("Course Lesson", topic.reference_docname, "course")
|
||||||
|
subject = _("{0} mentioned you in a comment in {1}").format(
|
||||||
|
from_user_name, topic.title
|
||||||
|
)
|
||||||
|
link = get_lesson_url(course, get_lesson_index(topic.reference_docname))
|
||||||
|
else:
|
||||||
|
batch_title = frappe.db.get_value("LMS Batch", topic.reference_docname, "title")
|
||||||
|
subject = _("{0} mentioned you in a comment in {1}").format(
|
||||||
|
from_user_name, batch_title
|
||||||
|
)
|
||||||
|
link = f"/batches/{topic.reference_docname}"
|
||||||
|
|
||||||
for user in mentions:
|
for user in mentions:
|
||||||
notification = frappe._dict(
|
notification = frappe._dict(
|
||||||
{
|
{
|
||||||
"subject": _("{0} mentioned you in a comment in {1}").format(
|
"subject": subject,
|
||||||
from_user_name, topic.title
|
|
||||||
),
|
|
||||||
"email_content": doc.reply,
|
"email_content": doc.reply,
|
||||||
"document_type": topic.reference_doctype,
|
"document_type": topic.reference_doctype,
|
||||||
"document_name": topic.reference_docname,
|
"document_name": topic.reference_docname,
|
||||||
"for_user": user,
|
"for_user": user,
|
||||||
"from_user": doc.owner,
|
"from_user": doc.owner,
|
||||||
"type": "Alert",
|
"type": "Alert",
|
||||||
"link": get_lesson_url(course, get_lesson_index(topic.reference_docname)),
|
"link": link,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
make_notification_logs(notification, user)
|
make_notification_logs(notification, user)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "frappe_lms",
|
"name": "frappe_lms",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Easy to use, open-source, Learning Management System",
|
"description": "Easy to use, open-source, Learning Management System",
|
||||||
"workspaces": [
|
"workspaces1": [
|
||||||
"frappe-ui",
|
"frappe-ui",
|
||||||
"frontend"
|
"frontend"
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user