Initial plugin commit
This commit is contained in:
262
assets/js/chat.js
Normal file
262
assets/js/chat.js
Normal file
@@ -0,0 +1,262 @@
|
||||
(function () {
|
||||
function escapeHtml(value) {
|
||||
return String(value || '')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/\"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function renderMessage(message, currentUserId) {
|
||||
var ownClass = Number(message.user_id) === Number(currentUserId) ? ' kgvvm-chat-message--own' : '';
|
||||
var roleLabel = message.role ? ' · ' + escapeHtml(message.role) : '';
|
||||
var text = escapeHtml(message.message).replace(/\n/g, '<br>');
|
||||
|
||||
return '' +
|
||||
'<article class="kgvvm-chat-message' + ownClass + '" data-id="' + escapeHtml(message.id) + '">' +
|
||||
'<div class="kgvvm-chat-message__meta">' +
|
||||
'<strong>' + escapeHtml(message.user) + '</strong>' + roleLabel + ' · ' + escapeHtml(message.time) +
|
||||
'</div>' +
|
||||
'<div class="kgvvm-chat-message__body">' + text + '</div>' +
|
||||
'</article>';
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var app = document.querySelector('.kgvvm-chat-app');
|
||||
|
||||
if (!app || typeof window.kgvvmChatConfig === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
var config = window.kgvvmChatConfig;
|
||||
var messagesEl = app.querySelector('[data-chat-messages]');
|
||||
var form = app.querySelector('[data-chat-form]');
|
||||
var input = app.querySelector('[data-chat-input]');
|
||||
var status = app.querySelector('[data-chat-status]');
|
||||
var roomButtons = app.querySelectorAll('[data-chat-room]');
|
||||
var roomTitle = app.querySelector('[data-chat-room-title]');
|
||||
var roomDescription = app.querySelector('[data-chat-room-description]');
|
||||
var currentRoom = app.getAttribute('data-room') || 'general';
|
||||
var initialRoom = currentRoom;
|
||||
var currentUserId = Number(app.getAttribute('data-current-user-id') || '0');
|
||||
var sendLocked = false;
|
||||
var storageKey = 'kgvvmActiveChatRoom';
|
||||
|
||||
function getAvailableRooms() {
|
||||
return Array.prototype.map.call(roomButtons, function (button) {
|
||||
return button.getAttribute('data-chat-room') || '';
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
function syncRoomState() {
|
||||
try {
|
||||
window.localStorage.setItem(storageKey, currentRoom);
|
||||
} catch (error) {
|
||||
// ignore storage errors
|
||||
}
|
||||
|
||||
if (window.history && typeof window.history.replaceState === 'function') {
|
||||
var url = new window.URL(window.location.href);
|
||||
url.searchParams.set('room', currentRoom);
|
||||
window.history.replaceState({}, '', url.toString());
|
||||
}
|
||||
}
|
||||
|
||||
function restoreRoomState() {
|
||||
var availableRooms = getAvailableRooms();
|
||||
var url = new window.URL(window.location.href);
|
||||
var requestedRoom = url.searchParams.get('room') || '';
|
||||
var storedRoom = '';
|
||||
|
||||
try {
|
||||
storedRoom = window.localStorage.getItem(storageKey) || '';
|
||||
} catch (error) {
|
||||
storedRoom = '';
|
||||
}
|
||||
|
||||
var preferredRoom = requestedRoom || storedRoom;
|
||||
|
||||
if (preferredRoom && availableRooms.indexOf(preferredRoom) !== -1) {
|
||||
currentRoom = preferredRoom;
|
||||
app.setAttribute('data-room', currentRoom);
|
||||
}
|
||||
}
|
||||
|
||||
function setStatus(text, isError) {
|
||||
if (!status) {
|
||||
return;
|
||||
}
|
||||
|
||||
status.textContent = text || '';
|
||||
status.classList.toggle('is-error', !!isError);
|
||||
}
|
||||
|
||||
function scrollToBottom() {
|
||||
if (messagesEl) {
|
||||
messagesEl.scrollTop = messagesEl.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
function updateRoomButtons() {
|
||||
roomButtons.forEach(function (button) {
|
||||
button.classList.toggle('is-active', button.getAttribute('data-chat-room') === currentRoom);
|
||||
});
|
||||
}
|
||||
|
||||
function updateRoomMeta() {
|
||||
roomButtons.forEach(function (button) {
|
||||
if (button.getAttribute('data-chat-room') !== currentRoom) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (roomTitle) {
|
||||
roomTitle.textContent = button.getAttribute('data-chat-label') || '';
|
||||
}
|
||||
|
||||
if (roomDescription) {
|
||||
roomDescription.textContent = button.getAttribute('data-chat-description') || '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderMessages(messages, replaceAll) {
|
||||
if (!messagesEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (replaceAll) {
|
||||
messagesEl.innerHTML = '';
|
||||
}
|
||||
|
||||
if (!Array.isArray(messages) || !messages.length) {
|
||||
if (replaceAll) {
|
||||
messagesEl.innerHTML = '<p class="kgvvm-chat-empty">' + escapeHtml(config.i18n.empty) + '</p>';
|
||||
messagesEl.dataset.lastId = '0';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (messagesEl.querySelector('.kgvvm-chat-empty')) {
|
||||
messagesEl.innerHTML = '';
|
||||
}
|
||||
|
||||
messages.forEach(function (message) {
|
||||
messagesEl.insertAdjacentHTML('beforeend', renderMessage(message, currentUserId));
|
||||
messagesEl.dataset.lastId = String(message.id || messagesEl.dataset.lastId || '0');
|
||||
});
|
||||
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
function fetchMessages(replaceAll) {
|
||||
var data = new window.FormData();
|
||||
data.append('action', 'kgvvm_fetch_chat_messages');
|
||||
data.append('nonce', config.nonce);
|
||||
data.append('room', currentRoom);
|
||||
|
||||
if (!replaceAll && messagesEl && messagesEl.dataset.lastId) {
|
||||
data.append('after_id', messagesEl.dataset.lastId);
|
||||
}
|
||||
|
||||
return window.fetch(config.ajaxUrl, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: data
|
||||
})
|
||||
.then(function (response) { return response.json(); })
|
||||
.then(function (payload) {
|
||||
if (!payload || !payload.success) {
|
||||
return;
|
||||
}
|
||||
|
||||
renderMessages(payload.data.messages || [], replaceAll);
|
||||
})
|
||||
.catch(function () {
|
||||
setStatus(config.i18n.fetchError, true);
|
||||
});
|
||||
}
|
||||
|
||||
roomButtons.forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
currentRoom = button.getAttribute('data-chat-room') || 'general';
|
||||
app.setAttribute('data-room', currentRoom);
|
||||
syncRoomState();
|
||||
updateRoomMeta();
|
||||
|
||||
if (messagesEl) {
|
||||
messagesEl.dataset.lastId = '0';
|
||||
}
|
||||
|
||||
updateRoomButtons();
|
||||
setStatus(config.i18n.loading, false);
|
||||
fetchMessages(true).then(function () {
|
||||
setStatus('', false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (form && input) {
|
||||
form.addEventListener('submit', function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (sendLocked) {
|
||||
return;
|
||||
}
|
||||
|
||||
var message = String(input.value || '').trim();
|
||||
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendLocked = true;
|
||||
setStatus(config.i18n.sending, false);
|
||||
|
||||
var data = new window.FormData();
|
||||
data.append('action', 'kgvvm_send_chat_message');
|
||||
data.append('nonce', config.nonce);
|
||||
data.append('room', currentRoom);
|
||||
data.append('message', message);
|
||||
|
||||
window.fetch(config.ajaxUrl, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: data
|
||||
})
|
||||
.then(function (response) { return response.json(); })
|
||||
.then(function (payload) {
|
||||
sendLocked = false;
|
||||
|
||||
if (!payload || !payload.success) {
|
||||
setStatus(config.i18n.sendError, true);
|
||||
return;
|
||||
}
|
||||
|
||||
renderMessages(payload.data.messages || [], false);
|
||||
input.value = '';
|
||||
input.focus();
|
||||
setStatus('', false);
|
||||
})
|
||||
.catch(function () {
|
||||
sendLocked = false;
|
||||
setStatus(config.i18n.sendError, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
restoreRoomState();
|
||||
updateRoomButtons();
|
||||
updateRoomMeta();
|
||||
syncRoomState();
|
||||
|
||||
if (currentRoom !== initialRoom) {
|
||||
fetchMessages(true);
|
||||
}
|
||||
|
||||
scrollToBottom();
|
||||
window.setInterval(function () {
|
||||
fetchMessages(false);
|
||||
}, Number(config.refreshInterval || 7000));
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user