feat: Implement zero-latency WebSocket live message broadcast with luxury audio chime and Apple-style toast notifications

This commit is contained in:
Hamza-Ayed
2026-08-27 15:22:24 +03:00
parent 7faf223583
commit 70176deb3b
3 changed files with 248 additions and 19 deletions
+97 -2
View File
@@ -946,14 +946,109 @@ class StudentPortal
}
}
// ==========================================
// Luxury Audio Chime & Real-time Notifications
// ==========================================
function playChimeNotification() {
try {
const ctx = new (window.AudioContext || window.webkitAudioContext)();
const now = ctx.currentTime;
const osc1 = ctx.createOscillator();
const gain1 = ctx.createGain();
osc1.type = 'sine';
osc1.frequency.setValueAtTime(587.33, now);
gain1.gain.setValueAtTime(0.2, now);
gain1.gain.exponentialRampToValueAtTime(0.001, now + 0.35);
osc1.connect(gain1);
gain1.connect(ctx.destination);
osc1.start(now);
osc1.stop(now + 0.35);
const osc2 = ctx.createOscillator();
const gain2 = ctx.createGain();
osc2.type = 'sine';
osc2.frequency.setValueAtTime(880.00, now + 0.09);
gain2.gain.setValueAtTime(0.25, now + 0.09);
gain2.gain.exponentialRampToValueAtTime(0.001, now + 0.55);
osc2.connect(gain2);
gain2.connect(ctx.destination);
osc2.start(now + 0.09);
osc2.stop(now + 0.55);
} catch (e) {
console.warn('Audio Context note:', e);
}
}
function showLuxuryToast(title, body, icon = '👨‍🏫') {
let container = document.getElementById('saqel_toast_stack');
if (!container) {
container = document.createElement('div');
container.id = 'saqel_toast_stack';
container.style.cssText = 'position: fixed; top: 24px; left: 24px; z-index: 99999; display: flex; flex-direction: column; gap: 10px; pointer-events: none; direction: rtl;';
document.body.appendChild(container);
}
const toast = document.createElement('div');
toast.style.cssText = `
pointer-events: auto;
min-width: 280px;
max-width: 380px;
background: rgba(22, 27, 34, 0.92);
backdrop-filter: blur(24px);
-webkit-backdrop-filter: blur(24px);
border: 1px solid rgba(255, 255, 255, 0.15);
border-right: 4px solid var(--accent-color, #38BDF8);
border-radius: 16px;
padding: 14px 18px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.6), 0 0 25px rgba(56, 189, 248, 0.2);
display: flex;
align-items: center;
gap: 12px;
animation: toastSlideIn 0.35s cubic-bezier(0.16, 1, 0.3, 1) forwards;
cursor: pointer;
`;
toast.innerHTML = `
<div style="font-size: 24px;">${icon}</div>
<div style="flex: 1; min-width: 0;">
<div style="font-size: 13px; font-weight: 700; color: #FFFFFF; margin-bottom: 2px;">${escapeHtml(title)}</div>
<div style="font-size: 12px; color: var(--text-muted, #94A3B8); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">${escapeHtml(body)}</div>
</div>
`;
container.appendChild(toast);
setTimeout(() => {
toast.style.opacity = '0';
toast.style.transform = 'translateX(-30px)';
toast.style.transition = 'all 0.3s ease';
setTimeout(() => toast.remove(), 300);
}, 5000);
}
function triggerDesktopNotification(title, body) {
if ('Notification' in window && Notification.permission === 'granted') {
try {
new Notification(title, {
body: body,
icon: '/favicon.ico'
});
} catch (e) {}
}
}
function handleIncomingWsEvent(msg) {
const event = msg.event;
const data = msg.data;
if (event === 'authenticated') {
updateWsStatus(true);
} else if (event === 'chat_message' || event === 'chat_sent') {
appendChatMessage(data);
} else if (event === 'chat_message') {
appendChatMessage({ ...data, is_mine: false });
playChimeNotification();
showLuxuryToast('رسالة جديدة من الأستاذ حمزة الغويري 👨‍🏫', data.message || 'رد المعلم على استفسارك');
triggerDesktopNotification('صَقِل — رد الأستاذ حمزة الغويري', data.message || 'رد المعلم على استفسارك');
} else if (event === 'drm_session_terminated') {
alert('⚠️ تم فتح هذا الحساب من متصفح آخر.');
handleLogout();