diff --git a/web/chatview.css b/web/chatview.css index 8883c6eb0bb5829c4b3794236d52d1442ffadb7b..6a6a83c6cf4cbcdfb2e0086db2cb839b1ac7f8b8 100644 --- a/web/chatview.css +++ b/web/chatview.css @@ -345,13 +345,14 @@ a:hover { /* The last message gets a bigger bottom padding so that it is not "glued" to the message bar. */ padding-bottom: 1.5em !important; + margin-top: 4px; } /* General messages */ .internal_mes_wrapper { max-width: 70%; - margin: 8px 0 0 0; + margin: 0px 0 0 0; display: flex; flex-direction: column; /* If a message is smaller (in width) than the timestamp, do not fill @@ -421,7 +422,11 @@ a:hover { .sender_image, .invite_sender_image { border-radius: 50%; - margin: 8px 10px 0px 10px; + margin: 0px 10px 0px 10px; +} + +div.last_message > span { + margin: 0px 10px 0px 10px; } .message_out .message_wrapper { @@ -498,7 +503,7 @@ a:hover { .middle_of_sequence .internal_mes_wrapper, .last_of_sequence .internal_mes_wrapper, .last_message .internal_mes_wrapper { - margin: 3px 0 0 0 !important; + margin: 0px 0 0 0 !important; } .message_out .sender_image { @@ -507,7 +512,7 @@ a:hover { .first_of_sequence.message_out .internal_mes_wrapper, .single_message.message_out .internal_mes_wrapper { - margin: 4px 0 0 0 !important; + margin: 0px 0 0 0 !important; } @-webkit-keyframes fade-in { @@ -520,6 +525,38 @@ a:hover { } } +.sender_image_cell { + vertical-align: bottom; + min-width: 16px; +} + +.dummy_cell { + padding: 0; +} + +.timestamp_cell { + padding: 0; + max-width: 0px; + overflow: visible; + white-space: nowrap; +} + +.timestamp_cell_out { + padding: 0; + text-align: right; + direction: rtl; + max-width: 0px; + overflow: visible; + white-space: nowrap; +} + +table { + border-collapse: collapse; + border-spacing: 0 0px; + margin: 0; + padding: 0; +} + .timestamp { display: inline-flex; justify-content: flex-start; diff --git a/web/chatview.js b/web/chatview.js index 76ee32796b293c809a1a2e377c90698cd596acee..7484fca93fae931c7eba724b8c0f2aba7d9589f4 100644 --- a/web/chatview.js +++ b/web/chatview.js @@ -233,21 +233,21 @@ function formatDate(date) { return date.toLocaleDateString() } if (interval > 1) { - return interval + " days ago" + return interval + "\u200E days ago" } if (interval === 1) { - return interval + " day ago" + return interval + "\u200E day ago" } interval = Math.floor(seconds / 3600) if (interval > 1) { - return interval + " hours ago" + return interval + "\u200E hours ago" } if (interval === 1) { - return interval + " hour ago" + return interval + "\u200E hour ago" } interval = Math.floor(seconds / 60) if (interval > 1) { - return interval + " minutes ago" + return interval + "\u200E minutes ago" } return "just now" } @@ -438,7 +438,7 @@ function isErrorStatus(status) { * Build a new file interaction * @param message_id */ -function fileInteraction(message_id) { +function fileInteraction(message_id, message_direction) { var message_wrapper = document.createElement("div") message_wrapper.setAttribute("class", "message_wrapper") @@ -491,11 +491,44 @@ function fileInteraction(message_id) { const internal_mes_wrapper = document.createElement("div") internal_mes_wrapper.setAttribute("class", "internal_mes_wrapper") - internal_mes_wrapper.appendChild(message_wrapper) + + var tbl = buildMsgTable(message_direction); + var msg_cell = tbl.querySelector(".msg_cell"); + msg_cell.appendChild(message_wrapper); + + internal_mes_wrapper.appendChild(tbl); return internal_mes_wrapper } +function buildMsgTable(message_direction) { + var tbl = document.createElement("table"); + + var row0 = document.createElement("tr"); + var sender_image_cell = document.createElement("td"); + sender_image_cell.setAttribute("class", "sender_image_cell") + var msg_cell = document.createElement("td"); + msg_cell.setAttribute("class", "msg_cell") + + row0.appendChild((message_direction === "in") ? sender_image_cell : msg_cell); + row0.appendChild((message_direction === "in") ? msg_cell : sender_image_cell); + + tbl.appendChild(row0); + + var row1 = document.createElement("tr"); + var dummy_cell = document.createElement("td"); + dummy_cell.setAttribute("class", "dummy_cell") + var timestamp_cell = document.createElement("td"); + timestamp_cell.setAttribute("class", "timestamp_cell") + + row1.appendChild((message_direction === "in") ? dummy_cell : timestamp_cell); + row1.appendChild((message_direction === "in") ? timestamp_cell : dummy_cell); + + tbl.appendChild(row1); + + return tbl; +} + /** * Build information text for passed file interaction message object * @@ -553,13 +586,13 @@ function updateFileInteraction(message_div, message_object, forceTypeToFile = fa media_wrapper.parentNode.removeChild(media_wrapper) } - var new_interaction = fileInteraction(message_id) + var new_interaction = fileInteraction(message_id, message_direction) var new_message_wrapper = new_interaction.querySelector(".message_wrapper") wrapper.prepend(new_message_wrapper) updateFileInteraction(message_div, message_object, true) } - var new_wrapper = mediaInteraction(message_id, message_text, null, errorHandler) + var new_wrapper = mediaInteraction(message_id, message_direction, message_text, null, errorHandler) message_div.insertBefore(new_wrapper, message_div.querySelector(".menu_interaction")) message_div.querySelector("img").id = message_id message_div.querySelector("img").msg_obj = message_object @@ -658,7 +691,7 @@ function buildVideoContainer(linkElt) { * @param ytid if it's a youtube video * @param errorHandler the new media's onerror field will be set to this function */ -function mediaInteraction(message_id, link, ytid, errorHandler) { +function mediaInteraction(message_id, message_direction, link, ytid, errorHandler) { /* TODO promise? Try to display images. */ const media_wrapper = document.createElement("div") @@ -722,7 +755,12 @@ function mediaInteraction(message_id, link, ytid, errorHandler) { const internal_mes_wrapper = document.createElement("div") internal_mes_wrapper.setAttribute("class", "internal_mes_wrapper") - internal_mes_wrapper.appendChild(media_wrapper) + + var tbl = buildMsgTable(message_direction); + var msg_cell = tbl.querySelector(".msg_cell"); + msg_cell.appendChild(media_wrapper); + + internal_mes_wrapper.appendChild(tbl) return internal_mes_wrapper } @@ -732,7 +770,7 @@ function mediaInteraction(message_id, link, ytid, errorHandler) { * @param message_id * @param htmlText the DOM to show */ -function textInteraction(message_id, htmlText) { +function textInteraction(message_id, message_direction, htmlText) { const message_wrapper = document.createElement("div") message_wrapper.setAttribute("class", "message_wrapper") var message_text = document.createElement("div") @@ -743,7 +781,12 @@ function textInteraction(message_id, htmlText) { const internal_mes_wrapper = document.createElement("div") internal_mes_wrapper.setAttribute("class", "internal_mes_wrapper") - internal_mes_wrapper.appendChild(message_wrapper) + + var tbl = buildMsgTable(message_direction); + var msg_cell = tbl.querySelector(".msg_cell"); + msg_cell.appendChild(message_wrapper); + + internal_mes_wrapper.appendChild(tbl); return internal_mes_wrapper } @@ -887,7 +930,7 @@ function removeInteraction(interaction_id) { var timestamp = interaction.querySelector(".timestamp") var previousTimeStamp = interaction.previousSibling.querySelector(".timestamp") if (timestamp && !previousTimeStamp) { - interaction.previousSibling.querySelector(".internal_mes_wrapper").appendChild(timestamp) + interaction.previousSibling.querySelector(".timestamp_cell").appendChild(timestamp) } } @@ -966,13 +1009,6 @@ function buildNewMessage(message_object) { 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") { @@ -989,16 +1025,16 @@ function buildNewMessage(message_object) { media_wrapper.parentNode.removeChild(media_wrapper) } - var new_interaction = fileInteraction(message_id) + var new_interaction = fileInteraction(message_id, message_direction) var new_message_wrapper = new_interaction.querySelector(".message_wrapper") wrapper.prepend(new_message_wrapper) updateFileInteraction(message_div, message_object, true) } - message_div.append(mediaInteraction(message_id, message_text, null, errorHandler)) + message_div.append(mediaInteraction(message_id, message_direction, message_text, null, errorHandler)) message_div.querySelector("img").id = message_id message_div.querySelector("img").msg_obj = message_object } else { - message_div.append(fileInteraction(message_id)) + message_div.append(fileInteraction(message_id, message_direction)) } } else if (message_type === "text") { // TODO add the possibility to update messages (remove and rebuild) @@ -1012,13 +1048,13 @@ function buildNewMessage(message_object) { 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)) + message_div.append(mediaInteraction(message_id, message_direction, message_text, ytid)) } } } if (type !== "media") { type = "text" - message_div.append(textInteraction(message_id, htmlText)) + message_div.append(textInteraction(message_id, message_direction, htmlText)) } } else if (message_type === "call" || message_type === "contact") { message_div.append(actionInteraction()) @@ -1036,6 +1072,19 @@ function buildNewMessage(message_object) { wrapper.insertBefore(message_dropdown, wrapper.firstChild) } + // 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 sender_image_cell = message_div.querySelector(".sender_image_cell") + if (sender_image_cell) { + var message_sender_image = document.createElement("span") + message_sender_image.setAttribute("class", `sender_image sender_image_${message_sender_contact_method}`) + sender_image_cell.appendChild(message_sender_image) + } else { + console.warn("can't find sender_image_cell"); + } + } + return message_div } @@ -1089,7 +1138,9 @@ function addOrUpdateMessage(message_object, new_message, insert_after = true, me message_div.querySelector(".message_wrapper").appendChild(date_elt) } else if (insert_after || !timestamp || timestamp.className !== date_elt.className || timestamp.innerHTML !== date_elt.innerHTML) { - message_div.querySelector(".internal_mes_wrapper").appendChild(date_elt) + message_div.querySelector(".timestamp_cell").appendChild(date_elt) + if (message_direction === "out") + message_div.querySelector(".timestamp_cell").setAttribute("class", "timestamp_cell_out") } var isGenerated = message_type === "call" || message_type === "contact" @@ -1531,7 +1582,7 @@ function setSenderImage(set_sender_image_object) style.type = "text/css" style.id = sender_image_id - style.innerHTML = "." + sender_image_id + " {content: url(data:image/png;base64," + sender_image + ");height: 32px;width: 32px;}" + style.innerHTML = "." + sender_image_id + " {content: url(data:image/png;base64," + sender_image + ");height: 2.25em;width: 2.25em;}" document.head.appendChild(style) invite_style = document.createElement("style")