PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.6
VikBooking Hotel Booking Engine & PMS v1.8.6
1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 All 36 releases
vikbooking / admin / layouts / chat / threads.php

threads.php in VikBooking Hotel Booking Engine & PMS 1.8.6, at admin/layouts/chat/threads.php

321 lines 12.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikBooking
4 * @subpackage com_vikbooking
5 * @author Alessio Gaggii - E4J srl
6 * @copyright Copyright (C) 2025 E4J srl. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 * @link https://vikwp.com
9 */
10
11 defined('ABSPATH') or die('No script kiddies please!');
12
13 /**
14 * Display data attributes.
15 *
16 * @var array $threads
17 * @var array $options
18 * @var string $id
19 */
20 extract($displayData);
21
22 $id = 'vbo-chat-interface-' . ($options['id'] ?? uniqid());
23
24 ?>
25
26 <div class="vbo-chat-interface<?php echo ($options['compact'] ?? false) ? ' compact' : ''; ?>" id="<?php echo $id; ?>">
27
28 <div class="vbo-chat-threads">
29
30 <?php
31 foreach ($threads as $thread) {
32 echo $this->sublayout('thread', [
33 'thread' => $thread,
34 'options' => $options ?? [],
35 ]);
36 }
37 ?>
38
39 </div>
40
41 <div class="vbo-chat-target">
42 <?php
43 echo JLayoutHelper::render('chat.blank', [
44 'title' => '',
45 'subtitle' => JText::translate('VBO_CHAT_CONV_EMPTY_SUBTITLE'),
46 ]);
47 ?>
48 </div>
49
50 <a href="javascript:void(0)" class="vbo-chat-back"><?php VikBookingIcons::e('chevron-left'); ?>&nbsp;<?php echo JText::translate('VBBACK'); ?></a>
51
52 </div>
53
54 <script>
55 (function($) {
56 'use strict';
57
58 const rearrangeThreads = () => {
59 const threads = $('#<?php echo $id; ?>').find('.vbo-chat-threads').children().sort((a, b) => {
60 // get values to compare
61 const x = $(a).attr('data-date');
62 const y = $(b).attr('data-date');
63
64 // equal by default
65 let delta = 0;
66
67 if (x < y) {
68 // A is lower than B
69 delta = 1;
70 } else if (x > y) {
71 // A is highet than B
72 delta = -1;
73 }
74
75 return delta;
76 });
77
78 $('#<?php echo $id; ?>').find('.vbo-chat-threads').html(threads);
79 }
80
81 const refreshThread = (chat) => {
82 const context = chat.data.environment.context;
83 const thread = $('#<?php echo $id; ?>').find('.chat-thread[data-context="' + context.alias + '"][data-id="' + context.id + '"]');
84
85 if (!thread.length) {
86 return;
87 }
88
89 const lastMessage = chat.getLatestMessage();
90
91 // obtain the details of the user that wrote the last message
92 const user = chat.getMessageUser(lastMessage);
93
94 // refresh avatar
95 thread.find('.chat-thread-avatar').html(chat.drawUserAvatar(user));
96
97 // refresh user name
98 thread.find('.message-author').text(user.name);
99
100 // refresh date
101 const date = DateHelper.stringToDate(lastMessage.createdon);
102 thread.attr('data-date', lastMessage.createdon);
103 thread.find('.last-update-time').text(date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }));
104
105 if (DateHelper.isToday(date)) {
106 thread.find('.last-update-date').text(Joomla.JText._('VBTODAY'));
107 } else if (DateHelper.isYesterday(date)) {
108 thread.find('.last-update-date').text(Joomla.JText._('VBOYESTERDAY'));
109 } else {
110 thread.find('.last-update-date').text(date.toLocaleDateString());
111 }
112
113 // refresh message
114 thread.find('.chat-thread-message-body').text(shortenText(lastMessage.message, 80)).attr('data-length', lastMessage.message.length);
115
116 // refresh attachments
117 const attachments = lastMessage.attachments.map(a => a.name).join(', ');
118 thread.find('.chat-thread-message-attachments').attr('data-length', attachments.length).find('span').text(shortenText(attachments, 80));
119
120 rearrangeThreads();
121 }
122
123 const shortenText = (text, max) => {
124 // Check whether we should take a substring of the text.
125 // Reserve an additional 25% of characters to avoid breaking the
126 // text too close to the end of the string.
127 if (max && text.length > max * 1.25) {
128 // explode the string in words
129 let chunks = text.split(' ');
130
131 text = '';
132
133 // keep adding words until we reach the maximum threshold
134 while (chunks && text.length < max) {
135 text += chunks.shift() + ' ';
136 }
137
138 // get rid of trailing special characters and add the ellipsis
139 text = text.replace(/[.,?!;:#'"([{ ]+$/, '') + '...';
140 }
141
142 return text;
143 }
144
145 $(document).on('click', '#<?php echo $id; ?> .chat-thread[data-context][data-id]', function() {
146 if ($(this).hasClass('active')) {
147 return false;
148 }
149
150 // always destroy and previously open chat
151 VBOChat.getInstance().destroy();
152
153 $('#<?php echo $id; ?>').find('.chat-thread[data-context][data-id].active').removeClass('active');
154 $(this).addClass('active');
155
156 // append loading box to the chat target
157 $('#<?php echo $id; ?>').find('.vbo-chat-target').append(
158 $('<div class="vbo-chat-loading"><?php VikBookingIcons::e('circle-notch', 'fa-spin fa-3x'); ?></div>')
159 ).addClass('slide-in');
160
161 VBOChatAjax.do(
162 '<?php echo VBOFactory::getPlatform()->getUri()->ajax('index.php?option=com_vikbooking&task=chat.render_chat'); ?>',
163 {
164 context: $(this).data('context'),
165 id_context: $(this).data('id'),
166 },
167 (resp) => {
168 $('#<?php echo $id; ?>').find('.vbo-chat-target').html(resp.html);
169 },
170 (err) => {
171 alert(err.responseText);
172 }
173 );
174 });
175
176 window.addEventListener('chat.sync', (event) => {
177 refreshThread(event.detail.chat);
178 });
179
180 window.addEventListener('chat.send', (event) => {
181 refreshThread(event.detail.chat);
182 });
183
184 window.addEventListener('chat.read', (event) => {
185 const context = event.detail.chat.data.environment.context;
186 const thread = $('#<?php echo $id; ?>').find('.chat-thread[data-context="' + context.alias + '"][data-id="' + context.id + '"]');
187
188 if (!thread.length) {
189 return;
190 }
191
192 // mark thread as read
193 thread.attr('data-read', 1);
194 });
195
196 let isLoadingOlderThreads = false;
197 let totalThreads = <?php echo count($threads); ?>;
198 let threadsLimit = <?php echo $options['limit'] ?? 20; ?>;
199
200 const createLoadingSkeleton = (count) => {
201 let skeleton = '';
202
203 for (let i = 1; i <= count; i++) {
204 skeleton += '<div class="vbo-dashboard-guest-activity vbo-dashboard-guest-activity-skeleton chat-thread">';
205 skeleton += ' <div class="vbo-dashboard-guest-activity-avatar">';
206 skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-avatar"></div>';
207 skeleton += ' </div>';
208 skeleton += ' <div class="vbo-dashboard-guest-activity-content chat-thread-content">';
209 skeleton += ' <div class="vbo-dashboard-guest-activity-content-head">';
210 skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-title"></div>';
211 skeleton += ' </div>';
212 skeleton += ' <div class="vbo-dashboard-guest-activity-content-subhead">';
213 skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-subtitle"></div>';
214 skeleton += ' </div>';
215 skeleton += ' <div class="vbo-dashboard-guest-activity-content-info-msg">';
216 skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-content"></div>';
217 skeleton += ' </div>';
218 skeleton += ' </div>';
219 skeleton += '</div>';
220 }
221
222 return skeleton;
223 }
224
225 const loadPreviousThreads = () => {
226 if (isLoadingOlderThreads) {
227 // do not proceed in case we are already loading something
228 return this;
229 }
230
231 // mark loading flag
232 isLoadingOlderThreads = true;
233
234 const threadsList = $('#<?php echo $id; ?>').find('.vbo-chat-threads');
235 threadsList.append(createLoadingSkeleton(5));
236
237 // make AJAX request to load older threads
238 VBOChatAjax.do(
239 // end-point URL
240 '<?php echo VBOFactory::getPlatform()->getUri()->ajax('index.php?option=com_vikbooking&task=chat.load_chats'); ?>',
241 // POST data
242 {
243 start: totalThreads,
244 limit: threadsLimit,
245 options: <?php echo json_encode($options ?? []); ?>,
246 },
247 // success callback
248 (threads) => {
249 // keep current scroll
250 let currentScrollTop = threadsList[0].scrollTop;
251 let currentScrollHeight = threadsList[0].scrollHeight;
252
253 // remove loading skeleton
254 threadsList.find('.vbo-dashboard-guest-activity-skeleton').remove();
255
256 threads.forEach((thread) => {
257 const existing = $('#<?php echo $id; ?>').find('.chat-thread[data-context="' + thread.alias + '"][data-id="' + thread.id + '"]');
258
259 if (!existing.length) {
260 // add thread only in case it is not already in the list
261 threadsList.append(thread.html);
262 }
263 });
264
265 // update count of loaded threads
266 totalThreads += threads.length;
267
268 // turn off scroll event in case we reached the limit
269 if (threads.length < threadsLimit) {
270 threadsList.off('scroll');
271 }
272
273 // make loading available again
274 isLoadingOlderThreads = false;
275 },
276 // failure callback
277 (error) => {
278 // remove loading skeleton
279 threadsList.find('.vbo-dashboard-guest-activity-skeleton').remove();
280 // make loading available again
281 isLoadingOlderThreads = false;
282 }
283 );
284 }
285
286 $(function() {
287 // do not register scroll event in case the number of messages is equal or
288 // higher then the total number of messages under this context
289 if (totalThreads >= threadsLimit) {
290 // setup scroll event to load older messages
291 $('#<?php echo $id; ?>').find('.vbo-chat-threads').on('scroll', function() {
292 if (isLoadingOlderThreads) {
293 // ignore if we are currently loading older messages
294 return;
295 }
296
297 // get scrollable pixel
298 const scrollHeight = this.scrollHeight - $(this).outerHeight();
299 // get scroll top
300 const scrollTop = this.scrollTop;
301
302 // start loading older threads only when scrollbar
303 // is 300px close to the end
304 if (scrollHeight - scrollTop <= 300) {
305 loadPreviousThreads();
306 }
307 });
308 }
309
310 $('#<?php echo $id; ?>').find('.vbo-chat-back').on('click', function() {
311 $(this).hide();
312 $('#<?php echo $id; ?>').find('.chat-thread[data-context][data-id].active').removeClass('active');
313 $(this).prev().removeClass('slide-in');
314
315 setTimeout(() => {
316 $(this).show();
317 }, 300);
318 });
319 });
320 })(jQuery);
321 </script>