blob: 721457e38b7ec56761a6ce39ea8ee3bdc89b7516 [file] [log] [blame]
aviau039001d2016-09-29 16:39:05 -04001<html>
aviau039001d2016-09-29 16:39:05 -04002<!-- Empty head might be needed for setSenderImage -->
3<head>
Frederic Guimontd8343e62016-11-01 23:31:25 -04004 <meta name="viewport" content="width=device-width, initial-scale=1" />
Frédéric Guimont13778a62016-11-02 21:21:21 -04005 <meta charset=“utf-8”>
aviau039001d2016-09-29 16:39:05 -04006</head>
7
8<body>
Sébastien Blinf4f90282017-10-03 14:07:16 -04009 <div id="invitation">
10 <div id="text">
11 </div>
12 <div id="actions">
13 <div id="accept-btn" class="flat-button button-green" onclick="ring.chatview.acceptInvitation()" >Accept</div>
14 <div id="refuse-btn" class="flat-button button-red" onclick="ring.chatview.refuseInvitation()" >Refuse</div>
15 <div id="block-btn" class="flat-button button-red" onclick="ring.chatview.blockConversation()" >Block</div>
16 </div>
17 </div>
Adrien Beraud8e25afb2017-04-19 01:38:57 +020018 <div id="container">
19 <div id="messages"></div>
AmarOkb4253242017-07-13 11:21:39 -040020
21 <div id="sendMessage">
Sébastien Blin55bff9d2017-10-03 15:15:23 -040022 <textarea id="message" autofocus placeholder="Message" onkeyup="grow_text_area()" rows="1" disabled="false"></textarea>
Guillaume Roguez5b137be2018-02-21 10:44:58 -050023 <div id="sendBtn" class="msg-button" onclick="ring.chatview.sendMessage()" title="Send">
AmarOkb4253242017-07-13 11:21:39 -040024 <svg viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg">
25 <path xmlns="http://www.w3.org/2000/svg" d="M12,11.874v4.357l7-6.69l-7-6.572v3.983c-8.775,0-11,9.732-11,9.732C3.484,12.296,7.237,11.874,12,11.874z"/>
26 </svg>
27 </div>
Guillaume Roguez5b137be2018-02-21 10:44:58 -050028 <div id="send-file-btn" class="msg-button" onclick="ring.chatview.sendFile()" title="Send File">
29 <svg viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg">
30 <path d="M0 0h24v24H0z" fill="none"/>
31 <path d="M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z"/>
32 </svg>
33 </div>
AmarOkb4253242017-07-13 11:21:39 -040034 </div>
35 </form>
Adrien Beraud8e25afb2017-04-19 01:38:57 +020036 </div>
aviau039001d2016-09-29 16:39:05 -040037</body>
38
Frederic Guimontd8343e62016-11-01 23:31:25 -040039<!--
aviau039001d2016-09-29 16:39:05 -040040<script src="https://soapbox.github.io/linkifyjs/js/linkify/linkify.min.js"></script>
41<script src="https://soapbox.github.io/linkifyjs/js/linkify/linkify-string.min.js"></script>
Frederic Guimontd8343e62016-11-01 23:31:25 -040042<script src="https://soapbox.github.io/linkifyjs/js/linkify/linkify-html.min.js"></script>
aviaue52afa22016-11-08 13:27:31 -050043<link rel="stylesheet" type="text/css" href="chatview.css">
Frederic Guimontd8343e62016-11-01 23:31:25 -040044-->
aviau039001d2016-09-29 16:39:05 -040045
aviau039001d2016-09-29 16:39:05 -040046<script>
AmarOk6286ad42017-07-14 12:11:08 -040047
48document.querySelector("#message").addEventListener("keydown", function (e) {
49 e = e || event;
50 var map = {};
51 map[e.keyCode] = e.type == 'keydown';
52 if (e.ctrlKey || e.shiftKey) {
53 return true;
54 }
55 if (map[13]) {
56 ring.chatview.sendMessage();
57 e.preventDefault();
58 }
59 return true;
60});
61
62function grow_text_area() {
63 var messages = document.querySelector('#messages');
64 var is_at_bottom = messages.scrollTop === (messages.scrollHeight - messages.offsetHeight);
65
66 var area = document.querySelector("#message");
67 var old_height = area.style.height;
68 area.style.height = "auto";
69 area.style.height = area.scrollHeight +"px";
70
71 if (is_at_bottom) {
72 messages.scrollTop = messages.scrollHeight;
73 }
74}
75
Sébastien Blinc6f3a262017-07-28 16:12:24 -040076/*
77 * Update timestamps messages
78 */
79function updateView() {
80 if (ring.chatview) ring.chatview.updateTimestamps();
81}
82setInterval(updateView, 60000);
83
aviau039001d2016-09-29 16:39:05 -040084var ring = {}; // ring namespace
85
86ring.chatview = (function(){
Sébastien Blinc6f3a262017-07-28 16:12:24 -040087 const avatar_size = 35;
aviau039001d2016-09-29 16:39:05 -040088 var dev = {}; // ring.chatview.dev namespace
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -050089 var raf = window.requestAnimationFrame || window.webkitRequestAnimationFrame;
90 var messages = document.querySelector("#messages");
Sébastien Blin70dc0b72017-07-31 16:24:41 -040091 var displayLinksEnabled = false;
Sébastien Blin05317a72018-02-21 11:09:16 -050092 var historyBufferIndex = 0; // When showing a large amount of interactions, this counter store the interaction's index to show
93 var historyBuffer = []; // Before showing a large amount of interactions, this array is used as a buffer.
Sébastien Blin70dc0b72017-07-31 16:24:41 -040094
95 function setDisplayLinks(display) {
96 displayLinksEnabled = display;
97 }
aviau039001d2016-09-29 16:39:05 -040098
99 /**
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400100 * Transform a date to a string group likes 1 hour ago.
101 */
102 function formatDate(date) {
103 const seconds = Math.floor((new Date() - date) / 1000);
104 var interval = Math.floor(seconds / (3600 * 24));
105 if (interval > 5) {
Sébastien Blin69242ce2017-08-04 11:40:50 -0400106 return date.toLocaleDateString();
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400107 }
108 if (interval > 1) {
109 return interval + ' days ago';
110 }
111 if (interval === 1) {
112 return interval + ' day ago';
113 }
114 interval = Math.floor(seconds / 3600);
115 if (interval > 1) {
116 return interval + ' hours ago';
117 }
118 if (interval === 1) {
119 return interval + ' hour ago';
120 }
121 interval = Math.floor(seconds / 60);
122 if (interval > 1) {
123 return interval + ' minutes ago';
124 }
125 return 'just now';
126 }
127
128 /**
AmarOkb4253242017-07-13 11:21:39 -0400129 * Send #sendMessage #message value
130 */
131 function sendMessage()
132 {
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400133 var input = document.querySelector("#sendMessage #message");
134 var message = input.value;
135 if (message.length > 0) {
136 input.value = '';
Sébastien Blinf4f90282017-10-03 14:07:16 -0400137 window.prompt('SEND:' + message);
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400138 }
AmarOkb4253242017-07-13 11:21:39 -0400139 }
140
Sébastien Blinf4f90282017-10-03 14:07:16 -0400141 /**
142 * Disable or enable textarea
143 */
144 function disableSendMessage(isDisabled)
145 {
146 var input = document.querySelector("#sendMessage #message");
147 input.disabled = isDisabled;
148 }
149
150 /**
151 * Accept an invite
152 */
153 function acceptInvitation()
154 {
155 window.prompt('ACCEPT');
156 }
157
158 /**
159 * Refuse an invite
160 */
161 function refuseInvitation()
162 {
163 window.prompt('REFUSE');
164 }
165
166 /**
167 * Accept an invite
168 */
169 function blockConversation()
170 {
171 window.prompt('BLOCK');
172 }
173
174 /**
175 * Change the content of the div invitation and show it
176 * @param contactAlias
177 */
178 function showInvitation(contactAlias) {
179 const invitationText = document.querySelector('#text');
180 const invitation = document.querySelector('#invitation');
181 invitationText.innerHTML = '<h1>' + contactAlias + ' sends you an invitation</h1>'
182 + 'Do you want to add them to the conversations list?<br>'
183 + 'Note: you can automatically accept this invitation by sending a message.';
184 invitation.style.visibility = 'visible';
185 }
186
187 /**
188 * Hide the content of invitation
189 * @param contactAlias
190 */
191 function hideInvitation() {
192 const invitation = document.querySelector('#invitation');
193 invitation.style.visibility = 'hidden';
194 }
195
AmarOkb4253242017-07-13 11:21:39 -0400196 /**
aviau039001d2016-09-29 16:39:05 -0400197 * Clears all messages
198 */
199 function clearMessages()
200 {
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500201 messages.innerHTML = "";
aviau039001d2016-09-29 16:39:05 -0400202 }
203
204 /**
205 * Converts text to HTML
206 */
207 function escapeHtml(html)
208 {
209 var text = document.createTextNode(html);
210 var div = document.createElement('div');
211 div.appendChild(text);
212 return div.innerHTML;
213 }
214
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500215
Sébastien Blin23c09e02017-07-28 16:19:17 -0400216 /**
217 * Get the youtube video id from a URI
218 */
219 function youtube_id(url) {
220 const regExp = /^.*(youtu\.be\/|v\/|\/u\/w|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;
221 const match = url.match(regExp);
222 return (match && match[2].length == 11) ? match[2] : null;
aviau039001d2016-09-29 16:39:05 -0400223 }
224
Sébastien Blin23c09e02017-07-28 16:19:17 -0400225
226 /**
227 * Show images/videos from links in a message.
228 */
229 function displayLinks(messageNode)
230 {
231 const finalMsg = document.createElement('div');
232 finalMsg.setAttribute('id', 'msg_content');
233 finalMsg.innerHTML = messageNode.outerHTML;
234
235 const parser = new DOMParser();
236 const DOMMsg = parser.parseFromString(messageNode.innerHTML, 'text/xml');
237 const links = DOMMsg.querySelectorAll('a');
238
239 const availableProtocols = ['http:', 'https:'];
240 const videoHostname = ['youtube.com', 'www.youtube.com', 'youtu.be'];
241
242 const cardElt = document.createElement('div');
243 cardElt.setAttribute('id', 'cardWrapper');
244
245 if (links.length) {
246 const link = links[0].getAttribute('href');
247 const urlParser = document.createElement('a');
248 urlParser.href = link;
249
250 // Parse videos
251 if (availableProtocols.includes(urlParser.protocol) && videoHostname.includes(urlParser.hostname)) {
252 // Youtube
253 const ytid = youtube_id(link)
254 if (ytid) {
255 const linkElt = document.createElement('a');
256 linkElt.href = link;
257 const containerElt = document.createElement('div');
258 containerElt.setAttribute('id', 'containerVideo');
259 const imageElt = document.createElement('img');
260 imageElt.src = `http://img.youtube.com/vi/${ytid}/0.jpg`;
261 const playDiv = document.createElement('div');
262 playDiv.setAttribute('id', 'playVideo');
263 playDiv.innerHTML = '<svg fill="#ffffff" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">\
264 <path d="M8 5v14l11-7z"/>\
265 <path d="M0 0h24v24H0z" fill="none"/>\
266 </svg>'
267 linkElt.appendChild(imageElt);
268 linkElt.appendChild(playDiv);
269 containerElt.appendChild(linkElt);
270 cardElt.appendChild(containerElt);
271 finalMsg.appendChild(cardElt);
272 messageNode.outerHTML = finalMsg.outerHTML;
273 }
274 } else {
275 // Try to display images.
276 const linkElt = document.createElement('a');
277 linkElt.href = link;
278 const imageElt = document.createElement('img');
279 // Note, here, we don't check the size of the image.
280 // in the future, we can check the content-type and content-length with a request
281 // and maybe disable svg
282 imageElt.setAttribute("onerror", "this.style.display='none'");
283 imageElt.src = link;
284 linkElt.appendChild(imageElt);
285 cardElt.appendChild(linkElt);
286 finalMsg.appendChild(cardElt);
287 messageNode.outerHTML = finalMsg.outerHTML;
288 }
289 }
290 }
291
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500292 /**
293 * Returns HTML message from the message text.
294 * Cleaned and linkified.
295 */
296 function getMessageHtml(message_text)
297 {
298 const escaped_message = escapeHtml(message_text);
299 var linkified_message = linkifyHtml(escaped_message, {});
Sébastien Blin23c09e02017-07-28 16:19:17 -0400300
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500301 const textPart = document.createElement('pre');
302 var linkified_message = linkified_message.replace("📞", "<span id=\"green\">📞</span>")
303 var linkified_message = linkified_message.replace("🕽", "<span id=\"red\">🕽</span>")
304 textPart.innerHTML = linkified_message;
Sébastien Blin23c09e02017-07-28 16:19:17 -0400305
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500306 return textPart.outerHTML;
307 }
Sébastien Blin23c09e02017-07-28 16:19:17 -0400308
aviau039001d2016-09-29 16:39:05 -0400309 /**
310 * Returns the message status, formatted for display
311 */
312 function getMessageDeliveryStatusText(message_delivery_status)
313 {
314 var formatted_delivery_status = message_delivery_status;
315
316 switch(message_delivery_status)
317 {
aviau039001d2016-09-29 16:39:05 -0400318 case "sending":
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500319 case "ongoing":
320 formatted_delivery_status = "Sending<svg overflow='visible' viewBox='0 -2 16 14' height='16px' width='16px'><circle class='status_circle anim-first' cx='4' cy='12' r='1'/><circle class='status_circle anim-second' cx='8' cy='12' r='1'/><circle class='status_circle anim-third' cx='12' cy='12' r='1'/></svg>";
Frédéric Guimont13778a62016-11-02 21:21:21 -0400321 break;
322 case "failure":
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500323 formatted_delivery_status = "Failure <svg overflow='visible' viewBox='0 -2 16 14' height='16px' width='16px'><path class='status-x x-first' stroke='#AA0000' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' fill='none' d='M4,4 L12,12'/><path class='status-x x-second' stroke='#AA0000' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' fill='none' d='M12,4 L4,12'/></svg>";
Frédéric Guimont13778a62016-11-02 21:21:21 -0400324 break;
aviau039001d2016-09-29 16:39:05 -0400325 case "sent":
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500326 case "finished":
327 case "unknown":
328 case "read":
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500329 formatted_delivery_status = "";
aviau039001d2016-09-29 16:39:05 -0400330 break;
331 default:
332 console.log("getMessageDeliveryStatusText: unknown delivery status: " + message_delivery_status);
333 break;
334 }
335
336 return formatted_delivery_status;
337 }
338
339 /**
340 * Returns the message date, formatted for display
341 */
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400342 function getMessageTimestampText(message_timestamp, custom_format)
aviau039001d2016-09-29 16:39:05 -0400343 {
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400344 const date = new Date(1000 * message_timestamp);
345 if(custom_format) {
346 return formatDate(date);
347 } else {
348 return date.toLocaleString();
349 }
350 }
351
352 /**
353 * Merge timestamps if they are from the same group (likes: "1 hour ago")
354 */
355 function cleanPreviousTimestamps (baseNode, endIndex) {
356 // Remove previous elements with the same formatted timestamp
357 for (var c = endIndex - 1 ; c >= 0 ; --c) {
358 const child = messages.children[c];
359 if (child.className.indexOf('timestamp') !== -1) {
360 if (child.className === baseNode.className &&
361 child.innerHTML === baseNode.innerHTML) {
362 messages.removeChild(child);
363 } else {
364 break; // A different timestamp is met, we can stop here.
365 }
366 }
367 }
368 }
369
370 /**
371 * Update padding if the sender image is displayed
372 */
373 function updateTimestampPadding (timestampNode, senderImage, direction) {
374 var new_padding = 20;
375 if (timestampNode.className.indexOf(`timestamp_${direction}`) &&
376 senderImage &&
377 senderImage.offsetHeight === avatar_size) {
378 new_padding += avatar_size;
379 }
380
381 if (direction == 'out')
382 timestampNode.style.paddingRight = new_padding;
383 else
384 timestampNode.style.paddingLeft = new_padding;
385 }
386
387 /**
388 * Update timestamps
389 */
390 function updateTimestamps() {
391 const timestamps = messages.querySelectorAll('.timestamp');
392 const image_out = messages.querySelector('.message_out .sender_image')
393 const image_in = messages.querySelector('.message_in .sender_image')
394 if (timestamps) {
395 for ( var c = 0 ; c < messages.children.length ; ++c) {
396 const child = messages.children[c];
397 if (child.className.indexOf('timestamp') !== -1) {
398 // Update timestamp
399 child.innerHTML = getMessageTimestampText(child.getAttribute('message_timestamp'), true)
400 // Update padding
401 updateTimestampPadding (child, image_out, 'out');
402 updateTimestampPadding (child, image_in, 'in');
403 // Remove previous elements with the same formatted timestamp
404 cleanPreviousTimestamps(child, c);
405 }
406 }
407 }
aviau039001d2016-09-29 16:39:05 -0400408 }
409
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500410 /**
411 * Convert a value in filesize
412 */
413 function humanFileSize(bytes) {
414 var thresh = 1024;
415 if(Math.abs(bytes) < thresh) {
416 return bytes + ' B';
417 }
418 var units = ['kB','MB','GB','TB','PB','EB','ZB','YB']
419 var u = -1;
420 do {
421 bytes /= thresh;
422 ++u;
423 } while(Math.abs(bytes) >= thresh && u < units.length - 1);
424 return bytes.toFixed(1)+' '+units[u];
425 }
426
427 /**
428 * Change the value of the progress bar
429 */
430 function updateProgressBar(progress_bar, message_object, message_delivery_status) {
431 var delivery_status = message_object["delivery_status"];
432 if ("progress" in message_object && !isErrorStatus(delivery_status) && message_object["progress"] !== 100) {
433 var progress_percent = (100 * message_object["progress"] / message_object["totalSize"]);
434 if (progress_percent !== 100)
435 progress_bar.childNodes[0].setAttribute("style", "width: " + progress_percent + "%");
436 else
437 progress_bar.setAttribute("style", "display: none");
438 } else
439 progress_bar.setAttribute("style", "display: none");
440 }
441
442 /**
443 * Check if a status is an error status
444 */
445 function isErrorStatus(status) {
446 return (status === 'failure'
447 || status === 'canceled'
448 || status === 'unjoinable peer');
449 }
450
451 /**
452 * Update a data transfer interaction
453 */
454 function updateDataTransferInteraction(message_div, message_object) {
455 var acceptSvg = '<svg fill="#ffffff" height="36" viewBox="0 0 24 24" width="36" xmlns="http://www.w3.org/2000/svg" style="width:50%; display:block; margin:auto;"><path d="M0 0h24v24H0z" fill="none"/><path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/></svg>',
456 refuseSvg = '<svg fill="#ffffff" height="36" viewBox="0 0 24 24" width="36" xmlns="http://www.w3.org/2000/svg" style="width:50%; display:block; margin:auto;"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/><path d="M0 0h24v24H0z" fill="none"/></svg>',
457 fileSvg = '<svg fill="#000000" height="36" viewBox="0 0 24 24" width="36" xmlns="http://www.w3.org/2000/svg" style="width:50%;"><path d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>',
458 warningSvg = '<svg fill="#000000" height="36" viewBox="0 0 24 24" width="36" xmlns="http://www.w3.org/2000/svg" style="width:50%;"><path d="M0 0h24v24H0z" fill="none"/><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg>',
459 incomingSvg = '<svg fill="#000000" height="36" viewBox="0 0 24 24" width="36" xmlns="http://www.w3.org/2000/svg" style="width:50%;"><path d="M0 0h24v24H0z" fill="none"/><path d="M20 5.41L18.59 4 7 15.59V9H5v10h10v-2H8.41z"/></svg>',
460 outgoingSvg = '<svg fill="#000000" height="36" viewBox="0 0 24 24" width="36" xmlns="http://www.w3.org/2000/svg" style="width:50%;"><path d="M0 0h24v24H0z" fill="none"/><path d="M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5z"/></svg>';
461 var message_delivery_status = message_object["delivery_status"];
462 var message_direction = message_object["direction"];
463 var message_id = message_object["id"];
464
465 // Set informations text
466 var informations_div = message_div.querySelector("#informations");
467 var informations_txt = getMessageTimestampText(message_object["timestamp"], true);
468 if (message_object["totalSize"] && message_object["progress"]) {
469 if (message_delivery_status === 'finished') {
470 informations_txt += " - " + humanFileSize(message_object["totalSize"]);
471 } else {
472 informations_txt += " - " + humanFileSize(message_object["progress"])
473 + " / " + humanFileSize(message_object["totalSize"]);
474 }
475 }
476 informations_txt += " - " + message_delivery_status;
477 informations_div.innerText = informations_txt;
478
479 // Update flat buttons
480 var left_buttons = message_div.querySelector("#left_buttons");
481 left_buttons.innerHTML = '';
482 if (message_delivery_status === 'awaiting') {
483 // add buttons to accept or refuse a call.
484 var accept_button = document.createElement('div');
485 accept_button.innerHTML = acceptSvg;
486 accept_button.setAttribute("id", "accept-btn");
487 accept_button.setAttribute("title", "Accept");
488 accept_button.setAttribute("class", "flat-button button-green");
489 accept_button.onclick = function() {
490 window.prompt('ACCEPT_FILE:' + message_id);
491 }
492 left_buttons.appendChild(accept_button);
493 var refuse_button = document.createElement('div');
494 refuse_button.innerHTML = refuseSvg;
495 refuse_button.setAttribute("id", "refuse-btn");
496 refuse_button.setAttribute("title", "Refuse");
497 refuse_button.setAttribute("class", "flat-button button-red");
498 refuse_button.onclick = function() {
499 window.prompt('REFUSE_FILE:' + message_id);
500 }
501 left_buttons.appendChild(refuse_button);
502 } else {
503 var status_button = document.createElement('div');
504 var statusFile = fileSvg;
505 if (isErrorStatus(message_delivery_status))
506 statusFile = warningSvg;
507 status_button.innerHTML = statusFile;
508 status_button.setAttribute("class", "flat-button");
509 left_buttons.appendChild(status_button);
510 var direction_button = document.createElement('div');
511 var directionFile = message_direction === 'out' ? outgoingSvg: incomingSvg;
512 direction_button.innerHTML = directionFile;
513 direction_button.setAttribute("class", "flat-button");
514 left_buttons.appendChild(direction_button);
515 }
516 updateProgressBar(chatview_message_transfer_progress_bar, message_object);
517 }
518
aviau039001d2016-09-29 16:39:05 -0400519 /**
520 * Adds a message to the buffer, or update it if new_message is
521 * TRUE
522 */
Sébastien Blin05317a72018-02-21 11:09:16 -0500523 function addOrUpdateMessage(message_object, new_message, insert_after = true)
aviau039001d2016-09-29 16:39:05 -0400524 {
525 // Properties of the message object
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500526 var message_id = message_object["id"],
527 message_text = message_object["text"],
528 message_sender = message_object["sender"],
529 message_sender_contact_method = message_object["sender_contact_method"],
530 message_timestamp = message_object["timestamp"],
531 message_direction = message_object["direction"],
Sébastien Blinf4f90282017-10-03 14:07:16 -0400532 message_type = message_object["type"],
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500533 message_delivery_status = message_object["delivery_status"],
534 message_div_classes,
535 chatview_message_div,
536 chatview_message_wrapper,
537 chatview_message_text,
538 chatview_message_sender,
539 chatview_message_delivery_status,
540 chatview_message_timestamp,
541 chatview_message_sender_span,
542 chatview_message_sender_image,
543 chatview_message_div,
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500544 sentAnimation = "<svg overflow='visible' viewBox='0 -2 16 14' height='16px' width='16px'><path class='status-check' stroke='#008000' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' fill='none' d='M2,8 L6,12 L14,4'/></svg>";
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500545
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500546 var chatview_sentCheckmark = document.createElement('span');
547 chatview_sentCheckmark.setAttribute("class", "sent-checkmark");
aviau039001d2016-09-29 16:39:05 -0400548
Adrien Beraud8e25afb2017-04-19 01:38:57 +0200549 chatview_message_div = document.querySelector("#message_" + message_id);
aviau039001d2016-09-29 16:39:05 -0400550 if (new_message)
551 {
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500552 message_div_classes = [
aviau039001d2016-09-29 16:39:05 -0400553 "message",
Sébastien Blinf4f90282017-10-03 14:07:16 -0400554 "message_" + message_direction,
555 "message_type_" + message_type
aviau039001d2016-09-29 16:39:05 -0400556 ];
557
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500558 chatview_message_div = document.createElement('div');
559 chatview_message_div.setAttribute("id", "message_" + message_id);
560 chatview_message_div.setAttribute("class", message_div_classes.join(" "));
aviau039001d2016-09-29 16:39:05 -0400561
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500562 chatview_message_wrapper = document.createElement('div');
563 chatview_message_wrapper.setAttribute("class", "message_wrapper wc");
Frederic Guimontd8343e62016-11-01 23:31:25 -0400564
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500565 if (message_type === 'data_transfer') {
566 var leftbuttons_div = document.createElement('div');
567 leftbuttons_div.setAttribute("id", "left_buttons");
568 chatview_message_wrapper.appendChild(leftbuttons_div);
569
570 var text_div = document.createElement('div');
571 text_div.setAttribute("id", "text");
572 text_div.addEventListener('click', function (event) {
573 // ask the soft to open the file
574 var filename = document.querySelector("#message_" + message_id + " #filename").innerText;
575 window.prompt('OPEN_FILE:' + filename);
576 });
577 chatview_message_text = document.createElement('div');
578 chatview_message_text.setAttribute("id", "filename");
579 var informations_div = document.createElement('div');
580 informations_div.setAttribute("id", "informations");
581 text_div.appendChild(chatview_message_text);
582 text_div.appendChild(informations_div);
583 chatview_message_wrapper.appendChild(text_div);
584
585 // DataTransfer progress bar
586 chatview_message_transfer_progress_bar = document.createElement('span');
587 chatview_message_transfer_progress_bar.setAttribute("class", "message_progress_bar");
588 chatview_message_transfer_progress_completion = document.createElement('span');
589 chatview_message_transfer_progress_bar.appendChild(chatview_message_transfer_progress_completion);
590 chatview_message_wrapper.appendChild(chatview_message_transfer_progress_bar);
591 } else {
592 chatview_message_text = document.createElement('span');
593 chatview_message_text.setAttribute("class", "message_text");
594 chatview_message_wrapper.appendChild(chatview_message_text);
595 chatview_message_delivery_status = document.createElement('span');
596 chatview_message_delivery_status.setAttribute("class", "message_delivery_status");
597 }
598
aviau039001d2016-09-29 16:39:05 -0400599
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500600 chatview_message_sender = document.createElement('span');
601 chatview_message_sender.setAttribute("class", "message_sender");
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500602 chatview_message_wrapper.appendChild(chatview_message_sender);
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500603 chatview_message_sender_span = document.createElement('span');
604 chatview_message_sender_span.setAttribute("class", "message_sender_image");
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500605 chatview_message_sender_image = document.createElement('span');
606 chatview_message_sender_image.setAttribute("class", "sender_image sender_image_" + message_sender_contact_method);
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500607 chatview_message_timestamp = document.createElement('span');
608 chatview_message_timestamp.setAttribute("class", "message_timestamp");
aviau039001d2016-09-29 16:39:05 -0400609
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500610 // Append elements to div
611 chatview_message_div.appendChild(chatview_message_sender_image);
612 chatview_message_div.appendChild(chatview_message_wrapper);
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500613 if (message_type !== 'data_transfer')
614 chatview_message_div.appendChild(chatview_message_delivery_status);
Frederic Guimontd8343e62016-11-01 23:31:25 -0400615
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400616 // Get timestamp to add
617 const formattedTimestamp = getMessageTimestampText(message_timestamp, true);
618 // Create the timestamp object
619 const date_elt = document.createElement('div');
620 date_elt.innerHTML = formattedTimestamp;
621 timestamp_div_classes = [
622 "timestamp",
623 "timestamp_" + message_direction
624 ];
625 date_elt.setAttribute("class", timestamp_div_classes.join(" "));
626 date_elt.setAttribute("message_timestamp", message_timestamp);
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500627 // Remove last timestamp if it's the same<h6></h6>
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400628 if (messages.querySelectorAll(".timestamp"))
629 cleanPreviousTimestamps(date_elt, messages.children.length);
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400630
Sébastien Blin05317a72018-02-21 11:09:16 -0500631 // Add message and the new timestamp
632 if (insert_after) {
633 messages.appendChild(chatview_message_div);
634 messages.appendChild(date_elt);
635 } else {
636 messages.insertBefore(date_elt, messages.firstChild);
637 messages.insertBefore(chatview_message_div, messages.firstChild);
638 }
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500639 } else {
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500640 if (chatview_message_div) {
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500641 if (message_type === 'data_transfer') {
642 chatview_message_text = chatview_message_div.querySelector("#filename");
643 } else {
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500644 chatview_message_text = chatview_message_div.querySelector(".message_text");
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500645 chatview_message_delivery_status = chatview_message_div.querySelector(".message_delivery_status");
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500646 }
647 chatview_message_timestamp = chatview_message_div.querySelector(".message_timestamp");
648 chatview_message_sender = chatview_message_div.querySelector(".message_sender");
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500649 } else {
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500650 console.log('no msg selector.');
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500651 }
aviau039001d2016-09-29 16:39:05 -0400652 }
653
654 // Set the variables
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500655 chatview_message_text.innerHTML = getMessageHtml(message_text);
Sébastien Blin70dc0b72017-07-31 16:24:41 -0400656 if (new_message && displayLinksEnabled)
Sébastien Blin23c09e02017-07-28 16:19:17 -0400657 displayLinks(chatview_message_text);
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500658 chatview_message_sender.textContent = message_sender + ": ";
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500659 chatview_message_timestamp.textContent = getMessageTimestampText(message_timestamp);
aviau039001d2016-09-29 16:39:05 -0400660
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500661 if (message_type === 'data_transfer') {
662 updateDataTransferInteraction(chatview_message_div, message_object);
663 } else {
664 chatview_message_delivery_status.innerHTML = getMessageDeliveryStatusText(message_delivery_status);
665 }
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400666
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500667 // Add timestamp and sent checkmark
668 if (new_message) {
669 if (message_direction === "out") {
670 chatview_sentCheckmark.innerHTML = sentAnimation;
671 chatview_message_div.querySelector(".message_wrapper").appendChild(chatview_sentCheckmark);
672 }
673 chatview_message_div.querySelector(".message_wrapper").appendChild(chatview_message_timestamp);
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500674 }
675
676 if (message_direction === "out") {
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500677 if (message_delivery_status === "sent" || message_delivery_status === "finished") {
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500678 chatview_message_div.classList.add("message--sent");
679 }
680 }
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400681
682 updateTimestamps();
aviau039001d2016-09-29 16:39:05 -0400683 }
684
aviau039001d2016-09-29 16:39:05 -0400685 /**
686 * Add a message to the buffer
687 */
688 function addMessage(message_object)
689 {
Adrien Beraud8e25afb2017-04-19 01:38:57 +0200690 var atEnd = messages.scrollTop >= messages.scrollHeight - messages.clientHeight - 5;
aviau039001d2016-09-29 16:39:05 -0400691 addOrUpdateMessage(message_object, true);
Adrien Beraud8e25afb2017-04-19 01:38:57 +0200692 if (atEnd) {
693 var startTime = Date.now(),
694 durTime = 250.,
695 scrollStartHeight = messages.scrollHeight,
696 scrollStart = messages.scrollTop,
697 scrollDiff = scrollStartHeight - messages.clientHeight - scrollStart;
aviau039001d2016-09-29 16:39:05 -0400698
Adrien Beraud8e25afb2017-04-19 01:38:57 +0200699 function loop() {
700 var time = Date.now() - startTime,
701 scrollHeight = messages.scrollHeight,
702 diff = messages.scrollTop - scrollStart; // If user scrolls up (diff > 0).
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500703
Adrien Beraud8e25afb2017-04-19 01:38:57 +0200704 if (time >= durTime || scrollHeight != scrollStartHeight || diff < 0) {
705 if (diff >= 0) { // User scrolled up, don't autoscroll.
706 messages.scrollTop = scrollHeight;
707 }
708 return false;
709 } else {
710 messages.scrollTop = scrollStart + (scrollDiff * (time/durTime));
711 raf(loop);
712 }
713 }
714 raf(loop); // Start the animation loop
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500715 }
aviau039001d2016-09-29 16:39:05 -0400716 }
717
718 /**
Sébastien Blin05317a72018-02-21 11:09:16 -0500719 * Show the history in reverse order and avoid the process to consumes a lot of ressources
720 */
721 function printHistoryPart () {
722 var previousScrollHeightMinusTop = messages.scrollHeight - messages.scrollTop;
723 // Show 10 messages
724 for (var i = 0; i < 10; ++i) {
725 addOrUpdateMessage(historyBuffer[historyBuffer.length - 1 - historyBufferIndex], true, false);
726 historyBufferIndex ++;
727 if (historyBufferIndex === historyBuffer.length)
728 break;
729 }
730 // Replace the scrollbar to the wanted position
731 messages.scrollTop = messages.scrollHeight - previousScrollHeightMinusTop;
732 // Make a short pause to minimizes ressources consumption
733 // show quickly the first hundred messages
734 if (historyBufferIndex !== historyBuffer.length)
735 setTimeout(printHistoryPart, (historyBufferIndex > 100) ? 100 : 1);
736 }
737
738 /**
739 * Show the whole history in the chatview.
740 */
741 function printHistory(messages_array)
742 {
743 historyBuffer = messages_array;
744 historyBufferIndex = 0;
745 setTimeout(printHistoryPart, 1);
746 }
747
748 /**
aviau039001d2016-09-29 16:39:05 -0400749 * Updated a message that was previously added with addMessage
750 */
751 function updateMessage(message_object)
752 {
753 addOrUpdateMessage(message_object, false);
754 }
755
756 /**
757 * Sets the image for a given sender
758 * set_sender_image object should contain the following keys:
759 * - sender: the name of the sender
760 * - sender_image: base64 png encoding of the sender image
761 */
762 function setSenderImage(set_sender_image_object)
763 {
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500764 var sender_contact_method = set_sender_image_object['sender_contact_method'],
765 sender_image = set_sender_image_object['sender_image'],
766 sender_image_id = "sender_image_" + sender_contact_method,
767 currentSenderImage = document.querySelector("#" + sender_image_id), // Remove the currently set sender image
768 style;
aviau039001d2016-09-29 16:39:05 -0400769
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500770 if (currentSenderImage) {
771 currentSenderImage.parentNode.removeChild(currentSenderImage);
772 }
aviau039001d2016-09-29 16:39:05 -0400773
774 // Create a new style element
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500775 style = document.createElement('style');
776
aviau039001d2016-09-29 16:39:05 -0400777 style.type = 'text/css';
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500778 style.id = sender_image_id;
Frederic Guimontd8343e62016-11-01 23:31:25 -0400779 style.innerHTML = '.' + sender_image_id + " { \n content: url(data:image/png;base64," + sender_image + "); \n height: 35px; \n width: 35px; \n }";
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500780 document.head.appendChild(style);
aviau039001d2016-09-29 16:39:05 -0400781 }
782
AmarOkb4253242017-07-13 11:21:39 -0400783 /**
784 * Change the send icon
785 */
786 function setSendIcon(source)
787 {
788 var sendBtn = document.querySelector("#sendicon");
789 sendBtn.src = "data:image/png;base64," + source;
790 }
791
aviau039001d2016-09-29 16:39:05 -0400792 function clearSenderImages()
793 {
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500794 var styles = document.head.querySelectorAll("style"),
795 i = styles.length;
796
797 while (i--){
798 document.head.removeChild(styles[i]);
799 }
aviau039001d2016-09-29 16:39:05 -0400800 }
801
Sébastien Blinf4f90282017-10-03 14:07:16 -0400802 function setTemporary(temporary)
803 {
804 var area = document.querySelector("#message");
805 var sendBtn = document.querySelector("#sendBtn");
806 if (temporary) {
807 area.placeholder = "Note: an interaction will create a new contact.";
808 sendBtn.innerHTML = "<svg viewBox=\"0 0 32 32\" xmlns=\"http://www.w3.org/2000/svg\">\
809 <path d=\"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/>\
810 <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\
811 </svg>";
812 } else {
813 area.placeholder = "Message";
814 sendBtn.innerHTML = "<svg viewBox=\"0 0 30 30\" xmlns=\"http://www.w3.org/2000/svg\">\
815 <path xmlns=\"http://www.w3.org/2000/svg\"\ d=\"M12,11.874v4.357l7-6.69l-7-6.572v3.983c-8.775,0-11,9.732-11,9.732C3.484,12.296,7.237,11.874,12,11.874z\"/>\
816 </svg>";
817 }
818 }
819
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500820 function sendFile()
821 {
822 window.prompt('SEND_FILE');
823 }
824
aviau039001d2016-09-29 16:39:05 -0400825 return {
826 addMessage: addMessage,
Sébastien Blin05317a72018-02-21 11:09:16 -0500827 printHistory: printHistory,
aviau039001d2016-09-29 16:39:05 -0400828 updateMessage: updateMessage,
829 setSenderImage: setSenderImage,
AmarOkb4253242017-07-13 11:21:39 -0400830 setSendIcon: setSendIcon,
aviau039001d2016-09-29 16:39:05 -0400831 dev: dev,
832 clearMessages: clearMessages,
833 clearSenderImages: clearSenderImages,
AmarOkb4253242017-07-13 11:21:39 -0400834 sendMessage: sendMessage,
Sébastien Blin70dc0b72017-07-31 16:24:41 -0400835 setDisplayLinks: setDisplayLinks,
Sébastien Blinf4f90282017-10-03 14:07:16 -0400836 setTemporary: setTemporary,
837 acceptInvitation: acceptInvitation,
838 refuseInvitation: refuseInvitation,
839 blockConversation: blockConversation,
840 showInvitation: showInvitation,
841 hideInvitation: hideInvitation,
842 disableSendMessage: disableSendMessage,
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500843 updateTimestamps: updateTimestamps,
844 sendFile: sendFile,
aviau039001d2016-09-29 16:39:05 -0400845 }
846
847})();
848
849/* DEV functions */
850ring.chatview.dev = (function(){
851
852
853
854 /**
855 * Fills the backlog with bogus messages
856 */
857 function fillMessages()
858 {
859 ring.chatview.clearMessages();
860
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500861 profile_picture = "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";
862
863 profile_picture = "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";
aviau039001d2016-09-29 16:39:05 -0400864
865 // Set the profile pictures
866 ring.chatview.setSenderImage(
867 {
aviaue52afa22016-11-08 13:27:31 -0500868 "sender_contact_method": "self",
aviau039001d2016-09-29 16:39:05 -0400869 "sender_image": profile_picture,
870 }
871 );
872
873 ring.chatview.setSenderImage(
874 {
aviaue52afa22016-11-08 13:27:31 -0500875 "sender_contact_method": "0x3345",
aviau039001d2016-09-29 16:39:05 -0400876 "sender_image": profile_picture,
877 }
878 )
879
880
881 // Create messages
882 ring.chatview.addMessage(
883 {
884 "id": 0,
885 "text": "Hello! This message's status will be updated to sent.",
886 "sender": "Me",
aviaue52afa22016-11-08 13:27:31 -0500887 "sender_contact_method": "self",
aviau039001d2016-09-29 16:39:05 -0400888 "timestamp": 1475275399,
889 "direction": "out",
890 "delivery_status": "sending",
891 }
892 );
893
894 ring.chatview.updateMessage(
895 {
896 "id": 0,
897 "text": "Hello! This message's status will be updated to sent.",
898 "sender": "Me",
aviaue52afa22016-11-08 13:27:31 -0500899 "sender_contact_method": "self",
aviau039001d2016-09-29 16:39:05 -0400900 "timestamp": 1475275399,
901 "direction": "out",
Frédéric Guimont13778a62016-11-02 21:21:21 -0400902 "delivery_status": "sending",
aviau039001d2016-09-29 16:39:05 -0400903 }
904 );
905
906 ring.chatview.addMessage(
907 {
908 "id": 1,
909 "text": "Hey! This message's status is 'Unknown', we shouldn't display that to the user.",
910 "sender": "Other Guy",
aviaue52afa22016-11-08 13:27:31 -0500911 "sender_contact_method": "0x3345",
aviau039001d2016-09-29 16:39:05 -0400912 "timestamp": 1475275399,
913 "direction": "in",
914 "delivery_status": "unknown",
915 }
916 );
917
918 ring.chatview.addMessage(
919 {
Frédéric Guimont13778a62016-11-02 21:21:21 -0400920 "id": 1,
aviau039001d2016-09-29 16:39:05 -0400921 "text": "This is the second message in a row from me. Don't bother displaying my profile picture twice.",
922 "sender": "Other Guy",
aviaue52afa22016-11-08 13:27:31 -0500923 "sender_contact_method": "0x3345",
aviau039001d2016-09-29 16:39:05 -0400924 "timestamp": 1475275399,
925 "direction": "in",
926 "delivery_status": "unknown",
927 }
928 );
929
930 ring.chatview.addMessage(
931 {
932 "id": 0,
933 "text": "Now its my turn to speak!",
934 "sender": "Me",
aviaue52afa22016-11-08 13:27:31 -0500935 "sender_contact_method": "self",
aviau039001d2016-09-29 16:39:05 -0400936 "timestamp": 1475275399,
937 "direction": "out",
Frédéric Guimont13778a62016-11-02 21:21:21 -0400938 "delivery_status": "sent",
aviau039001d2016-09-29 16:39:05 -0400939 }
940 );
941
Frédéric Guimont13778a62016-11-02 21:21:21 -0400942 ring.chatview.addMessage(
943 {
944 "id": 5,
945 "text": "Hello! This message's status will be updated to sent.",
946 "sender": "Me",
aviaue52afa22016-11-08 13:27:31 -0500947 "sender_contact_method": "self",
Frédéric Guimont13778a62016-11-02 21:21:21 -0400948 "timestamp": 1475275399,
949 "direction": "out",
950 "delivery_status": "sending",
951 }
952 );
953
954
955 ring.chatview.updateMessage(
956 {
957 "id": 5,
958 "text": "Hello! This message's status will be updated to sent.",
959 "sender": "Me",
aviaue52afa22016-11-08 13:27:31 -0500960 "sender_contact_method": "self",
Frédéric Guimont13778a62016-11-02 21:21:21 -0400961 "timestamp": 1475275399,
962 "direction": "out",
963 "delivery_status": "failure",
964 }
965 );
aviau039001d2016-09-29 16:39:05 -0400966
967 }
968
969 return {
970 fillMessages: fillMessages,
971 }
972
973})();
974
975</script>
976
977</html>