feat: Implement zero-latency WebSocket live message broadcast with luxury audio chime and Apple-style toast notifications
This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user