chatview: modify interface and add the ability to cancel while transfering
+ addOrUpdateMessage was really hard to read and the UI a bit dirty.
+ Fix svg width when resizing. Add some media queries to supports
either big resolutions than little one.
+ Clean generated DOM and CSS
Change-Id: I8980f78a04ab372cfddbc7eefe4542a5e9a3c5f2
diff --git a/web/chatview.html b/web/chatview.html
index d534a0e..cf11b40 100644
--- a/web/chatview.html
+++ b/web/chatview.html
@@ -10,9 +10,9 @@
<div id="text">
</div>
<div id="actions">
- <div id="accept-btn" class="flat-button button-green" onclick="ring.chatview.acceptInvitation()" >Accept</div>
- <div id="refuse-btn" class="flat-button button-red" onclick="ring.chatview.refuseInvitation()" >Refuse</div>
- <div id="block-btn" class="flat-button button-red" onclick="ring.chatview.blockConversation()" >Block</div>
+ <div id="accept-btn" class="invitation-button button-green" onclick="ring.chatview.acceptInvitation()" >Accept</div>
+ <div id="refuse-btn" class="invitation-button button-red" onclick="ring.chatview.refuseInvitation()" >Refuse</div>
+ <div id="block-btn" class="invitation-button button-red" onclick="ring.chatview.blockConversation()" >Block</div>
</div>
</div>
<div id="container">
@@ -20,14 +20,14 @@
<div id="sendMessage">
<textarea id="message" autofocus placeholder="Message" onkeyup="grow_text_area()" rows="1" disabled="false"></textarea>
- <div id="sendBtn" class="msg-button" onclick="ring.chatview.sendMessage()" title="Send">
+ <div class="msg-button" onclick="ring.chatview.sendMessage()" title="Send">
<svg viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg">
<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"/>
</svg>
</div>
- <div id="send-file-btn" class="msg-button" onclick="ring.chatview.sendFile()" title="Send File">
+ <div class="msg-button" onclick="ring.chatview.sendFile()" title="Send File">
<svg viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg">
- <path d="M0 0h24v24H0z" fill="none"/>
+ <path d="M0 0h24v24H0z" fill="none" />
<path d="M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z"/>
</svg>
</div>
@@ -81,6 +81,10 @@
}
setInterval(updateView, 60000);
+window.onresize = function(event) {
+ if (ring.chatview) ring.chatview.updateTimestamps();
+};
+
var ring = {}; // ring namespace
ring.chatview = (function(){
@@ -222,73 +226,6 @@
return (match && match[2].length == 11) ? match[2] : null;
}
-
- /**
- * Show images/videos from links in a message.
- */
- function displayLinks(messageNode)
- {
- const finalMsg = document.createElement('div');
- finalMsg.setAttribute('id', 'msg_content');
- finalMsg.innerHTML = messageNode.outerHTML;
-
- const parser = new DOMParser();
- const DOMMsg = parser.parseFromString(messageNode.innerHTML, 'text/xml');
- const links = DOMMsg.querySelectorAll('a');
-
- const availableProtocols = ['http:', 'https:'];
- const videoHostname = ['youtube.com', 'www.youtube.com', 'youtu.be'];
-
- const cardElt = document.createElement('div');
- cardElt.setAttribute('id', 'cardWrapper');
-
- if (links.length) {
- const link = links[0].getAttribute('href');
- const urlParser = document.createElement('a');
- urlParser.href = link;
-
- // Parse videos
- if (availableProtocols.includes(urlParser.protocol) && videoHostname.includes(urlParser.hostname)) {
- // Youtube
- const ytid = youtube_id(link)
- if (ytid) {
- const linkElt = document.createElement('a');
- linkElt.href = link;
- const containerElt = document.createElement('div');
- containerElt.setAttribute('id', 'containerVideo');
- const imageElt = document.createElement('img');
- imageElt.src = `http://img.youtube.com/vi/${ytid}/0.jpg`;
- const playDiv = document.createElement('div');
- playDiv.setAttribute('id', 'playVideo');
- playDiv.innerHTML = '<svg fill="#ffffff" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">\
- <path d="M8 5v14l11-7z"/>\
- <path d="M0 0h24v24H0z" fill="none"/>\
- </svg>'
- linkElt.appendChild(imageElt);
- linkElt.appendChild(playDiv);
- containerElt.appendChild(linkElt);
- cardElt.appendChild(containerElt);
- finalMsg.appendChild(cardElt);
- messageNode.outerHTML = finalMsg.outerHTML;
- }
- } else {
- // Try to display images.
- const linkElt = document.createElement('a');
- linkElt.href = link;
- const imageElt = document.createElement('img');
- // Note, here, we don't check the size of the image.
- // in the future, we can check the content-type and content-length with a request
- // and maybe disable svg
- imageElt.setAttribute("onerror", "this.style.display='none'");
- imageElt.src = link;
- linkElt.appendChild(imageElt);
- cardElt.appendChild(linkElt);
- finalMsg.appendChild(cardElt);
- messageNode.outerHTML = finalMsg.outerHTML;
- }
- }
- }
-
/**
* Returns HTML message from the message text.
* Cleaned and linkified.
@@ -299,8 +236,6 @@
var linkified_message = linkifyHtml(escaped_message, {});
const textPart = document.createElement('pre');
- var linkified_message = linkified_message.replace("📞", "<span id=\"green\">📞</span>")
- var linkified_message = linkified_message.replace("🕽", "<span id=\"red\">🕽</span>")
textPart.innerHTML = linkified_message;
return textPart.outerHTML;
@@ -368,38 +303,35 @@
}
/**
- * Update padding if the sender image is displayed
- */
- function updateTimestampPadding (timestampNode, senderImage, direction) {
- var new_padding = 20;
- if (timestampNode.className.indexOf(`timestamp_${direction}`) &&
- senderImage &&
- senderImage.offsetHeight === avatar_size) {
- new_padding += avatar_size;
- }
-
- if (direction == 'out')
- timestampNode.style.paddingRight = new_padding;
- else
- timestampNode.style.paddingLeft = new_padding;
- }
-
- /**
* Update timestamps
*/
function updateTimestamps() {
const timestamps = messages.querySelectorAll('.timestamp');
- const image_out = messages.querySelector('.message_out .sender_image')
- const image_in = messages.querySelector('.message_in .sender_image')
+ const is_image_out = messages.querySelector('.message_out .sender_image')
+ const is_image_in = messages.querySelector('.message_in .sender_image')
if (timestamps) {
for ( var c = 0 ; c < messages.children.length ; ++c) {
const child = messages.children[c];
if (child.className.indexOf('timestamp') !== -1) {
// Update timestamp
child.innerHTML = getMessageTimestampText(child.getAttribute('message_timestamp'), true)
- // Update padding
- updateTimestampPadding (child, image_out, 'out');
- updateTimestampPadding (child, image_in, 'in');
+
+ var desktop_margin = '25%'
+ const height = document.body.clientHeight
+ const width = document.body.clientWidth
+ if (width <= 1920 || height <= 1080) {
+ desktop_margin = '15%'
+ }
+ if (width <= 1000 || height <= 480) {
+ desktop_margin = '0px'
+ }
+ if (child.className.indexOf('timestamp_out') !== -1) {
+ const avatar_px = is_image_out ? (is_image_out.offsetHeight === avatar_size ? '60px' : '20px') : '20px'
+ child.style.paddingRight = `calc(${desktop_margin} + ${avatar_px})`
+ } else if (child.className.indexOf('timestamp_in') !== -1) {
+ const avatar_px = is_image_in ? (is_image_in.offsetHeight === avatar_size ? '60px' : '20px') : '20px'
+ child.style.paddingLeft = `calc(${desktop_margin} + ${avatar_px})`
+ }
// Remove previous elements with the same formatted timestamp
cleanPreviousTimestamps(child, c);
}
@@ -449,21 +381,63 @@
}
/**
- * Update a data transfer interaction
+ * Build a new file interaction
+ * @param message_id
*/
- function updateDataTransferInteraction(message_div, message_object) {
- 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>',
- 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>',
- 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>',
- 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>',
- 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>',
- 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>';
+ function fileInteraction(message_id) {
+ var message_wrapper = document.createElement('div')
+ message_wrapper.setAttribute('class', 'message_wrapper')
+
+ // An file interaction contains buttons at the left of the interaction
+ // for the status or accept/refuse
+ var left_buttons = document.createElement('div')
+ left_buttons.setAttribute('class', 'left_buttons')
+ message_wrapper.appendChild(left_buttons)
+ // Also contains a bold clickable text
+ var text_div = document.createElement('div')
+ text_div.setAttribute('class', 'text')
+ text_div.addEventListener('click', function (event) {
+ // ask ring to open the file
+ const filename = document.querySelector('#message_' + message_id + ' .full').innerText
+ window.prompt('OPEN_FILE:' + filename)
+ });
+ var full_div = document.createElement('div')
+ full_div.setAttribute('class', 'full')
+ full_div.style = 'visibility: hidden; display: none;'
+ var message_text = document.createElement('div')
+ message_text.setAttribute('class', 'filename')
+ // And some informations like size or error message.
+ var informations_div = document.createElement('div')
+ informations_div.setAttribute('class', 'informations')
+ text_div.appendChild(message_text)
+ text_div.appendChild(full_div)
+ text_div.appendChild(informations_div)
+ message_wrapper.appendChild(text_div)
+ // And finally, a progress bar
+ message_transfer_progress_bar = document.createElement('span')
+ message_transfer_progress_bar.setAttribute('class', 'message_progress_bar')
+ message_transfer_progress_completion = document.createElement('span')
+ message_transfer_progress_bar.appendChild(message_transfer_progress_completion)
+ message_wrapper.appendChild(message_transfer_progress_bar)
+ return message_wrapper
+ }
+
+ /**
+ * Update a file interaction (icons + filename + status + progress bar)
+ * @param message_div the message to update
+ * @param message_object new informations
+ */
+ function updateFileInteraction(message_div, message_object) {
+ var acceptSvg = '<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><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>',
+ refuseSvg = '<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><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>',
+ fileSvg = '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><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>',
+ warningSvg = '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg>'
var message_delivery_status = message_object["delivery_status"];
var message_direction = message_object["direction"];
var message_id = message_object["id"];
// Set informations text
- var informations_div = message_div.querySelector("#informations");
+ var informations_div = message_div.querySelector(".informations");
var informations_txt = getMessageTimestampText(message_object["timestamp"], true);
if (message_object["totalSize"] && message_object["progress"]) {
if (message_delivery_status === 'finished') {
@@ -477,16 +451,15 @@
informations_div.innerText = informations_txt;
// Update flat buttons
- var left_buttons = message_div.querySelector("#left_buttons");
+ var left_buttons = message_div.querySelector(".left_buttons");
left_buttons.innerHTML = '';
- if (message_delivery_status.indexOf('awaiting') === 0) {
- if (message_direction === 'in') {
+ if (message_delivery_status.indexOf('awaiting') === 0 || message_delivery_status.indexOf('ongoing') === 0) {
+ if (message_direction === 'in' && message_delivery_status.indexOf('ongoing') !== 0) {
// add buttons to accept or refuse a call.
var accept_button = document.createElement('div');
accept_button.innerHTML = acceptSvg;
- accept_button.setAttribute("id", "accept-btn");
accept_button.setAttribute("title", "Accept");
- accept_button.setAttribute("class", "flat-button button-green");
+ accept_button.setAttribute("class", "flat-button accept");
accept_button.onclick = function() {
window.prompt('ACCEPT_FILE:' + message_id);
}
@@ -494,9 +467,8 @@
}
var refuse_button = document.createElement('div');
refuse_button.innerHTML = refuseSvg;
- refuse_button.setAttribute("id", "refuse-btn");
refuse_button.setAttribute("title", "Refuse");
- refuse_button.setAttribute("class", "flat-button button-red");
+ refuse_button.setAttribute("class", "flat-button refuse");
refuse_button.onclick = function() {
window.prompt('REFUSE_FILE:' + message_id);
}
@@ -509,182 +481,344 @@
status_button.innerHTML = statusFile;
status_button.setAttribute("class", "flat-button");
left_buttons.appendChild(status_button);
- var direction_button = document.createElement('div');
- var directionFile = message_direction === 'out' ? outgoingSvg: incomingSvg;
- direction_button.innerHTML = directionFile;
- direction_button.setAttribute("class", "flat-button");
- left_buttons.appendChild(direction_button);
}
- updateProgressBar(chatview_message_transfer_progress_bar, message_object);
+
+ message_div.querySelector('.full').innerText = message_object['text']
+ message_div.querySelector('.filename').innerText = message_object['text'].split('/').pop()
+ updateProgressBar(message_div.querySelector('.message_progress_bar'), message_object);
}
- /**
- * Adds a message to the buffer, or update it if new_message is
- * TRUE
- */
- function addOrUpdateMessage(message_object, new_message, insert_after = true)
- {
- // Properties of the message object
- var message_id = message_object["id"],
- message_text = message_object["text"],
- message_sender = message_object["sender"],
- message_sender_contact_method = message_object["sender_contact_method"],
- message_timestamp = message_object["timestamp"],
- message_direction = message_object["direction"],
- message_type = message_object["type"],
- message_delivery_status = message_object["delivery_status"],
- message_div_classes,
- chatview_message_div,
- chatview_message_wrapper,
- chatview_message_text,
- chatview_message_sender,
- chatview_message_delivery_status,
- chatview_message_timestamp,
- chatview_message_sender_span,
- chatview_message_sender_image,
- chatview_message_div,
- 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>";
-
- var chatview_sentCheckmark = document.createElement('span');
- chatview_sentCheckmark.setAttribute("class", "sent-checkmark");
-
- chatview_message_div = document.querySelector("#message_" + message_id);
- if (new_message)
- {
- message_div_classes = [
- "message",
- "message_" + message_direction,
- "message_type_" + message_type
- ];
-
- chatview_message_div = document.createElement('div');
- chatview_message_div.setAttribute("id", "message_" + message_id);
- chatview_message_div.setAttribute("class", message_div_classes.join(" "));
-
- chatview_message_wrapper = document.createElement('div');
- chatview_message_wrapper.setAttribute("class", "message_wrapper wc");
-
- if (message_type === 'data_transfer') {
- var leftbuttons_div = document.createElement('div');
- leftbuttons_div.setAttribute("id", "left_buttons");
- chatview_message_wrapper.appendChild(leftbuttons_div);
-
- var text_div = document.createElement('div');
- text_div.setAttribute("id", "text");
- text_div.addEventListener('click', function (event) {
- // ask the soft to open the file
- var filename = document.querySelector("#message_" + message_id + " #filename").innerText;
- window.prompt('OPEN_FILE:' + filename);
- });
- chatview_message_text = document.createElement('div');
- chatview_message_text.setAttribute("id", "filename");
- var informations_div = document.createElement('div');
- informations_div.setAttribute("id", "informations");
- text_div.appendChild(chatview_message_text);
- text_div.appendChild(informations_div);
- chatview_message_wrapper.appendChild(text_div);
-
- // DataTransfer progress bar
- chatview_message_transfer_progress_bar = document.createElement('span');
- chatview_message_transfer_progress_bar.setAttribute("class", "message_progress_bar");
- chatview_message_transfer_progress_completion = document.createElement('span');
- chatview_message_transfer_progress_bar.appendChild(chatview_message_transfer_progress_completion);
- chatview_message_wrapper.appendChild(chatview_message_transfer_progress_bar);
- } else {
- chatview_message_text = document.createElement('span');
- chatview_message_text.setAttribute("class", "message_text");
- chatview_message_wrapper.appendChild(chatview_message_text);
- chatview_message_delivery_status = document.createElement('span');
- chatview_message_delivery_status.setAttribute("class", "message_delivery_status");
- }
-
-
- chatview_message_sender = document.createElement('span');
- chatview_message_sender.setAttribute("class", "message_sender");
- chatview_message_wrapper.appendChild(chatview_message_sender);
- chatview_message_sender_span = document.createElement('span');
- chatview_message_sender_span.setAttribute("class", "message_sender_image");
- chatview_message_sender_image = document.createElement('span');
- chatview_message_sender_image.setAttribute("class", "sender_image sender_image_" + message_sender_contact_method);
- chatview_message_timestamp = document.createElement('span');
- chatview_message_timestamp.setAttribute("class", "message_timestamp");
-
- // Append elements to div
- chatview_message_div.appendChild(chatview_message_sender_image);
- chatview_message_div.appendChild(chatview_message_wrapper);
- if (message_type !== 'data_transfer')
- chatview_message_div.appendChild(chatview_message_delivery_status);
-
- // Get timestamp to add
- const formattedTimestamp = getMessageTimestampText(message_timestamp, true);
- // Create the timestamp object
- const date_elt = document.createElement('div');
- date_elt.innerHTML = formattedTimestamp;
- timestamp_div_classes = [
- "timestamp",
- "timestamp_" + message_direction
- ];
- date_elt.setAttribute("class", timestamp_div_classes.join(" "));
- date_elt.setAttribute("message_timestamp", message_timestamp);
- // Remove last timestamp if it's the same<h6></h6>
- if (messages.querySelectorAll(".timestamp"))
- cleanPreviousTimestamps(date_elt, messages.children.length);
-
- // Add message and the new timestamp
- if (insert_after) {
- messages.appendChild(chatview_message_div);
- messages.appendChild(date_elt);
- } else {
- messages.insertBefore(date_elt, messages.firstChild);
- messages.insertBefore(chatview_message_div, messages.firstChild);
- }
- } else {
- if (chatview_message_div) {
- if (message_type === 'data_transfer') {
- chatview_message_text = chatview_message_div.querySelector("#filename");
- } else {
- chatview_message_text = chatview_message_div.querySelector(".message_text");
- chatview_message_delivery_status = chatview_message_div.querySelector(".message_delivery_status");
- }
- chatview_message_timestamp = chatview_message_div.querySelector(".message_timestamp");
- chatview_message_sender = chatview_message_div.querySelector(".message_sender");
- } else {
- console.log('no msg selector.');
- }
- }
-
- // Set the variables
- chatview_message_text.innerHTML = getMessageHtml(message_text);
- if (new_message && displayLinksEnabled)
- displayLinks(chatview_message_text);
- chatview_message_sender.textContent = message_sender + ": ";
- chatview_message_timestamp.textContent = getMessageTimestampText(message_timestamp);
-
- if (message_type === 'data_transfer') {
- updateDataTransferInteraction(chatview_message_div, message_object);
- } else {
- chatview_message_delivery_status.innerHTML = getMessageDeliveryStatusText(message_delivery_status);
- }
-
- // Add timestamp and sent checkmark
- if (new_message) {
- if (message_direction === "out") {
- chatview_sentCheckmark.innerHTML = sentAnimation;
- chatview_message_div.querySelector(".message_wrapper").appendChild(chatview_sentCheckmark);
- }
- chatview_message_div.querySelector(".message_wrapper").appendChild(chatview_message_timestamp);
- }
-
- if (message_direction === "out") {
- if (message_delivery_status === "sent" || message_delivery_status === "finished") {
- chatview_message_div.classList.add("message--sent");
- }
- }
-
- updateTimestamps();
+ /**
+ * Return if a file is an image
+ * @param file
+ */
+ function isImage(file) {
+ return file.match(/\.(jpeg|jpg|gif|png)$/) !== null
}
/**
+ * Return if a file is a youtube video
+ * @param file
+ */
+ function isVideo(file) {
+ const availableProtocols = ['http:', 'https:']
+ const videoHostname = ['youtube.com', 'www.youtube.com', 'youtu.be']
+ const urlParser = document.createElement('a')
+ urlParser.href = file
+ return (availableProtocols.includes(urlParser.protocol) && videoHostname.includes(urlParser.hostname))
+ }
+
+ /**
+ * Try to show an image or a video link (youtube for now)
+ * @param message_id
+ * @param link to show
+ * @param ytid if it's a youtube video
+ */
+ function mediaInteraction(message_id, link, ytid) {
+ // TODO promise?
+ // Try to display images.
+ const media_wrapper = document.createElement('div')
+ media_wrapper.setAttribute('class', 'media_wrapper')
+ const linkElt = document.createElement('a')
+ linkElt.href = link
+ linkElt.style = 'text-decoration: none;'
+ const imageElt = document.createElement('img')
+ // Note, here, we don't check the size of the image.
+ // in the future, we can check the content-type and content-length with a request
+ // and maybe disable svg
+ imageElt.setAttribute('onerror', 'this.style.display=\'none\'')
+ if (ytid) {
+ imageElt.src = `http://img.youtube.com/vi/${ytid}/0.jpg`
+ } else {
+ imageElt.src = link
+ }
+ linkElt.appendChild(imageElt)
+ if (ytid) {
+ const containerElt = document.createElement('div');
+ containerElt.setAttribute('class', 'containerVideo');
+ const playDiv = document.createElement('div')
+ playDiv.setAttribute('class', 'playVideo')
+ playDiv.innerHTML = '<svg fill="#ffffff" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">\
+ <path d="M8 5v14l11-7z"/>\
+ <path d="M0 0h24v24H0z" fill="none"/>\
+ </svg>'
+ linkElt.appendChild(playDiv)
+ containerElt.appendChild(linkElt)
+ media_wrapper.appendChild(containerElt)
+ } else {
+ media_wrapper.appendChild(linkElt)
+ }
+ return media_wrapper
+ }
+
+ /**
+ * Build a new text interaction
+ * @param message_id
+ * @param htmlText the DOM to show
+ */
+ function textInteraction(message_id, htmlText) {
+ const message_wrapper = document.createElement('div')
+ message_wrapper.setAttribute('class', 'message_wrapper')
+ var message_text = document.createElement('span')
+ message_text.setAttribute('class', 'message_text')
+ message_text.innerHTML = htmlText
+ message_wrapper.appendChild(message_text)
+ // TODO STATUS
+ return message_wrapper
+ }
+
+ /**
+ * Update a text interaction (text)
+ * @param message_div the message to update
+ * @param delivery_status the status of the message
+ * @TODO retry button
+ */
+ function updateTextInteraction(message_div, delivery_status) {
+ if (!message_div.querySelector('.message_text')) return // media
+ switch(delivery_status)
+ {
+ case "ongoing":
+ case "sending":
+ var sending_div = message_div.querySelector('.sending')
+ if (!sending_div) {
+ sending_div = document.createElement('div')
+ sending_div.setAttribute('class', 'sending')
+ sending_div.innerHTML = '<svg overflow="hidden" 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>'
+ // add sending animation to message
+ message_div.appendChild(sending_div)
+ }
+ message_div.querySelector('.message_text').style = 'color: #888'
+ break
+ case "failure":
+ // change text color to red
+ message_div.querySelector('.message_wrapper').style = 'background-color: #f3a6a6'
+ message_div.querySelector('.message_text').style = 'color: #000'
+ var failure_div = message_div.querySelector('.failure')
+ if (!failure_div) {
+ failure_div = document.createElement('div')
+ failure_div.setAttribute('class', 'failure')
+ failure_div.innerHTML = '<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>'
+ // add failure animation to message
+ message_div.appendChild(failure_div)
+ }
+ var sending = message_div.querySelector('.sending')
+ if (sending) sending.style.visibility = 'hidden'
+ break
+ case "sent":
+ case "finished":
+ case "unknown":
+ case "read":
+ // change text color to black
+ message_div.querySelector('.message_text').style = 'color: #000'
+ var sending = message_div.querySelector('.sending')
+ if (sending) sending.style.visibility = 'hidden'
+ break
+ default:
+ console.log("getMessageDeliveryStatusText: unknown delivery status: " + delivery_status)
+ break
+ }
+ }
+
+ /**
+ * Build a new interaction (call or contact)
+ * @param message_id
+ */
+ function actionInteraction(message_id) {
+ var message_wrapper = document.createElement('div')
+ message_wrapper.setAttribute('class', 'message_wrapper')
+
+ // An file interaction contains buttons at the left of the interaction
+ // for the status or accept/refuse
+ var left_buttons = document.createElement('div')
+ left_buttons.setAttribute('class', 'left_buttons')
+ message_wrapper.appendChild(left_buttons)
+ // Also contains a bold clickable text
+ var text_div = document.createElement('div')
+ text_div.setAttribute('class', 'text')
+ message_wrapper.appendChild(text_div)
+ return message_wrapper
+ }
+
+ /**
+ * Update a call interaction (icon + text)
+ * @param message_div the message to update
+ * @param message_object new informations
+ */
+ function updateCallInteraction(message_div, message_object) {
+ const outgoingCall = '<svg fill="#219d55" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5z"/></svg>'
+ const callMissed = '<svg fill="#dc2719" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M19.59 7L12 14.59 6.41 9H11V7H3v8h2v-4.59l7 7 9-9z"/></svg>'
+ const outgoingMissed = '<svg fill="#dc2719" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path d="M24 24H0V0h24v24z" id="a"/></defs><clipPath id="b"><use overflow="visible" xlink:href="#a"/></clipPath><path clip-path="url(#b)" d="M3 8.41l9 9 7-7V15h2V7h-8v2h4.59L12 14.59 4.41 7 3 8.41z"/></svg>'
+ const callReceived = '<svg fill="#219d55" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M20 5.41L18.59 4 7 15.59V9H5v10h10v-2H8.41z"/></svg>'
+
+ const message_text = message_object['text']
+ const message_direction = (message_text.toLowerCase().indexOf('incoming') !== -1) ? 'in' : 'out'
+ const missed = message_text.indexOf('Missed') !== -1
+
+ message_div.querySelector('.text').innerText = message_text.substring(2)
+
+ var left_buttons = message_div.querySelector('.left_buttons')
+ left_buttons.innerHTML = ''
+ var status_button = document.createElement('div')
+ var statusFile = ''
+ if (missed)
+ statusFile = (message_direction === 'in') ? callMissed : outgoingMissed
+ else
+ statusFile = (message_direction === 'in') ? callReceived : outgoingCall
+ status_button.innerHTML = statusFile
+ status_button.setAttribute('class', 'flat-button')
+ left_buttons.appendChild(status_button)
+ }
+
+ /**
+ * Update a contact interaction (icon + text)
+ * @param message_div the message to update
+ * @param message_object new informations
+ */
+ function updateContactInteraction(message_div, message_object) {
+ const message_text = message_object['text']
+
+ message_div.querySelector('.text').innerText = message_text
+
+ var left_buttons = message_div.querySelector('.left_buttons')
+ left_buttons.innerHTML = ''
+ var status_button = document.createElement('div')
+ status_button.innerHTML = '<?xml version="1.0" ?><svg height="32" viewBox="0 0 32 32" width="32" xmlns="http://www.w3.org/2000/svg">\
+ <path d="M8.013766 23.107838l0.00465 -0.810382 0.033095 -0.133474c0.1495327 -0.603099 0.50107 -1.099452 1.1411917 -1.611311 0.1690725 -0.135195 0.5493165 -0.388522 0.7866284 -0.524069 1.4449359 -0.825313 3.6497829 -1.439907 5.5693029 -1.552424 0.268992 -0.01577 0.866138 -0.0085 1.131356 0.01373 1.873734 0.157215 3.884055 0.729268 5.262712 1.497544 1.031356 0.574739 1.70347 1.254543 1.937152 1.959316 0.101301 0.305518 0.100213 0.293393 0.105924 1.180132l0.0051 0.791314 -7.990878 0 -7.990878 0 0.00465 -0.810381z" fill="#000000"/>\
+ <path d="M15.641818 15.906151c-0.512638 -0.04757 -0.970838 -0.177847 -1.424519 -0.40503 -0.534623 -0.267715 -0.972855 -0.622072 -1.344489 -1.087162 -0.500329 -0.626146 -0.797007 -1.385268 -0.858271 -2.196087 -0.01377 -0.182277 -0.0068 -0.568261 0.01341 -0.738308 0.137062 -1.155519 0.73576 -2.1602655 1.685808 -2.8291565 1.010233 -0.711264 2.333521 -0.90809 3.519055 -0.523424 0.511977 0.166119 0.9845 0.434474 1.39324 0.791249 0.103105 0.09 0.316993 0.304905 0.402137 0.404056 0.848222 0.9877635 1.16007 2.3144245 0.843044 3.5864705 -0.116312 0.466694 -0.339476 0.947863 -0.621858 1.340798 -0.695617 0.967955 -1.761735 1.568261 -2.95044 1.661324 -0.155481 0.01217 -0.501821 0.0097 -0.657113 -0.0047z" fill="#000000"/>\
+ </svg>'
+ status_button.setAttribute('class', 'flat-button')
+ left_buttons.appendChild(status_button)
+ }
+
+ /**
+ * Add a message to the conversation.
+ * @param message_object to treat
+ * @param new_message if it's a new message or if we need to update
+ * @param insert_after if we want the message at the end or the top of the conversation
+ */
+ function addOrUpdateMessage(message_object, new_message, insert_after = true) {
+ const message_id = message_object['id']
+ const message_type = message_object['type']
+ const message_text = message_object['text']
+ const message_direction = message_object['direction']
+ const message_timestamp = message_object['timestamp']
+ const delivery_status = message_object['delivery_status']
+ const message_sender_contact_method = message_object['sender_contact_method']
+
+ var message_div = document.querySelector('#message_' + message_id);
+ var type = ''
+ if (new_message) {
+ // Build new message
+ var classes = [
+ 'message',
+ `message_${message_direction}`,
+ `message_type_${message_type}`
+ ]
+
+ message_div = document.createElement('div')
+ message_div.setAttribute('id', `message_${message_id}`)
+ message_div.setAttribute('class', classes.join(' '))
+
+ // Build message for each types.
+ // Add sender images if necessary (like if the interaction doesn't take the whole width)
+ const need_sender = (message_type === 'data_transfer' || message_type === 'text');
+ if (need_sender) {
+ var message_sender_image = document.createElement('span')
+ message_sender_image.setAttribute('class', `sender_image sender_image_${message_sender_contact_method}`)
+ message_div.appendChild(message_sender_image)
+ }
+
+ // Build main content
+ if (message_type === 'data_transfer') {
+ type = 'fileInteraction'
+ message_div.append(fileInteraction(message_id))
+ } else if (message_type === 'text') {
+ // TODO add the possibility to update messages (remove and rebuild)
+ const htmlText = getMessageHtml(message_text)
+ if (displayLinksEnabled) {
+ const parser = new DOMParser();
+ const DOMMsg = parser.parseFromString(htmlText, 'text/xml');
+ const links = DOMMsg.querySelectorAll('a');
+ if (DOMMsg.childNodes.length && links.length) {
+ var isTextToShow = (DOMMsg.childNodes[0].childNodes.length > 1)
+ const ytid = (isVideo(message_text))? youtube_id(message_text) : ''
+ if (!isTextToShow && (ytid || isImage(message_text))) {
+ type = 'media'
+ message_div.append(mediaInteraction(message_id, message_text, ytid))
+ }
+ }
+ }
+ if (type !== 'media') {
+ type = 'text'
+ message_div.append(textInteraction(message_id, htmlText))
+ }
+ } else if (message_type === 'call' || message_type === 'contact') {
+ message_div.append(actionInteraction(message_id))
+ } else {
+ const temp = document.createElement('div')
+ temp.innerText = message_type
+ message_div.appendChild(temp)
+ }
+
+ // Get timestamp to add
+ const formattedTimestamp = getMessageTimestampText(message_timestamp, true)
+ // Create the timestamp object
+ const date_elt = document.createElement('div')
+ date_elt.innerText = formattedTimestamp
+ if (message_type === 'call' || message_type === 'contact') {
+ timestamp_div_classes = [
+ 'timestamp',
+ `timestamp_action`
+ ]
+ } else {
+ timestamp_div_classes = [
+ 'timestamp',
+ `timestamp_${message_direction}`
+ ]
+ }
+ date_elt.setAttribute('class', timestamp_div_classes.join(' '))
+ date_elt.setAttribute('message_timestamp', message_timestamp)
+ // Remove last timestamp if it's the same<h6></h6>
+ if (messages.querySelectorAll('.timestamp'))
+ cleanPreviousTimestamps(date_elt, messages.children.length)
+
+ // Add message and the new timestamp
+ if (insert_after) {
+ if (message_type === 'call' || message_type === 'contact') {
+ message_div.querySelector('.message_wrapper').appendChild(date_elt)
+ messages.appendChild(message_div)
+ } else {
+ messages.appendChild(message_div)
+ messages.appendChild(date_elt)
+ }
+ } else {
+ if (message_type === 'call' || message_type === 'contact') {
+ message_div.querySelector('.message_wrapper').appendChild(date_elt)
+ messages.insertBefore(message_div, messages.firstChild)
+ } else {
+ messages.insertBefore(date_elt, messages.firstChild)
+ messages.insertBefore(message_div, messages.firstChild)
+ }
+ }
+ }
+
+ // Update informations if needed
+ if (message_type === 'data_transfer')
+ updateFileInteraction(message_div, message_object)
+ if (message_type === 'text' && message_direction === 'out')
+ // Modify sent status if necessary
+ updateTextInteraction(message_div, delivery_status)
+ if (message_type === 'call')
+ updateCallInteraction(message_div, message_object)
+ if (message_type === 'contact')
+ updateContactInteraction(message_div, message_object)
+
+ // Clean timestamps
+ updateTimestamps();
+ }
+
+
+ /**
* Add a message to the buffer
*/
function addMessage(message_object)
@@ -850,9 +984,6 @@
/* DEV functions */
ring.chatview.dev = (function(){
-
-
-
/**
* Fills the backlog with bogus messages
*/