|
Server IP : 217.21.85.138 / Your IP : 216.73.216.103 Web Server : LiteSpeed System : Linux in-mum-web906.main-hosting.eu 4.18.0-553.37.1.lve.el8.x86_64 #1 SMP Mon Feb 10 22:45:17 UTC 2025 x86_64 User : u915722082 ( 915722082) PHP Version : 7.4.33 Disable Function : system, exec, shell_exec, passthru, mysql_list_dbs, ini_alter, dl, symlink, link, chgrp, leak, popen, apache_child_terminate, virtual, mb_send_mail MySQL : OFF | cURL : ON | WGET : ON | Perl : OFF | Python : OFF Directory (0755) : /home/u915722082/public_html/js/../pms/config/../admin/includes/ |
| [ Home ] | [ C0mmand ] | [ Upload File ] |
|---|
<!-- Optimized Chat Widget - Fast Loading + Notifications -->
<div id="chatWidget" style="position: fixed; bottom: 20px; right: 20px; z-index: 9999;">
<!-- Chat Button (When Closed) -->
<button id="chatButton" class="btn btn-primary shadow-lg" style="width: 60px; height: 60px; border-radius: 50%; display: flex; align-items: center; justify-content: center; position: relative;">
<i class="ki-duotone ki-message-text-2 fs-2x text-white">
<span class="path1"></span>
<span class="path2"></span>
<span class="path3"></span>
</i>
<span id="unreadBadge" class="badge badge-circle badge-danger" style="position: absolute; top: 0; right: 0; width: 20px; height: 20px; display: none; font-size: 11px; padding: 0; line-height: 20px;">0</span>
</button>
<!-- Chat Window (Hidden by default) -->
<div id="chatWindow" class="card shadow-lg" style="width: 380px;display: none; margin-bottom: 10px; border-radius: 15px; overflow: hidden;">
<!-- Chat List View -->
<div id="chatListView">
<!-- Chat Header -->
<div class="card-header bg-primary" style="padding: 15px; border-radius: 15px 15px 0 0;">
<div class="d-flex align-items-center justify-content-between w-100">
<div class="d-flex align-items-center">
<div class="symbol symbol-35px me-3">
<span class="symbol-label bg-white">
<i class="ki-duotone ki-message-text-2 fs-2 text-primary">
<span class="path1"></span>
<span class="path2"></span>
<span class="path3"></span>
</i>
</span>
</div>
<div>
<h5 class="text-white mb-0">Messages</h5>
<small class="text-white" style="opacity: 0.8;">
<span class="online-indicator-widget"></span>
<span id="onlineCountWidget">Loading...</span>
</small>
</div>
</div>
<button id="closeChatBtn" class="btn btn-sm btn-icon" style="background: transparent; border: none; padding: 0; color: white;">
<i class="ki-duotone ki-cross fs-2" style="color:white">
<span class="path1"></span>
<span class="path2"></span>
</i>
</button>
</div>
</div>
<!-- Chat Tabs -->
<div class="card-body" style="padding: 0;">
<ul class="nav nav-tabs nav-line-tabs mb-0" style="padding: 10px 15px 0 15px; background: white;">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="tab" href="#teamChatTab">Team Chat</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="tab" href="#directMessagesTab">Direct Messages</a>
</li>
</ul>
<div class="tab-content" style="height: 435px; overflow-y: auto;">
<!-- Team Chat Tab -->
<div class="tab-pane fade show active" id="teamChatTab">
<div class="p-4">
<button id="openTeamChatBtn" class="btn btn-primary w-100">
<i class="ki-duotone ki-message-text-2 fs-3">
<span class="path1"></span>
<span class="path2"></span>
<span class="path3"></span>
</i>
Open Team Chat
</button>
</div>
</div>
<!-- Direct Messages Tab -->
<div class="tab-pane fade" id="directMessagesTab">
<div id="usersList" class="p-3">
<div class="text-center text-muted py-10">
<i class="ki-duotone ki-profile-user fs-3x mb-3">
<span class="path1"></span>
<span class="path2"></span>
<span class="path3"></span>
<span class="path4"></span>
</i>
<p>Loading users...</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Conversation View -->
<div id="conversationView" style="display: none; height: 100%;">
<!-- Conversation Header -->
<div class="card-header bg-primary" style="padding: 15px; border-radius: 15px 15px 0 0;">
<div class="d-flex align-items-center justify-content-between w-100">
<div class="d-flex align-items-center">
<button id="backToListBtn" class="btn btn-sm btn-icon me-2" style="background: transparent; border: none; padding: 0; color: white;">
<i class="ki-duotone ki-arrow-left fs-2">
<span class="path1"></span>
<span class="path2"></span>
</i>
</button>
<div class="symbol symbol-35px me-3">
<span id="conversationAvatar" class="symbol-label bg-white">
<i class="ki-duotone ki-message-text-2 fs-2 text-primary">
<span class="path1"></span>
<span class="path2"></span>
<span class="path3"></span>
</i>
</span>
</div>
<div>
<h5 id="conversationTitle" class="text-white mb-0">Chat</h5>
<small id="conversationSubtitle" class="text-white" style="opacity: 0.8;"></small>
</div>
</div>
<button id="closeConversationBtn" class="btn btn-sm btn-icon" style="background: transparent; border: none; padding: 0; color: white;">
<i class="ki-duotone ki-cross fs-2" style="color:white">
<span class="path1"></span>
<span class="path2"></span>
</i>
</button>
</div>
</div>
<!-- Reply Preview -->
<div id="replyPreview" style="display: none; background: #f8f9fa; padding: 10px 15px; border-bottom: 1px solid #e4e6ef;">
<div class="d-flex align-items-center justify-content-between">
<div style="flex: 1; border-left: 3px solid #3699ff; padding-left: 10px;">
<div style="font-size: 12px; font-weight: 600; color: #3699ff;" id="replyToName"></div>
<div style="font-size: 13px; color: #666;" id="replyToMessage"></div>
</div>
<button type="button" onclick="cancelReply()" class="btn btn-sm btn-icon">
<i class="ki-duotone ki-cross fs-3">
<span class="path1"></span>
<span class="path2"></span>
</i>
</button>
</div>
</div>
<!-- Chat Messages -->
<div id="chatMessagesWidget" class="card-body" style="height: 400px; overflow-y: auto; background: #f8f9fa; padding: 15px;">
<div class="text-center text-muted py-10">
<i class="ki-duotone ki-message-text-2 fs-3x mb-3">
<span class="path1"></span>
<span class="path2"></span>
<span class="path3"></span>
</i>
<p>Loading messages...</p>
</div>
</div>
<!-- Chat Input -->
<div class="card-footer" style="padding: 15px; background: white; border-top: 1px solid #e4e6ef;">
<div id="mediaPreview" style="display: none; margin-bottom: 10px; position: relative;">
<div id="previewContainer" style="position: relative; display: inline-block;"></div>
<button type="button" id="removeMediaBtn" class="btn btn-sm btn-icon btn-light" style="position: absolute; top: 5px; right: 5px;">
<i class="ki-duotone ki-cross fs-3">
<span class="path1"></span>
<span class="path2"></span>
</i>
</button>
<div id="uploadProgress" style="display: none; margin-top: 5px;">
<div class="progress" style="height: 20px;">
<div id="uploadProgressBar" class="progress-bar" role="progressbar" style="width: 0%;">0%</div>
</div>
</div>
</div>
<form id="chatFormWidget" class="d-flex align-items-center">
<input type="file" id="mediaInput" accept="image/*,video/*" style="display: none;">
<button type="button" id="attachMediaBtn" class="btn btn-light btn-icon me-2">
<i class="ki-duotone ki-file fs-2">
<span class="path1"></span>
<span class="path2"></span>
</i>
</button>
<input type="text"
class="form-control form-control-solid me-2"
id="messageInputWidget"
placeholder="Type your message..."
autocomplete="off"
style="flex: 1;" />
<button type="submit" class="btn btn-primary btn-icon" id="sendMessageBtn">
<i class="ki-duotone ki-send fs-2">
<span class="path1"></span>
<span class="path2"></span>
</i>
</button>
</form>
</div>
</div>
</div>
</div>
<!-- Media Download Modal -->
<div id="mediaModal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.9); z-index: 10000; align-items: center; justify-content: center;">
<div style="position: relative; max-width: 90%; max-height: 90%;">
<button onclick="closeMediaModal()" style="position: absolute; top: 10px; right: 10px; background: white; border: none; border-radius: 50%; width: 40px; height: 40px; cursor: pointer; z-index: 10001;">
<i class="ki-duotone ki-cross fs-2">
<span class="path1"></span>
<span class="path2"></span>
</i>
</button>
<button id="downloadMediaBtn" style="position: absolute; top: 10px; left: 10px; background: #3699ff; color: white; border: none; border-radius: 8px; padding: 10px 20px; cursor: pointer; z-index: 10001;">
<i class="ki-duotone ki-down fs-2">
<span class="path1"></span>
<span class="path2"></span>
<span class="path3"></span>
</i> Download
</button>
<div id="mediaModalContent" style="text-align: center;"></div>
</div>
</div>
<!-- Notification Toast -->
<div id="notificationToast" style="position: fixed; top: 20px; right: 20px; z-index: 10002; display: none; min-width: 300px;">
<div class="card shadow-lg" style="border-left: 4px solid #3699ff;">
<div class="card-body p-3">
<div class="d-flex align-items-start">
<div class="symbol symbol-40px me-3">
<span class="symbol-label bg-light-primary">
<i class="ki-duotone ki-message-text-2 fs-2 text-primary">
<span class="path1"></span>
<span class="path2"></span>
<span class="path3"></span>
</i>
</span>
</div>
<div class="flex-grow-1">
<div class="fw-bold" id="notifSender"></div>
<div class="text-muted" id="notifMessage"></div>
</div>
<button onclick="closeNotification()" class="btn btn-sm btn-icon ms-2">
<i class="ki-duotone ki-cross fs-3">
<span class="path1"></span>
<span class="path2"></span>
</i>
</button>
</div>
</div>
</div>
</div>
<style>
.online-indicator-widget {
display: inline-block;
width: 8px;
height: 8px;
background: #50cd89;
border-radius: 50%;
margin-right: 5px;
}
.message-bubble-widget {
max-width: 75%;
margin-bottom: 15px;
clear: both;
animation: slideIn 0.3s ease-out;
}
.message-bubble-widget.sent {
float: right;
}
.message-bubble-widget.received {
float: left;
}
.message-content-widget {
padding: 8px 12px;
border-radius: 8px;
word-wrap: break-word;
font-size: 14px;
line-height: 1.4;
position: relative;
}
.message-bubble-widget.sent .message-content-widget {
background: #dcf8c6;
color: #000;
border-bottom-right-radius: 2px;
box-shadow: 0 1px 2px rgba(0,0,0,0.15);
}
.message-bubble-widget.received .message-content-widget {
background: white;
color: #000;
border-bottom-left-radius: 2px;
box-shadow: 0 1px 2px rgba(0,0,0,0.15);
}
.reply-preview-in-message {
background: rgba(0,0,0,0.1);
border-left: 3px solid #3699ff;
padding: 5px 8px;
margin-bottom: 5px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
}
.reply-preview-in-message .reply-author {
font-weight: 600;
color: #3699ff;
margin-bottom: 2px;
}
.reply-preview-in-message .reply-text {
color: #666;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.message-media-widget {
max-width: 100%;
border-radius: 8px;
margin-top: 5px;
position: relative;
}
.message-media-widget img {
max-width: 250px;
max-height: 250px;
border-radius: 8px;
cursor: pointer;
display: block;
}
.message-media-widget video {
max-width: 250px;
max-height: 250px;
border-radius: 8px;
display: block;
}
.message-info-widget {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 4px;
font-size: 11px;
}
.message-time-widget {
color: #667781;
font-size: 11px;
}
.message-sender-widget {
font-size: 12px;
font-weight: 600;
margin-bottom: 3px;
color: #00a884;
}
.message-bubble-widget.sent .message-sender-widget {
color: #05605f;
}
.user-item {
padding: 12px;
border-bottom: 1px solid #e4e6ef;
cursor: pointer;
transition: background 0.2s;
position: relative;
}
.user-item:hover {
background: #f5f8fa;
}
.user-item:last-child {
border-bottom: none;
}
.user-item .unread-count {
background: #f1416c;
color: white;
border-radius: 10px;
padding: 2px 8px;
font-size: 11px;
font-weight: 600;
min-width: 20px;
text-align: center;
}
.user-item .last-message {
font-size: 12px;
color: #a1a5b7;
margin-top: 3px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.user-item .last-message-time {
font-size: 11px;
color: #a1a5b7;
}
.message-bubble-widget:hover .reply-btn {
opacity: 1;
}
.reply-btn {
position: absolute;
top: 5px;
right: 5px;
opacity: 0;
transition: opacity 0.2s;
background: rgba(0,0,0,0.5);
color: white;
border: none;
border-radius: 4px;
padding: 4px 8px;
font-size: 11px;
cursor: pointer;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideInFromRight {
from {
opacity: 0;
transform: translateX(100px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
#notificationToast {
animation: slideInFromRight 0.3s ease-out;
}
#chatButton {
transition: all 0.3s ease;
}
#chatButton:hover {
transform: scale(1.1);
}
#chatWindow {
animation: slideUp 0.3s ease-out;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
#chatMessagesWidget::-webkit-scrollbar {
width: 6px;
}
#chatMessagesWidget::-webkit-scrollbar-track {
background: #f1f1f1;
}
#chatMessagesWidget::-webkit-scrollbar-thumb {
background: #888;
border-radius: 3px;
}
#chatMessagesWidget::-webkit-scrollbar-thumb:hover {
background: #555;
}
#mediaPreview img, #mediaPreview video {
max-width: 100%;
max-height: 150px;
border-radius: 8px;
}
#mediaModal {
display: none;
}
#mediaModal.active {
display: flex !important;
}
#mediaModalContent img {
max-width: 100%;
max-height: 80vh;
border-radius: 8px;
}
#mediaModalContent video {
max-width: 100%;
max-height: 80vh;
border-radius: 8px;
}
</style>
<script src="https://www.gstatic.com/firebasejs/10.7.1/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.7.1/firebase-database-compat.js"></script>
<script>
const firebaseConfig = {
apiKey: "AIzaSyDfycMOFrE5-z-yTyFiNA1cLKCgn6G3hEY",
authDomain: "projects-aa300.firebaseapp.com",
databaseURL: "https://projects-aa300-default-rtdb.firebaseio.com",
projectId: "projects-aa300",
storageBucket: "projects-aa300.appspot.com",
messagingSenderId: "1018429343204",
appId: "1:1018429343204:web:e8801e0726af72662333f1",
measurementId: "G-7Z67HPKRVH"
};
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
const database = firebase.database();
const currentUserId = <?php echo $current_user_id; ?>;
const currentUserName = "<?php echo addslashes($current_user_name); ?>";
const currentUserRole = "<?php echo $current_user_role; ?>";
let currentChatRoom = null;
let currentChatType = null;
let currentRecipientId = null;
let unreadCount = 0;
let isChatOpen = false;
let selectedMediaFile = null;
let selectedMediaBase64 = null;
let messageListeners = {};
let currentMediaUrl = null;
let replyToMessage = null;
let notificationSound = null;
let lastMessagesCache = {};
// ✅ Request notification permission
if ("Notification" in window && Notification.permission === "default") {
Notification.requestPermission();
}
// Create notification sound
notificationSound = new Audio('data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmwhBjeR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmwhBjeR1/LMeSwFJHfH8N2');
document.getElementById('chatButton').addEventListener('click', function() {
const chatWindow = document.getElementById('chatWindow');
if (isChatOpen) {
chatWindow.style.display = 'none';
isChatOpen = false;
} else {
chatWindow.style.display = 'block';
showChatList();
isChatOpen = true;
unreadCount = 0;
updateUnreadBadge();
}
});
document.getElementById('closeChatBtn').addEventListener('click', function() {
document.getElementById('chatWindow').style.display = 'none';
isChatOpen = false;
});
document.getElementById('closeConversationBtn').addEventListener('click', function() {
document.getElementById('chatWindow').style.display = 'none';
isChatOpen = false;
});
function showChatList() {
document.getElementById('chatListView').style.display = 'block';
document.getElementById('conversationView').style.display = 'none';
loadUsersList();
}
function showConversation(chatType, recipientId = null, recipientName = '', recipientRole = '') {
document.getElementById('chatListView').style.display = 'none';
document.getElementById('conversationView').style.display = 'block';
currentChatType = chatType;
currentRecipientId = recipientId;
if (chatType === 'team') {
document.getElementById('conversationTitle').textContent = 'Team Chat';
document.getElementById('conversationSubtitle').innerHTML = '<span class="online-indicator-widget"></span><span id="onlineCountConv">Loading...</span>';
currentChatRoom = 'general';
} else {
document.getElementById('conversationTitle').textContent = recipientName;
document.getElementById('conversationSubtitle').textContent = recipientRole;
currentChatRoom = getChatRoomId(currentUserId, recipientId);
markMessagesAsRead(currentChatRoom);
}
document.getElementById('chatMessagesWidget').innerHTML = `
<div class="text-center text-muted py-10">
<i class="ki-duotone ki-message-text-2 fs-3x mb-3">
<span class="path1"></span>
<span class="path2"></span>
<span class="path3"></span>
</i>
<p>Loading messages...</p>
</div>
`;
loadMessages();
if (chatType === 'team') {
updateOnlineCount();
}
}
document.getElementById('backToListBtn').addEventListener('click', function() {
if (messageListeners[currentChatRoom]) {
database.ref('messages/' + currentChatRoom).off('child_added', messageListeners[currentChatRoom]);
delete messageListeners[currentChatRoom];
}
cancelReply();
showChatList();
});
document.getElementById('openTeamChatBtn').addEventListener('click', function() {
showConversation('team');
});
// ✅ OPTIMIZED: Load users list with cached last messages
function loadUsersList() {
const usersListDiv = document.getElementById('usersList');
usersListDiv.innerHTML = '<div class="text-center text-muted py-10"><p>Loading...</p></div>';
// Listen to last messages updates
database.ref('last_messages').on('value', (snapshot) => {
lastMessagesCache = {};
snapshot.forEach((child) => {
lastMessagesCache[child.key] = child.val();
});
// Now load users
loadUsersWithCachedMessages();
});
}
function loadUsersWithCachedMessages() {
const usersRef = database.ref('users');
usersRef.once('value', (snapshot) => {
const users = [];
snapshot.forEach((childSnapshot) => {
const user = childSnapshot.val();
if (user.userId != currentUserId) {
const roomId = getChatRoomId(currentUserId, user.userId);
const lastMsgData = lastMessagesCache[roomId] || {};
users.push({
...user,
lastMessage: lastMsgData.message || null,
lastMessageTime: lastMsgData.timestamp || 0,
unreadCount: 0
});
}
});
if (users.length === 0) {
document.getElementById('usersList').innerHTML = `
<div class="text-center text-muted py-10">
<i class="ki-duotone ki-profile-user fs-3x mb-3">
<span class="path1"></span>
<span class="path2"></span>
<span class="path3"></span>
<span class="path4"></span>
</i>
<p>No other users yet</p>
</div>
`;
} else {
users.sort((a, b) => b.lastMessageTime - a.lastMessageTime);
displayUsersList(users);
}
});
}
function displayUsersList(users) {
const usersListDiv = document.getElementById('usersList');
usersListDiv.innerHTML = '';
const roleColors = {
'CEO': 'danger',
'Manager': 'warning',
'Employee': 'info'
};
users.forEach(user => {
const userRole = user.userRole || 'Employee';
const unreadBadge = user.unreadCount > 0 ? `<span class="unread-count">${user.unreadCount}</span>` : '';
const lastMessagePreview = user.lastMessage ? `<div class="last-message">${truncate(user.lastMessage, 40)}</div>` : '';
const lastMessageTime = user.lastMessageTime ? `<div class="last-message-time">${formatTime(user.lastMessageTime)}</div>` : '';
const userHtml = `
<div class="user-item" onclick="openDirectMessage(${user.userId}, '${escapeHtml(user.userName)}', '${userRole}')">
<div class="d-flex align-items-start justify-content-between">
<div class="d-flex align-items-start flex-grow-1">
<div class="symbol symbol-40px me-3">
<span class="symbol-label bg-light-primary">
<i class="ki-duotone ki-profile-user fs-2 text-primary">
<span class="path1"></span>
<span class="path2"></span>
<span class="path3"></span>
<span class="path4"></span>
</i>
</span>
</div>
<div class="flex-grow-1">
<div class="d-flex justify-content-between align-items-start">
<div>
<div class="fw-bold">${escapeHtml(user.userName)}</div>
<span class="badge badge-light-${roleColors[userRole]} badge-sm">${userRole}</span>
</div>
<div class="text-end">
${lastMessageTime}
${unreadBadge}
</div>
</div>
${lastMessagePreview}
</div>
</div>
</div>
</div>
`;
usersListDiv.insertAdjacentHTML('beforeend', userHtml);
});
}
function truncate(str, len) {
return str.length > len ? str.substring(0, len) + '...' : str;
}
window.openDirectMessage = function(userId, userName, userRole) {
showConversation('direct', userId, userName, userRole);
};
function getChatRoomId(userId1, userId2) {
return userId1 < userId2 ? `dm_${userId1}_${userId2}` : `dm_${userId2}_${userId1}`;
}
function markMessagesAsRead(roomId) {
// Implementation can be added here
}
function loadMessages() {
const messagesRef = database.ref('messages/' + currentChatRoom);
if (messageListeners[currentChatRoom]) {
messagesRef.off('child_added', messageListeners[currentChatRoom]);
}
const listener = (snapshot) => {
const msg = snapshot.val();
msg.id = snapshot.key;
displayMessage(msg);
scrollToBottom();
// ✅ Show notification if message from someone else
if (msg.userId !== currentUserId && (!isChatOpen || currentChatRoom !== getChatRoomIdFromMessage(msg))) {
showNotification(msg.userName, msg.message || '📎 Media');
}
};
messageListeners[currentChatRoom] = listener;
messagesRef.on('child_added', listener);
messagesRef.once('value', (snapshot) => {
const messagesDiv = document.getElementById('chatMessagesWidget');
if (snapshot.numChildren() === 0) {
messagesDiv.innerHTML = `
<div class="text-center text-muted py-10">
<i class="ki-duotone ki-message-text-2 fs-3x mb-3">
<span class="path1"></span>
<span class="path2"></span>
<span class="path3"></span>
</i>
<p>No messages yet. Start the conversation!</p>
</div>`;
}
});
}
function getChatRoomIdFromMessage(msg) {
// Determine which chat room the message belongs to
return currentChatRoom;
}
// ✅ Show notification
function showNotification(senderName, message) {
const toast = document.getElementById('notificationToast');
document.getElementById('notifSender').textContent = senderName;
document.getElementById('notifMessage').textContent = truncate(message, 50);
toast.style.display = 'block';
// Play sound
if (notificationSound) {
notificationSound.play().catch(e => console.log('Sound play failed:', e));
}
// Browser notification
if ("Notification" in window && Notification.permission === "granted") {
new Notification(senderName, {
body: message,
icon: '/path/to/icon.png'
});
}
// Auto-hide after 5 seconds
setTimeout(() => {
toast.style.display = 'none';
}, 5000);
// Update unread badge
unreadCount++;
updateUnreadBadge();
}
window.closeNotification = function() {
document.getElementById('notificationToast').style.display = 'none';
};
function displayMessage(msg) {
const messagesDiv = document.getElementById('chatMessagesWidget');
const placeholder = messagesDiv.querySelector('.text-center');
if (placeholder) {
placeholder.remove();
}
const isSent = msg.userId == currentUserId;
let replyHtml = '';
if (msg.replyTo) {
replyHtml = `
<div class="reply-preview-in-message" onclick="scrollToMessage('${msg.replyTo.messageId}')">
<div class="reply-author">${escapeHtml(msg.replyTo.userName)}</div>
<div class="reply-text">${escapeHtml(msg.replyTo.message || '📎 Media')}</div>
</div>
`;
}
let mediaHtml = '';
if (msg.mediaData && msg.mediaType) {
const mediaId = 'media_' + msg.id;
if (msg.mediaType === 'image') {
mediaHtml = `<div class="message-media-widget"><img id="${mediaId}" src="${msg.mediaData}" alt="Image" onclick="openMediaModal('${mediaId}', '${msg.mediaType}')"></div>`;
} else if (msg.mediaType === 'video') {
mediaHtml = `<div class="message-media-widget"><video id="${mediaId}" controls><source src="${msg.mediaData}"></video></div>`;
}
}
let senderHtml = '';
if (!isSent && currentChatType === 'team') {
senderHtml = `<div class="message-sender-widget">${escapeHtml(msg.userName)}</div>`;
}
const messageHtml = `
<div class="message-bubble-widget ${isSent ? 'sent' : 'received'}" id="msg_${msg.id}">
${senderHtml}
<div class="message-content-widget">
<button class="reply-btn" onclick="replyToMsg('${msg.id}', '${escapeHtml(msg.userName)}', '${escapeHtml(msg.message || 'Media')}')">Reply</button>
${replyHtml}
${msg.message ? `<div style="margin-bottom: ${msg.mediaData ? '5px' : '0'};">${escapeHtml(msg.message)}</div>` : ''}
${mediaHtml}
<div class="message-info-widget">
<span class="message-time-widget">${formatTime(msg.timestamp)}</span>
${isSent ? '<span>✓✓</span>' : ''}
</div>
</div>
</div>
`;
messagesDiv.insertAdjacentHTML('beforeend', messageHtml);
}
window.replyToMsg = function(messageId, userName, messageText) {
replyToMessage = {
messageId: messageId,
userName: userName,
message: messageText
};
document.getElementById('replyToName').textContent = userName;
document.getElementById('replyToMessage').textContent = messageText.substring(0, 50);
document.getElementById('replyPreview').style.display = 'block';
document.getElementById('messageInputWidget').focus();
};
window.cancelReply = function() {
replyToMessage = null;
document.getElementById('replyPreview').style.display = 'none';
};
window.scrollToMessage = function(messageId) {
const messageElement = document.getElementById('msg_' + messageId);
if (messageElement) {
messageElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
messageElement.style.backgroundColor = '#fff3cd';
setTimeout(() => {
messageElement.style.backgroundColor = '';
}, 2000);
}
};
window.openMediaModal = function(mediaId, mediaType) {
const mediaElement = document.getElementById(mediaId);
const modal = document.getElementById('mediaModal');
const modalContent = document.getElementById('mediaModalContent');
if (mediaType === 'image') {
currentMediaUrl = mediaElement.src;
modalContent.innerHTML = `<img src="${currentMediaUrl}" alt="Full Image">`;
} else if (mediaType === 'video') {
currentMediaUrl = mediaElement.querySelector('source').src;
modalContent.innerHTML = `<video controls autoplay><source src="${currentMediaUrl}"></video>`;
}
modal.classList.add('active');
};
window.closeMediaModal = function() {
document.getElementById('mediaModal').classList.remove('active');
currentMediaUrl = null;
};
document.getElementById('downloadMediaBtn').addEventListener('click', function() {
if (currentMediaUrl) {
const link = document.createElement('a');
link.href = currentMediaUrl;
link.download = 'download_' + Date.now();
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
});
document.getElementById('mediaModal').addEventListener('click', function(e) {
if (e.target.id === 'mediaModal') {
closeMediaModal();
}
});
document.getElementById('attachMediaBtn').addEventListener('click', function() {
document.getElementById('mediaInput').click();
});
document.getElementById('mediaInput').addEventListener('change', function(e) {
const file = e.target.files[0];
if (file) {
const maxSize = 200 * 1024 * 1024;
if (file.size > maxSize) {
alert('File too large. Maximum size is 200MB');
this.value = '';
return;
}
selectedMediaFile = file;
convertToBase64WithProgress(file);
}
});
document.getElementById('removeMediaBtn').addEventListener('click', function() {
selectedMediaFile = null;
selectedMediaBase64 = null;
document.getElementById('mediaPreview').style.display = 'none';
document.getElementById('mediaInput').value = '';
});
function convertToBase64WithProgress(file) {
const reader = new FileReader();
document.getElementById('uploadProgress').style.display = 'block';
reader.onprogress = function(e) {
if (e.lengthComputable) {
const percentLoaded = Math.round((e.loaded / e.total) * 100);
const progressBar = document.getElementById('uploadProgressBar');
progressBar.style.width = percentLoaded + '%';
progressBar.textContent = percentLoaded + '%';
}
};
reader.onload = function(e) {
selectedMediaBase64 = e.target.result;
showMediaPreview(e.target.result, file.type);
document.getElementById('uploadProgress').style.display = 'none';
};
reader.readAsDataURL(file);
}
function showMediaPreview(base64, fileType) {
const previewContainer = document.getElementById('previewContainer');
const mediaPreview = document.getElementById('mediaPreview');
if (fileType.startsWith('image/')) {
previewContainer.innerHTML = `<img src="${base64}" alt="Preview">`;
} else if (fileType.startsWith('video/')) {
previewContainer.innerHTML = `<video controls><source src="${base64}"></video>`;
}
mediaPreview.style.display = 'block';
}
document.getElementById('chatFormWidget').addEventListener('submit', async function(e) {
e.preventDefault();
const messageInput = document.getElementById('messageInputWidget');
const message = messageInput.value.trim();
const sendBtn = document.getElementById('sendMessageBtn');
if (!message && !selectedMediaBase64) return;
sendBtn.disabled = true;
sendBtn.innerHTML = '<span class="spinner-border spinner-border-sm"></span>';
const messagesRef = database.ref('messages/' + currentChatRoom);
const newMessageRef = messagesRef.push();
const messageData = {
userId: currentUserId,
userName: currentUserName,
userRole: currentUserRole,
message: message,
timestamp: firebase.database.ServerValue.TIMESTAMP
};
if (replyToMessage) {
messageData.replyTo = replyToMessage;
}
if (selectedMediaBase64) {
messageData.mediaData = selectedMediaBase64;
messageData.mediaType = selectedMediaFile.type.startsWith('image/') ? 'image' : 'video';
}
try {
await newMessageRef.set(messageData);
// ✅ Update last message cache
database.ref('last_messages/' + currentChatRoom).set({
message: message || (messageData.mediaType === 'image' ? '📷 Photo' : '🎥 Video'),
timestamp: firebase.database.ServerValue.TIMESTAMP,
userId: currentUserId
});
messageInput.value = '';
selectedMediaFile = null;
selectedMediaBase64 = null;
document.getElementById('mediaPreview').style.display = 'none';
document.getElementById('mediaInput').value = '';
cancelReply();
} catch (error) {
alert('Failed to send message: ' + error.message);
}
sendBtn.disabled = false;
sendBtn.innerHTML = '<i class="ki-duotone ki-send fs-2"><span class="path1"></span><span class="path2"></span></i>';
});
function updateOnlineCount() {
const presenceRef = database.ref('presence/general/' + currentUserId);
presenceRef.set({
userId: currentUserId,
userName: currentUserName,
userRole: currentUserRole,
timestamp: firebase.database.ServerValue.TIMESTAMP
});
presenceRef.onDisconnect().remove();
database.ref('presence/general').on('value', (snapshot) => {
const count = snapshot.numChildren();
const elem = document.getElementById('onlineCountConv');
if (elem) {
elem.textContent = count + ' online';
}
const widgetElem = document.getElementById('onlineCountWidget');
if (widgetElem) {
widgetElem.textContent = count + ' online';
}
});
setInterval(() => {
presenceRef.set({
userId: currentUserId,
userName: currentUserName,
userRole: currentUserRole,
timestamp: firebase.database.ServerValue.TIMESTAMP
});
}, 10000);
}
function updateUnreadBadge() {
const badge = document.getElementById('unreadBadge');
if (unreadCount > 0) {
badge.textContent = unreadCount > 9 ? '9+' : unreadCount;
badge.style.display = 'flex';
badge.style.alignItems = 'center';
badge.style.justifyContent = 'center';
} else {
badge.style.display = 'none';
}
}
function scrollToBottom() {
const messagesDiv = document.getElementById('chatMessagesWidget');
setTimeout(() => {
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}, 100);
}
function formatTime(timestamp) {
if (!timestamp) return 'Just now';
const date = new Date(timestamp);
const now = new Date();
const diffMs = now - date;
const diffMins = Math.floor(diffMs / 60000);
if (diffMins < 1) return 'Just now';
if (diffMins < 60) return diffMins + 'm';
if (diffMins < 1440) return Math.floor(diffMins / 60) + 'h';
const today = new Date();
today.setHours(0, 0, 0, 0);
const yesterday = new Date(today);
yesterday.setDate(yesterday.getDate() - 1);
if (date >= today) {
return date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
} else if (date >= yesterday) {
return 'Yesterday';
} else {
return date.toLocaleDateString([], {month: 'short', day: 'numeric'});
}
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function registerUser() {
const userRef = database.ref('users/' + currentUserId);
userRef.set({
userId: currentUserId,
userName: currentUserName,
userRole: currentUserRole,
lastSeen: firebase.database.ServerValue.TIMESTAMP
});
}
registerUser();
updateOnlineCount();
</script>