File "threads.php"
Full Path: /home/romayxjt/public_html/wp-content/plugins/vikbooking/admin/layouts/chat/threads.php
File size: 12.15 KB
MIME-type: text/x-php
Charset: utf-8
<?php
/**
* @package VikBooking
* @subpackage com_vikbooking
* @author Alessio Gaggii - E4J srl
* @copyright Copyright (C) 2025 E4J srl. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
* @link https://vikwp.com
*/
defined('ABSPATH') or die('No script kiddies please!');
/**
* Display data attributes.
*
* @var array $threads
* @var array $options
* @var string $id
*/
extract($displayData);
$id = 'vbo-chat-interface-' . ($options['id'] ?? uniqid());
?>
<div class="vbo-chat-interface<?php echo ($options['compact'] ?? false) ? ' compact' : ''; ?>" id="<?php echo $id; ?>">
<div class="vbo-chat-threads">
<?php
foreach ($threads as $thread) {
echo $this->sublayout('thread', [
'thread' => $thread,
'options' => $options ?? [],
]);
}
?>
</div>
<div class="vbo-chat-target">
<?php
echo JLayoutHelper::render('chat.blank', [
'title' => '',
'subtitle' => JText::translate('VBO_CHAT_CONV_EMPTY_SUBTITLE'),
]);
?>
</div>
<a href="javascript:void(0)" class="vbo-chat-back"><?php VikBookingIcons::e('chevron-left'); ?> <?php echo JText::translate('VBBACK'); ?></a>
</div>
<script>
(function($) {
'use strict';
const rearrangeThreads = () => {
const threads = $('#<?php echo $id; ?>').find('.vbo-chat-threads').children().sort((a, b) => {
// get values to compare
const x = $(a).attr('data-date');
const y = $(b).attr('data-date');
// equal by default
let delta = 0;
if (x < y) {
// A is lower than B
delta = 1;
} else if (x > y) {
// A is highet than B
delta = -1;
}
return delta;
});
$('#<?php echo $id; ?>').find('.vbo-chat-threads').html(threads);
}
const refreshThread = (chat) => {
const context = chat.data.environment.context;
const thread = $('#<?php echo $id; ?>').find('.chat-thread[data-context="' + context.alias + '"][data-id="' + context.id + '"]');
if (!thread.length) {
return;
}
const lastMessage = chat.getLatestMessage();
// obtain the details of the user that wrote the last message
const user = chat.getMessageUser(lastMessage);
// refresh avatar
thread.find('.chat-thread-avatar').html(chat.drawUserAvatar(user));
// refresh user name
thread.find('.message-author').text(user.name);
// refresh date
const date = DateHelper.stringToDate(lastMessage.createdon);
thread.attr('data-date', lastMessage.createdon);
thread.find('.last-update-time').text(date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }));
if (DateHelper.isToday(date)) {
thread.find('.last-update-date').text(Joomla.JText._('VBTODAY'));
} else if (DateHelper.isYesterday(date)) {
thread.find('.last-update-date').text(Joomla.JText._('VBOYESTERDAY'));
} else {
thread.find('.last-update-date').text(date.toLocaleDateString());
}
// refresh message
thread.find('.chat-thread-message-body').text(shortenText(lastMessage.message, 80)).attr('data-length', lastMessage.message.length);
// refresh attachments
const attachments = lastMessage.attachments.map(a => a.name).join(', ');
thread.find('.chat-thread-message-attachments').attr('data-length', attachments.length).find('span').text(shortenText(attachments, 80));
rearrangeThreads();
}
const shortenText = (text, max) => {
// Check whether we should take a substring of the text.
// Reserve an additional 25% of characters to avoid breaking the
// text too close to the end of the string.
if (max && text.length > max * 1.25) {
// explode the string in words
let chunks = text.split(' ');
text = '';
// keep adding words until we reach the maximum threshold
while (chunks && text.length < max) {
text += chunks.shift() + ' ';
}
// get rid of trailing special characters and add the ellipsis
text = text.replace(/[.,?!;:#'"([{ ]+$/, '') + '...';
}
return text;
}
$(document).on('click', '#<?php echo $id; ?> .chat-thread[data-context][data-id]', function() {
if ($(this).hasClass('active')) {
return false;
}
// always destroy and previously open chat
VBOChat.getInstance().destroy();
$('#<?php echo $id; ?>').find('.chat-thread[data-context][data-id].active').removeClass('active');
$(this).addClass('active');
// append loading box to the chat target
$('#<?php echo $id; ?>').find('.vbo-chat-target').append(
$('<div class="vbo-chat-loading"><?php VikBookingIcons::e('circle-notch', 'fa-spin fa-3x'); ?></div>')
).addClass('slide-in');
VBOChatAjax.do(
'<?php echo VBOFactory::getPlatform()->getUri()->ajax('index.php?option=com_vikbooking&task=chat.render_chat'); ?>',
{
context: $(this).data('context'),
id_context: $(this).data('id'),
},
(resp) => {
$('#<?php echo $id; ?>').find('.vbo-chat-target').html(resp.html);
},
(err) => {
alert(err.responseText);
}
);
});
window.addEventListener('chat.sync', (event) => {
refreshThread(event.detail.chat);
});
window.addEventListener('chat.send', (event) => {
refreshThread(event.detail.chat);
});
window.addEventListener('chat.read', (event) => {
const context = event.detail.chat.data.environment.context;
const thread = $('#<?php echo $id; ?>').find('.chat-thread[data-context="' + context.alias + '"][data-id="' + context.id + '"]');
if (!thread.length) {
return;
}
// mark thread as read
thread.attr('data-read', 1);
});
let isLoadingOlderThreads = false;
let totalThreads = <?php echo count($threads); ?>;
let threadsLimit = <?php echo $options['limit'] ?? 20; ?>;
const createLoadingSkeleton = (count) => {
let skeleton = '';
for (let i = 1; i <= count; i++) {
skeleton += '<div class="vbo-dashboard-guest-activity vbo-dashboard-guest-activity-skeleton chat-thread">';
skeleton += ' <div class="vbo-dashboard-guest-activity-avatar">';
skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-avatar"></div>';
skeleton += ' </div>';
skeleton += ' <div class="vbo-dashboard-guest-activity-content chat-thread-content">';
skeleton += ' <div class="vbo-dashboard-guest-activity-content-head">';
skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-title"></div>';
skeleton += ' </div>';
skeleton += ' <div class="vbo-dashboard-guest-activity-content-subhead">';
skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-subtitle"></div>';
skeleton += ' </div>';
skeleton += ' <div class="vbo-dashboard-guest-activity-content-info-msg">';
skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-content"></div>';
skeleton += ' </div>';
skeleton += ' </div>';
skeleton += '</div>';
}
return skeleton;
}
const loadPreviousThreads = () => {
if (isLoadingOlderThreads) {
// do not proceed in case we are already loading something
return this;
}
// mark loading flag
isLoadingOlderThreads = true;
const threadsList = $('#<?php echo $id; ?>').find('.vbo-chat-threads');
threadsList.append(createLoadingSkeleton(5));
// make AJAX request to load older threads
VBOChatAjax.do(
// end-point URL
'<?php echo VBOFactory::getPlatform()->getUri()->ajax('index.php?option=com_vikbooking&task=chat.load_chats'); ?>',
// POST data
{
start: totalThreads,
limit: threadsLimit,
options: <?php echo json_encode($options ?? []); ?>,
},
// success callback
(threads) => {
// keep current scroll
let currentScrollTop = threadsList[0].scrollTop;
let currentScrollHeight = threadsList[0].scrollHeight;
// remove loading skeleton
threadsList.find('.vbo-dashboard-guest-activity-skeleton').remove();
threads.forEach((thread) => {
const existing = $('#<?php echo $id; ?>').find('.chat-thread[data-context="' + thread.alias + '"][data-id="' + thread.id + '"]');
if (!existing.length) {
// add thread only in case it is not already in the list
threadsList.append(thread.html);
}
});
// update count of loaded threads
totalThreads += threads.length;
// turn off scroll event in case we reached the limit
if (threads.length < threadsLimit) {
threadsList.off('scroll');
}
// make loading available again
isLoadingOlderThreads = false;
},
// failure callback
(error) => {
// remove loading skeleton
threadsList.find('.vbo-dashboard-guest-activity-skeleton').remove();
// make loading available again
isLoadingOlderThreads = false;
}
);
}
$(function() {
// do not register scroll event in case the number of messages is equal or
// higher then the total number of messages under this context
if (totalThreads >= threadsLimit) {
// setup scroll event to load older messages
$('#<?php echo $id; ?>').find('.vbo-chat-threads').on('scroll', function() {
if (isLoadingOlderThreads) {
// ignore if we are currently loading older messages
return;
}
// get scrollable pixel
const scrollHeight = this.scrollHeight - $(this).outerHeight();
// get scroll top
const scrollTop = this.scrollTop;
// start loading older threads only when scrollbar
// is 300px close to the end
if (scrollHeight - scrollTop <= 300) {
loadPreviousThreads();
}
});
}
$('#<?php echo $id; ?>').find('.vbo-chat-back').on('click', function() {
$(this).hide();
$('#<?php echo $id; ?>').find('.chat-thread[data-context][data-id].active').removeClass('active');
$(this).prev().removeClass('slide-in');
setTimeout(() => {
$(this).show();
}, 300);
});
});
})(jQuery);
</script>