diff --git a/.gitignore b/.gitignore index 61fec2ad12f7c5875df6fb019a064672b3c04527..fea697405d906f3a833b0d884e3acdad84efe464 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ build/ +build-local/ doc/Doxyfile diff --git a/web/chatview.css b/web/chatview.css index 07be2e560fb59e2ac292594a937e4bfb9ce3e0f2..b0e752e5df46507b0eafdc072ed4f19282e61a0c 100644 --- a/web/chatview.css +++ b/web/chatview.css @@ -1,8 +1,11 @@ body { margin: 0; overflow: hidden; + background-color: #f2f2f2; } +/** TODO invitation bar like Tirifto mock up */ + #invitation { visibility: hidden; width: 100%; @@ -24,12 +27,13 @@ body { justify-content: center; } +.invitation-button, .flat-button { margin: 5px; border: 0; border-radius: 5px; transition: all 0.3s ease; - color: white; + color: #f9f9f9; padding: 10px 20px 10px 20px; vertical-align: middle; } @@ -73,43 +77,6 @@ a:hover { border: 0; } -.message_out #cardWrapper { - display: inline-block; - text-align: right; - width: 100%; -} - -.message_out #cardWrapper a { - border: 0; -} - -.message_out #containerVideo { - float: right; -} - -#playVideo { - background-color: rgba(0, 0, 0, 0.4); - height: 50px; - width: 50px; - border-radius: 5px; - float: right; - position: absolute; - top: calc(50% - 25px); - left: calc(50% - 25px); - z-index: 3; -} - -#containerVideo { - max-width: 350px; - position: relative; -} - -#playVideo svg { - height: 40px; - width: 40px; - margin: 5px; -} - #container { position: relative; height: 100%; @@ -127,16 +94,16 @@ a:hover { width: 100%; overflow-y: scroll; height: auto; + padding-bottom: 1em; max-height: calc(100% - 50px); } #sendMessage { + background-color: white; display: flex; position: relative; - border-top : 2px solid #d3d3d3; - margin-left: 10px; - margin-right: 10px; - padding-left: 8px; + padding-right: 10px; + padding-left: 18px; overflow: hidden; padding-top: 8px; padding-bottom: 8px; @@ -168,25 +135,6 @@ input[placeholder], [placeholder], *[placeholder] { color: #d3d3d3; } -.msg-button { - border-radius: 50%; - border: 0; - width: 40px; - height: 40px; - align-self: center; - transition: all 0.3s ease; -} - -.msg-button.hover, -.msg-button:hover { - background: #bae5f0; -} - -.msg-button.hover svg, -.msg-button:hover svg { - fill: black; -} - #sendMessage svg { fill: #666; padding: 5px; @@ -195,99 +143,18 @@ input[placeholder], [placeholder], *[placeholder] { transition: all 0.3s ease; } +/* General messages */ .wc { will-change: transform; } -.status_circle { - fill: #A0A0A0; - -webkit-animation: circle-dance; - -webkit-animation-duration: 0.8s; - -webkit-animation-iteration-count: infinite; - -webkit-animation-direction: alternate; - -webkit-animation-timing-function: ease-in-out; -} - -.anim-first { - -webkit-animation-delay: 0.7s; -} - -.anim-second { - -webkit-animation-delay: 0.9s; -} - -.anim-third { - -webkit-animation-delay: 1.1s; -} - -@-webkit-keyframes circle-dance { - 0%,50% { - -webkit-transform: translateY(0); - fill: #A0A0A0; - } - 100% { - -webkit-transform: translateY(-8px); - fill: #000; - } -} - -.status-check { - stroke-dasharray: 17; - stroke-dashoffset: 17; - -webkit-animation: dash-check; - -webkit-animation-duration: 0.4s; - -webkit-animation-delay: 0.7s; - -webkit-animation-fill-mode: forwards; - -webkit-animation-timing-function: ease-in-out; - display: none; -} - -.message--sent .status-check { - display: inline-block; -} - -@-webkit-keyframes dash-check{ - from { - stroke-dashoffset: 17; - } - to { - stroke-dashoffset: 0; - } -} - -.status-x { - stroke-dasharray: 12; - stroke-dashoffset: 12; - -webkit-animation: dash-x; - -webkit-animation-duration: 0.2s; - -webkit-animation-fill-mode: forwards; - -webkit-animation-timing-function: ease-in-out; -} - -.x-first { - -webkit-animation-delay: 0.7s; -} - -.x-second { - -webkit-animation-delay: 0.9s; -} - -@-webkit-keyframes dash-x{ - from { - stroke-dashoffset: 12; - } - to { - stroke-dashoffset: 0; - } -} - .message_wrapper { transform: scale(0); max-width: 70%; - margin: 10px 0; + margin: 8px 0 0 0; display: inline-block; - padding: 10px; + padding: 1em; border-radius: 10px; position: relative; -webkit-animation: talk; @@ -306,41 +173,63 @@ input[placeholder], [placeholder], *[placeholder] { overflow: hidden; } -#msg_content img { - max-width: 300px; - align-content: center; +.message_in { + padding-left: 25%; } -#msg_content img:first-of-type { - margin: 5px; +.message_out { + padding-right: 25%; + flex-direction: row-reverse; } -pre { - font : inherit; - font-family : inherit; - font-size : inherit; - font-style : inherit; - font-variant : inherit; - font-weight : inherit; - margin: 0; - padding: 0; +.message_in + .message_in .sender_image { + opacity: 0; } -.message_out { - flex-direction: row-reverse; +.message_out + .message_out .sender_image { + opacity: 0; } -.timestamp -{ - display: flex; - justify-content: flex-start; - color: #333; - font-size: 10px; - padding: 5px; +.message_in > .message_wrapper:before { + content: ""; + width: 10px; + height: 10px; + position: absolute; + left: -10px; + top: 0; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E %3Cpath d= 'M10,0 L0,0 C10,0 10,10 10,10' fill='%23fdfdfd'/%3E %3C/svg%3E"); + transform-origin: top left; } -.timestamp_out { - flex-direction: row-reverse; +.message_in + .message_in > .message_wrapper:before { + display: none; +} + +.message_out + .message_out > .message_wrapper:after { + display: none; +} + +.message_out > .message_wrapper:after { + content: ""; + width: 10px; + height: 10px; + position: absolute; + right: -10px; + top: 0; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E %3Cpath d= 'M0,0 L10,0 C0,0 0,10 0,10' fill='%23cfd8dc'/%3E %3C/svg%3E"); + transform-origin: top right; +} + +.message_delivery_status { + margin: 10px 10px; + color: #A0A0A0; + opacity: 0; + -webkit-animation-name: fade-in; + -webkit-animation-duration: 0.2s; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-delay: 0.4s; + -webkit-animation-fill-mode: forwards; + transition: opacity 0.5s ease-in-out; } .message_sender { @@ -384,7 +273,7 @@ pre { } .message_out > .message_wrapper { - background-color: #cfebf5; + background-color: #cfd8dc; border-top-right-radius: 0; transform-origin: top right; } @@ -394,7 +283,7 @@ pre { } .message_in > .message_wrapper { - background-color: #DEDEE0; + background-color: #fdfdfd; border-top-left-radius: 0; transform-origin: top left; } @@ -403,25 +292,6 @@ pre { border-top-left-radius: 10px; } -.message_timestamp { - color: #333; - font-size: 0.8em; - margin-top: 0.5em; - transition: opacity 0.3s ease-in-out; - line-height: 1em; - display: inline-block; - opacity: 0; - float: right; -} - -.message_wrapper:hover .message_timestamp { - opacity: 1; -} - -.sent-checkmark { - margin-left: 0.313em; - float: right; -} @-webkit-keyframes fade-in { from { @@ -432,11 +302,6 @@ pre { } } -.message_text { - word-wrap: break-word; - display: block; -} - @-webkit-keyframes talk { from { transform: scale(0); @@ -446,173 +311,201 @@ pre { } } -.message_in > .message_wrapper:before { - content: ""; - width: 10px; - height: 10px; - position: absolute; - left: -10px; - top: 0; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E %3Cpath d= 'M10,0 L0,0 C10,0 10,10 10,10' fill='%23DEDEE0'/%3E %3C/svg%3E"); - transform-origin: top left; +.timestamp { + display: flex; + justify-content: flex-start; + color: #333; + font-size: 10px; + padding: 5px; } -.message_in + .message_in > .message_wrapper:before { - display: none; +.timestamp_out { + flex-direction: row-reverse; } -.message_out + .message_out > .message_wrapper:after { - display: none; +/* Buttons */ + +.flat-button { + flex: 1; + padding: 0; } -.message_out > .message_wrapper:after { - content: ""; - width: 10px; - height: 10px; - position: absolute; - right: -10px; - top: 0; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E %3Cpath d= 'M0,0 L10,0 C0,0 0,10 0,10' fill='%23cfebf5'/%3E %3C/svg%3E"); - transform-origin: top right; +.left_buttons { + display: flex; + align-self: center; + max-width: 90px; + padding-left: 1em; } -.message_delivery_status { - margin: 10px 10px; - color: #A0A0A0; - opacity: 0; - -webkit-animation-name: fade-in; - -webkit-animation-duration: 0.2s; - -webkit-animation-timing-function: ease-in-out; - -webkit-animation-delay: 0.4s; - -webkit-animation-fill-mode: forwards; - transition: opacity 0.5s ease-in-out; +.msg-button { + border-radius: 50%; + border: 0; + width: 40px; + height: 40px; + align-self: center; + transition: all 0.3s ease; + cursor: pointer; } -.message_in + .message_in .sender_image { - opacity: 0; +.msg-button.hover, +.msg-button:hover { + background: #bae5f0; } -.message_out + .message_out .sender_image { - opacity: 0; + +/* Status */ + +.status_circle { + fill: #A0A0A0; + -webkit-animation: circle-dance; + -webkit-animation-duration: 0.8s; + -webkit-animation-iteration-count: infinite; + -webkit-animation-direction: alternate; + -webkit-animation-timing-function: ease-in-out; } -.message_type_contact, -.message_type_call { - width: 100%; +.anim-first { + -webkit-animation-delay: 0.7s; } -.message_type_data_transfer .message_timestamp, -.message_type_data_transfer .message_delivery_status, -.message_type_data_transfer .sent-checkmark, -.message_type_call .sent-checkmark, -.message_type_call .message_sender, -.message_type_call .message_delivery_status, -.message_type_call .message_timestamp, -.message_type_call .message_sender_image, -.message_type_call .message_progress_bar, -.message_type_contact .sent-checkmark, -.message_type_contact .message_sender, -.message_type_contact .message_delivery_status, -.message_type_contact .message_timestamp, -.message_type_contact .message_sender_image, -.message_type_contact .message_progress_bar, -.message_type_text .message_progress_bar { - visibility: hidden; - display: none; +.anim-second { + -webkit-animation-delay: 0.9s; +} + +.anim-third { + -webkit-animation-delay: 1.1s; +} + +@-webkit-keyframes circle-dance { + 0%,50% { + -webkit-transform: translateY(0); + fill: #A0A0A0; + } + 100% { + -webkit-transform: translateY(-8px); + fill: #000; + } +} + +.status-x { + stroke-dasharray: 12; + stroke-dashoffset: 12; + -webkit-animation: dash-x; + -webkit-animation-duration: 0.2s; + -webkit-animation-fill-mode: forwards; + -webkit-animation-timing-function: ease-in-out; +} + +.x-first { + -webkit-animation-delay: 0.7s; +} + +.x-second { + -webkit-animation-delay: 0.9s; +} + +@-webkit-keyframes dash-x{ + from { + stroke-dashoffset: 12; + } + to { + stroke-dashoffset: 0; + } +} + +/* Contact + Call interactions */ +.message_type_contact .message_wrapper, +.message_type_call .message_wrapper { + width: auto; + margin-left: 30%; + margin-right: 30%; + display: flex; + flex-wrap: wrap; + background-color: #f2f2f2; + padding: 0; } .message_type_contact .message_wrapper:before, -.message_type_data_transfer .message_wrapper:before, .message_type_call .message_wrapper:before { display: none; } .message_type_call .message_wrapper:after, -.message_type_data_transfer .message_wrapper:after, .message_type_contact .message_wrapper:after { display: none; } -.message_type_call .message_wrapper, -.message_type_contact .message_wrapper { - background: #fff; - width: 100%; - max-width: 100%; - text-align: center; - margin: 0; +.message_type_contact .text, +.message_type_call .text { + align-self: center; + font-size: 1.2em; + padding: 1em; +} + +/* file interactions */ + +.message_type_data_transfer .message_wrapper { padding: 0; - color: #888; - font-style: italic; - border-radius: 0; + width: 30%; + display: flex; + flex-wrap: wrap; } -.message_type_call .message_text, -.message_type_contact .message_text { - overflow: hidden; - text-align: center; - padding-bottom: 5px; - margin-bottom: 5px; - border-bottom: 1px solid #ccc; - margin-left: 20px; +.accept, .refuse { + border-radius: 50%; + cursor: pointer; } -.message_type_call .message_text { - border-bottom: 1px dotted #ccc; +.accept svg, +.refuse svg { + padding: 8px; + width: 24px; + height: 24px; } -#green { - color: green; +.accept { + fill: #219d55; } -#red { - color: red; - font-size: 1.25em; +.accept:hover { + fill: white; + background: #219d55; } -.message_type_data_transfer .message_wrapper -{ - padding: 0; - width: 30%; - display: flex; - flex-wrap: wrap; +.refuse { + fill: #dc2719; } -.message_type_data_transfer #left_buttons -{ - width: 25%; - display: flex; - padding-left: 5px; - overflow: hidden; +.refuse:hover { + fill: white; + background: #dc2719; } -.message_type_data_transfer #text -{ - width: 70%; - text-align: center; - padding-top: 14px; - margin-bottom: 12px; +.message_type_data_transfer .text { + padding: 1em; + text-align: left; + align-self: left; + max-width: calc(100% - 180px); } -.message_type_data_transfer #filename -{ - font-weight: bold; +.message_type_data_transfer .filename { + cursor: pointer; + font-size: 1.1em; overflow: hidden; } -.message_type_data_transfer #informations -{ +.message_type_data_transfer .informations { color: #555; font-size: 0.8em; } .message_progress_bar { - margin-top: 12px; width: 100%; - height: 12px; + height: 1em; position: relative; overflow: hidden; background-color: #eee; - border-radius: 2px; + border-radius: 0; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25) inset; } @@ -624,7 +517,157 @@ pre { overflow: hidden; } -.message_type_data_transfer .flat-button -{ +/* text interactions */ + +.message_type_text .message_wrapper { + max-width: 40%; +} + +.message_type_text .message_text { + padding: 0px; +} + +.message_text { + hyphens: auto; /* When webkitgtk+ will supports this, intelligent word-breaking */ + word-break: break-word; + word-wrap: break-word; + display: block; +} + +pre { + font : inherit; + font-family : inherit; + font-size : inherit; + font-style : inherit; + font-variant : inherit; + font-weight : inherit; + margin: 0; padding: 0; } + +/* Media interactions */ +.media_wrapper img { + max-width: 800px; + max-height: 700px; + margin: 5px 0 0 0; +} + +.playVideo { + background-color: rgba(0, 0, 0, 0.6); + height: 50px; + width: 50px; + border-radius: 5px; + float: right; + position: absolute; + top: calc(50% - 25px); + left: calc(50% - 25px); + z-index: 3; +} + +.containerVideo { + width: 100%; + position: relative; +} + +.playVideo svg { + height: 40px; + width: 40px; + margin: 5px; +} + +/* Text interaction */ +.failure, +.sending { + margin: 10px 10px; + color: #A0A0A0; + opacity: 0; + -webkit-animation-name: fade-in; + -webkit-animation-duration: 0.2s; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-delay: 0.4s; + -webkit-animation-fill-mode: forwards; + transition: opacity 0.5s ease-in-out; +} + +/* Classic screens */ +@media screen and (max-width: 1920px), screen and (max-height: 1080px) { + .message_in { + padding-left: 15%; + } + + .message_out { + padding-right: 15%; + } + + .message_type_text .message_wrapper { + max-width: 60%; + } + + /* File interactions */ + .message_type_data_transfer .message_wrapper { + width: 40%; + } + + /* Media interactions */ + .media_wrapper img { + max-width: 450px; + max-height: 450px; + } +} + +/* lower resolutions */ +@media screen and (max-width: 1000px), screen and (max-height: 480px) { + .message_in { + padding-left: 0; + } + + .message_out { + padding-right: 0; + } + + .message_type_text .message_wrapper { + max-width: 90%; + } + + /* File interactions */ + .message_type_data_transfer .message_wrapper { + width: 70%; + } + + /* Media interactions */ + .media_wrapper img { + max-width: 200px; + max-height: 200px; + } +} + +/* Special case */ +@media screen and (max-width: 300px) { + .sender_image { + visibility: hidden; + display: none; + } + + /* File interactions */ + .message_type_data_transfer .left_buttons { + max-width: 100%; + } + + .message_type_data_transfer .text { + max-width: 100%; + padding-left: 0; + } +} + +.message_type_contact .message_wrapper:hover .timestamp_action, +.message_type_call .message_wrapper:hover .timestamp_action { + opacity: 1; +} + +.timestamp_action { + display: flex; + align-items: center; + justify-content: center; + opacity:0; + transition:visibility 0.3s linear,opacity 0.3s linear; +} diff --git a/web/chatview.html b/web/chatview.html index d534a0e276bea92c87361a3b82f4cde6ef8984b1..cf11b402d9f93d2a3c698dd2b224f941485fa538 100644 --- a/web/chatview.html +++ b/web/chatview.html @@ -10,9 +10,9 @@
-
Accept
-
Refuse
-
Block
+
Accept
+
Refuse
+
Block
@@ -20,14 +20,14 @@
-
+
-
+
- +
@@ -81,6 +81,10 @@ function updateView() { } setInterval(updateView, 60000); +window.onresize = function(event) { + if (ring.chatview) ring.chatview.updateTimestamps(); +}; + var ring = {}; // ring namespace ring.chatview = (function(){ @@ -222,73 +226,6 @@ ring.chatview = (function(){ 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 = '\ - \ - \ - ' - 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 @@ ring.chatview = (function(){ var linkified_message = linkifyHtml(escaped_message, {}); const textPart = document.createElement('pre'); - var linkified_message = linkified_message.replace("📞", "📞") - var linkified_message = linkified_message.replace("🕽", "🕽") textPart.innerHTML = linkified_message; return textPart.outerHTML; @@ -367,39 +302,36 @@ ring.chatview = (function(){ } } - /** - * 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 @@ ring.chatview = (function(){ } /** - * Update a data transfer interaction + * Build a new file interaction + * @param message_id + */ + 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 updateDataTransferInteraction(message_div, message_object) { - var acceptSvg = '', - refuseSvg = '', - fileSvg = '', - warningSvg = '', - incomingSvg = '', - outgoingSvg = ''; + function updateFileInteraction(message_div, message_object) { + var acceptSvg = '', + refuseSvg = '', + fileSvg = '', + warningSvg = '' 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 @@ ring.chatview = (function(){ 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 @@ ring.chatview = (function(){ } 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,181 +481,343 @@ ring.chatview = (function(){ 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 = ""; - - 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"); - } + /** + * 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)) + } - 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
- 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.'); - } - } + /** + * 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 = '\ + \ + \ + ' + linkElt.appendChild(playDiv) + containerElt.appendChild(linkElt) + media_wrapper.appendChild(containerElt) + } else { + media_wrapper.appendChild(linkElt) + } + return media_wrapper + } - // 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); + /** + * 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 + } - if (message_type === 'data_transfer') { - updateDataTransferInteraction(chatview_message_div, message_object); - } else { - chatview_message_delivery_status.innerHTML = getMessageDeliveryStatusText(message_delivery_status); + /** + * 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 = '' + // 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 = '' + // 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 = '' + const callMissed = '' + const outgoingMissed = '' + const callReceived = '' + + 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 = '\ + \ + \ + ' + 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) } - // 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); + // 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)) } - chatview_message_div.querySelector(".message_wrapper").appendChild(chatview_message_timestamp); + } 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) } - if (message_direction === "out") { - if (message_delivery_status === "sent" || message_delivery_status === "finished") { - chatview_message_div.classList.add("message--sent"); - } + // 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
+ 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) + } } + } - updateTimestamps(); + // 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 */ @@ -850,9 +984,6 @@ ring.chatview = (function(){ /* DEV functions */ ring.chatview.dev = (function(){ - - - /** * Fills the backlog with bogus messages */