feat(templates): add push notification prompt UI and JS logic

Enable real-time user notifications to improve engagement and ensure timely updates.
This commit is contained in:
sankalp committed 2026-06-30 10:22:21 +05:45
1 parent 612a3dd151
commit 22826fe78b
1 file changed
+77
+77
View File
@@ -229,6 +229,21 @@
</footer>
</div>
</div>
<div id="push-prompt" class="hidden fixed bottom-24 right-4 z-[999] max-w-sm w-full bg-white dark:bg-gray-950 rounded-xl shadow-2xl border border-gray-200 dark:border-gray-700 p-5 transition-all duration-300">
<div class="flex items-start gap-4">
<div class="shrink-0 w-10 h-10 rounded-full bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center">
<svg class="w-5 h-5 text-blue-600 dark:text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/></svg>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-semibold text-gray-900 dark:text-white">Enable notifications?</p>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Get notified about ticket updates and mentions in real time.</p>
</div>
</div>
<div class="flex justify-end gap-2 mt-4">
<button onclick="dismissPush()" class="px-3 py-1.5 text-xs font-medium rounded-lg border border-gray-200 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800">Not now</button>
<button onclick="enablePush()" class="px-3 py-1.5 text-xs font-medium rounded-lg text-white bg-blue-600 hover:bg-blue-700">Enable</button>
</div>
</div>
{% include "components/confirm_modal.html" %}
{% include "components/toast_container.html" %}
@@ -262,6 +277,68 @@
});
document.addEventListener('htmx:afterSettle', initTooltips);
initTooltips();
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.startsWith(name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
function doPushSubscribe(reg) {
const vapidKey = "{{ vapid_public_key }}";
if (!vapidKey) return;
reg.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: vapidKey,
}).then(sub => {
fetch('/api/notifications/push/subscribe/', {
method: 'POST',
headers: {'Content-Type': 'application/json', 'X-CSRFToken': getCookie('csrftoken')},
body: JSON.stringify({
endpoint: sub.endpoint,
auth_key: btoa(String.fromCharCode(...new Uint8Array(sub.getKey('auth')))),
p256dh_key: btoa(String.fromCharCode(...new Uint8Array(sub.getKey('p256dh')))),
}),
});
}).catch(() => {});
}
const vapidPublicKey = "{{ vapid_public_key }}";
if (vapidPublicKey && 'serviceWorker' in navigator && 'PushManager' in window) {
navigator.serviceWorker.register('/sw.js').then(reg => {
if (Notification.permission === 'granted') {
doPushSubscribe(reg);
} else if (Notification.permission === 'default' && !localStorage.getItem('push_declined')) {
document.getElementById('push-prompt').classList.remove('hidden');
window.__pushReg = reg;
}
}).catch(() => {});
}
window.enablePush = function() {
const reg = window.__pushReg;
if (!reg) return;
Notification.requestPermission().then(perm => {
if (perm === 'granted') {
doPushSubscribe(reg);
}
document.getElementById('push-prompt').classList.add('hidden');
});
};
window.dismissPush = function() {
localStorage.setItem('push_declined', '1');
document.getElementById('push-prompt').classList.add('hidden');
};
</script>
{% block extra_scripts %}{% endblock %}
</body>