[ADD] NOTIFICATION: Added notification sound

This commit is contained in:
Horilla
2024-12-31 18:56:48 +05:30
parent 9aa78f56e4
commit 0c974724d8
7 changed files with 172 additions and 91 deletions

View File

@@ -1750,6 +1750,15 @@ class PenaltyAccounts(HorillaModel):
ordering = ["-created_at"]
class NotificationSound(models.Model):
from employee.models import Employee
employee = models.OneToOneField(
Employee, on_delete=models.CASCADE, related_name="notification_sound"
)
sound_enabled = models.BooleanField(default=False)
@receiver(post_save, sender=PenaltyAccounts)
def create_deduction_cutleave_from_penalty(sender, instance, created, **kwargs):
"""

View File

@@ -8,36 +8,29 @@ var notify_refresh_period = 15000;
var consecutive_misfires = 0;
var registered_functions = [];
function fill_notification_badge(data) {
var badges = document.getElementsByClassName(notify_badge_class);
if (badges) {
for(var i = 0; i < badges.length; i++){
badges[i].innerHTML = data.unread_count;
}
}
}
function fill_notification_list(data) {
var menus = document.getElementsByClassName(notify_menu_class);
if (menus) {
var messages = data.unread_list.map(function (item) {
var message = "";
if(typeof item.actor !== 'undefined'){
message = item.actor;
}
if(typeof item.verb !== 'undefined'){
message = message + " " + item.verb;
}
if(typeof item.target !== 'undefined'){
message = message + " " + item.target;
}
if(typeof item.timestamp !== 'undefined'){
message = message + " " + item.timestamp;
}
return '<li>' + message + '</li>';
}).join('')
var messages = data.unread_list
.map(function (item) {
var message = "";
if (typeof item.actor !== "undefined") {
message = item.actor;
}
if (typeof item.verb !== "undefined") {
message = message + " " + item.verb;
}
if (typeof item.target !== "undefined") {
message = message + " " + item.target;
}
if (typeof item.timestamp !== "undefined") {
message = message + " " + item.timestamp;
}
return "<li>" + message + "</li>";
})
.join("");
for (var i = 0; i < menus.length; i++){
for (var i = 0; i < menus.length; i++) {
menus[i].innerHTML = messages;
}
}
@@ -51,30 +44,30 @@ function fetch_api_data() {
if (registered_functions.length > 0) {
//only fetch data if a function is setup
var r = new XMLHttpRequest();
r.addEventListener('readystatechange', function(event){
if (this.readyState === 4){
if (this.status === 200){
r.addEventListener("readystatechange", function (event) {
if (this.readyState === 4) {
if (this.status === 200) {
consecutive_misfires = 0;
var data = JSON.parse(r.responseText);
for(var i = 0; i < registered_functions.length; i++) {
registered_functions[i](data);
for (var i = 0; i < registered_functions.length; i++) {
registered_functions[i](data);
}
}else{
} else {
consecutive_misfires++;
}
}
})
r.open("GET", notify_api_url+'?max='+notify_fetch_count, true);
});
r.open("GET", notify_api_url + "?max=" + notify_fetch_count, true);
r.send();
}
if (consecutive_misfires < 10) {
setTimeout(fetch_api_data,notify_refresh_period);
setTimeout(fetch_api_data, notify_refresh_period);
} else {
var badges = document.getElementsByClassName(notify_badge_class);
if (badges) {
for (var i = 0; i < badges.length; i++){
for (var i = 0; i < badges.length; i++) {
badges[i].innerHTML = "!";
badges[i].title = "Connection lost!"
badges[i].title = "Connection lost!";
}
}
}

View File

@@ -6,6 +6,7 @@ from distutils.version import ( # pylint: disable=no-name-in-module,import-erro
)
from django import get_version
from django.urls import path
from . import views
@@ -44,6 +45,11 @@ urlpatterns = [
views.live_all_notification_list,
name="live_all_notification_list",
),
path(
"notification-sound",
views.notification_sound,
name="notification-sound",
),
]
app_name = "notifications"

View File

@@ -7,12 +7,14 @@ from distutils.version import ( # pylint: disable=no-name-in-module,import-erro
from django import get_version
from django.contrib.auth.decorators import login_required
from django.forms import model_to_dict
from django.http import HttpResponse # noqa
from django.shortcuts import get_object_or_404, redirect
from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache
from django.views.generic import ListView
from swapper import load_model
from base.models import NotificationSound
from notifications import settings
from notifications.settings import get_config
from notifications.utils import id2slug, slug2id
@@ -25,8 +27,6 @@ else:
# Django 1.6 doesn't have a proper JsonResponse
import json
from django.http import HttpResponse # noqa
def date_handler(obj):
return obj.isoformat() if hasattr(obj, "isoformat") else obj
@@ -248,3 +248,14 @@ def live_all_notification_count(request):
"all_count": request.user.notifications.count(),
}
return JsonResponse(data)
@login_required
def notification_sound(request):
employee = request.user.employee_get
sound, created = NotificationSound.objects.get_or_create(employee=employee)
if not created:
sound.sound_enabled = not sound.sound_enabled
sound.save()
return HttpResponse("")

Binary file not shown.

View File

@@ -1,63 +1,104 @@
{% load basefilters %}
{% load static %}
{% load i18n %}
{% load notifications_tags %}
{% notifications_unread as unread_count %}
{% load basefilters %} {% load static %} {% load i18n %}
{% load notifications_tags %} {% notifications_unread as unread_count %}
<div
class="oh-navbar__notifications"
id="notificationIcon"
x-data="{open: false}"
hx-get="{% url 'notifications' %}"
hx-target="#notificationContainer"
class="oh-navbar__notifications"
id="notificationIcon"
x-data="{open: false}"
hx-get="{% url 'notifications' %}"
hx-target="#notificationContainer"
>
<a href="#" class="oh-navbar__notification-link" @click="open = !open" title="Notifications">
<ion-icon name="notifications-outline" class="oh-navbar__icon"></ion-icon>
<span class="oh-navbar__notification-beacon">
{% live_notify_badge %}
</span>
<a
href="#"
class="oh-navbar__notification-link"
@click="open = !open"
title="Notifications"
>
<ion-icon
name="notifications-outline"
class="oh-navbar__icon"
></ion-icon>
<span class="oh-navbar__notification-beacon">
{% live_notify_badge %}
</span>
</a>
<div id="showallnotificationbtn" class="oh-navbar__notification-tray"
x-data="{markRead: false, visible: true}" x-show="open" style="display: none;"
@click.outside="open = false">
<div id="notificationContainer">
{% include 'notification/notification_items.html' %}
</div>
<div class="oh-navbar__notification-footer" x-show="visible">
<a
{% comment %} href="{% url 'notifications:all' %}" {% endcomment %}
id="viewallnotification"
data-target="#allNotifications"
hx-get="{% url 'all-notifications' %}"
hx-target="#allNotificationBody"
class="oh-navbar__notification-tray-link oh-activity-sidebar__open">{% trans "View all notifications" %}</a>
</div>
<div class="oh-navbar__notification-empty" x-show="!visible">
<img src="{% static 'images/ui/happy.svg' %}" alt="All caught up" width="50" height="50" loading="lazy" />
<span class="oh-navbar__notification-empty-title">{% trans "All caught up!" %}</span>
<span class="oh-navbar__notification-empty-desc">{% trans "You have no new notifications at the moment." %}</span>
</div>
<div
id="showallnotificationbtn"
class="oh-navbar__notification-tray"
x-data="{markRead: false, visible: true}"
x-show="open"
style="display: none"
@click.outside="open = false"
>
<div id="notificationContainer">
{% include 'notification/notification_items.html' %}
</div>
<div class="oh-navbar__notification-empty" x-show="!visible">
<img
src="{% static 'images/ui/happy.svg' %}"
alt="All caught up"
width="50"
height="50"
loading="lazy"
/>
<span class="oh-navbar__notification-empty-title"
>{% trans "All caught up!" %}</span
>
<span class="oh-navbar__notification-empty-desc"
>{% trans "You have no new notifications at the moment." %}</span
>
</div>
</div>
</div>
</div>
<script src="{% static 'notifications/notify.js' %}" type="text/javascript"></script>
{% register_notify_callbacks callbacks='fill_notification_list,fill_notification_badge' %}
<script>
function markAsRead(notificationId) {
fetch('/notifications/mark-as-read/' + notificationId + '/')
.then(response => {
if (response.ok) {
// Reload the page to update the notifications
location.reload();
} else {
console.error('Failed to mark notification as read');
}
});
}
var staticUrl = $("#statiUrl").attr("data-url");
var notification_sound = {{request.user.employee_get.notification_sound.sound_enabled|yesno:"true,false"}};
function fill_notification_badge(data) {
var badges = document.getElementsByClassName(notify_badge_class);
let previousUnreadCount = parseInt(localStorage.getItem('previousUnreadCount')) || 0;
if (notification_sound) {
if (
previousUnreadCount !== null &&
previousUnreadCount < data.unread_count
) {
const audio = new Audio(
`${staticUrl}static/audio/notification-sound.wav`
);
audio.play();
}
}
$("#viewallnotification").click(function () {
$("#showallnotificationbtn").toggle();
});
if (badges) {
for (var i = 0; i < badges.length; i++) {
badges[i].innerHTML = data.unread_count;
}
}
localStorage.setItem('previousUnreadCount', data.unread_count);
}
</script>
<script
src="{% static 'notifications/notify.js' %}"
type="text/javascript"
></script>
{% register_notify_callbacks callbacks='fill_notification_list,fill_notification_badge' %}
<script>
function markAsRead(notificationId) {
fetch("/notifications/mark-as-read/" + notificationId + "/").then(
(response) => {
if (response.ok) {
// Reload the page to update the notifications
location.reload();
} else {
console.error("Failed to mark notification as read");
}
}
);
}
$("#viewallnotification").click(function () {
$("#showallnotificationbtn").toggle();
});
</script>

View File

@@ -103,6 +103,27 @@
</div>
{% endif %}
</div>
<div class="oh-navbar__notification-footer" x-show="visible" style="position:relative;">
<a
id="viewallnotification"
data-target="#allNotifications"
hx-get="{% url 'all-notifications' %}"
hx-target="#allNotificationBody"
class="oh-navbar__notification-tray-link oh-activity-sidebar__open"
>{% trans "View all notifications" %} </a
>
<div class="oh-navbar__volume-icon" title="{% trans 'Notification Sound' %}" style="position:absolute; right:25px" hx-get="{% url 'notifications:notification-sound' %}" hx-swap="none">
{% if request.user.employee_get.notification_sound.sound_enabled %}
<span class="material-symbols-outlined text-danger">
volume_up
</span>
{% else %}
<span class="material-symbols-outlined text-danger">
volume_off
</span>
{% endif %}
</div>
</div>
<script>
$(document).ready(function () {
$(".oh-navbar__notification-item").on("click", function (event) {