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:
1 file changed
+77
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user