diff --git a/scripts/system/domainChat/formatting.js b/scripts/system/domainChat/formatting.js index 22ae3ee203..3cd0d9d335 100644 --- a/scripts/system/domainChat/formatting.js +++ b/scripts/system/domainChat/formatting.js @@ -10,158 +10,158 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html const formatting = { - toJSON: function(data) { - if (typeof data == "object") return data; // Already JSON - - try { - const parsedData = JSON.parse(data); - return parsedData; - } catch (e) { - console.log('Failed to convert data to JSON.') - return null; // Could not convert to json, some error; - } - }, - addTimeAndDateStringToPacket: function(packet) { - // Gets the current time and adds it to a given packet - const timeArray = formatting.helpers._timestampArray(packet.timestamp); - packet.timeString = timeArray[0]; - packet.dateString = timeArray[1]; - return packet; - }, - trimPacketToSave: function(packet) { - // Takes a packet, and returns a packet containing only what is needed to save. - let newPacket = { - channel: packet.channel || "", - displayName: packet.displayName || "", - message: packet.message || "", - timestamp: packet.timestamp || formatting.helpers.getTimestamp(), - }; - return newPacket; - }, - parseMessage: async function(message, enableEmbedding) { - const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/; - const overteLocationRegex = /hifi:\/\/[a-zA-Z0-9_-]+\/[-+]?\d*\.?\d+,[+-]?\d*\.?\d+,[+-]?\d*\.?\d+\/[-+]?\d*\.?\d+,[+-]?\d*\.?\d+,[+-]?\d*\.?\d+,[+-]?\d*\.?\d+/; + toJSON: function(data) { + if (typeof data == "object") return data; // Already JSON + + try { + const parsedData = JSON.parse(data); + return parsedData; + } catch (e) { + console.log('Failed to convert data to JSON.') + return null; // Could not convert to json, some error; + } + }, + addTimeAndDateStringToPacket: function(packet) { + // Gets the current time and adds it to a given packet + const timeArray = formatting.helpers._timestampArray(packet.timestamp); + packet.timeString = timeArray[0]; + packet.dateString = timeArray[1]; + return packet; + }, + trimPacketToSave: function(packet) { + // Takes a packet, and returns a packet containing only what is needed to save. + let newPacket = { + channel: packet.channel || "", + displayName: packet.displayName || "", + message: packet.message || "", + timestamp: packet.timestamp || formatting.helpers.getTimestamp(), + }; + return newPacket; + }, + parseMessage: async function(message, enableEmbedding) { + const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/; + const overteLocationRegex = /hifi:\/\/[a-zA-Z0-9_-]+\/[-+]?\d*\.?\d+,[+-]?\d*\.?\d+,[+-]?\d*\.?\d+\/[-+]?\d*\.?\d+,[+-]?\d*\.?\d+,[+-]?\d*\.?\d+,[+-]?\d*\.?\d+/; - let runningMessage = message; // The remaining message that will be parsed - let messageArray = []; // An array of messages that are split up by the formatting functions + let runningMessage = message; // The remaining message that will be parsed + let messageArray = []; // An array of messages that are split up by the formatting functions - const regexPatterns = [ - { type: "url", regex: urlRegex }, - { type: "overteLocation", regex: overteLocationRegex } - ] + const regexPatterns = [ + { type: "url", regex: urlRegex }, + { type: "overteLocation", regex: overteLocationRegex } + ] - while (true) { - let firstMatch = _findFirstMatch(); + while (true) { + let firstMatch = _findFirstMatch(); - if (firstMatch == null) { - // If there is no more text to parse, break out of the loop and return the message array. - // Format any remaining text as a basic 'text' type. - if (runningMessage.trim() != "") messageArray.push({type: 'text', value: runningMessage}); + if (firstMatch == null) { + // If there is no more text to parse, break out of the loop and return the message array. + // Format any remaining text as a basic 'text' type. + if (runningMessage.trim() != "") messageArray.push({type: 'text', value: runningMessage}); - // Append a final 'fill width' to the message text. - messageArray.push({type: 'messageEnd'}); - break; - } + // Append a final 'fill width' to the message text. + messageArray.push({type: 'messageEnd'}); + break; + } - _formatMessage(firstMatch); - } + _formatMessage(firstMatch); + } - // Embed images in the message array. - if (enableEmbedding) { - for (dataChunk of messageArray){ - if (dataChunk.type == 'url'){ - let url = dataChunk.value; + // Embed images in the message array. + if (enableEmbedding) { + for (dataChunk of messageArray){ + if (dataChunk.type == 'url'){ + let url = dataChunk.value; - const res = await formatting.helpers.fetch(url, {method: 'GET'}); // TODO: Replace with 'HEAD' method. https://github.com/overte-org/overte/issues/1273 - const contentType = res.getResponseHeader("content-type"); + const res = await formatting.helpers.fetch(url, {method: 'GET'}); // TODO: Replace with 'HEAD' method. https://github.com/overte-org/overte/issues/1273 + const contentType = res.getResponseHeader("content-type"); - if (contentType.startsWith('image/')) { - messageArray.push({type: 'imageEmbed', value: url}); - continue; - } - if (contentType.startsWith('video/')){ - messageArray.push({type: 'videoEmbed', value: url}); - continue; - } - } - } - } + if (contentType.startsWith('image/')) { + messageArray.push({type: 'imageEmbed', value: url}); + continue; + } + if (contentType.startsWith('video/')){ + messageArray.push({type: 'videoEmbed', value: url}); + continue; + } + } + } + } - return messageArray; + return messageArray; - function _formatMessage(firstMatch){ - let indexOfFirstMatch = firstMatch[0]; - let regex = regexPatterns[firstMatch[1]].regex; + function _formatMessage(firstMatch){ + let indexOfFirstMatch = firstMatch[0]; + let regex = regexPatterns[firstMatch[1]].regex; - let foundMatch = runningMessage.match(regex)[0]; + let foundMatch = runningMessage.match(regex)[0]; - if (runningMessage.substring(0, indexOfFirstMatch) != "") messageArray.push({type: 'text', value: runningMessage.substring(0, indexOfFirstMatch)}); - messageArray.push({type: regexPatterns[firstMatch[1]].type, value: runningMessage.substring(indexOfFirstMatch, indexOfFirstMatch + foundMatch.length)}); - - runningMessage = runningMessage.substring(indexOfFirstMatch + foundMatch.length); // Remove the part of the message we have worked with - } + if (runningMessage.substring(0, indexOfFirstMatch) != "") messageArray.push({type: 'text', value: runningMessage.substring(0, indexOfFirstMatch)}); + messageArray.push({type: regexPatterns[firstMatch[1]].type, value: runningMessage.substring(indexOfFirstMatch, indexOfFirstMatch + foundMatch.length)}); + + runningMessage = runningMessage.substring(indexOfFirstMatch + foundMatch.length); // Remove the part of the message we have worked with + } - function _findFirstMatch(){ - let indexOfFirstMatch = Infinity; - let indexOfRegexPattern = Infinity; + function _findFirstMatch(){ + let indexOfFirstMatch = Infinity; + let indexOfRegexPattern = Infinity; - for (let i = 0; regexPatterns.length > i; i++){ - let indexOfMatch = runningMessage.search(regexPatterns[i].regex); + for (let i = 0; regexPatterns.length > i; i++){ + let indexOfMatch = runningMessage.search(regexPatterns[i].regex); - if (indexOfMatch == -1) continue; // No match found + if (indexOfMatch == -1) continue; // No match found - if (indexOfMatch < indexOfFirstMatch) { - indexOfFirstMatch = indexOfMatch; - indexOfRegexPattern = i; - } - } + if (indexOfMatch < indexOfFirstMatch) { + indexOfFirstMatch = indexOfMatch; + indexOfRegexPattern = i; + } + } - if (indexOfFirstMatch !== Infinity) return [indexOfFirstMatch, indexOfRegexPattern]; // If there was a found match - return null; // No found match - } - }, + if (indexOfFirstMatch !== Infinity) return [indexOfFirstMatch, indexOfRegexPattern]; // If there was a found match + return null; // No found match + } + }, - helpers: { - // Small functions that are used often in the other functions. - _timestampArray: function(timestamp) { - const currentDate = timestamp || formatting.helpers.getTimestamp(); - let timeArray = []; + helpers: { + // Small functions that are used often in the other functions. + _timestampArray: function(timestamp) { + const currentDate = timestamp || formatting.helpers.getTimestamp(); + let timeArray = []; - timeArray.push(new Date(currentDate).toLocaleTimeString(undefined, { - hour12: false, - })); - - timeArray.push(new Date(currentDate).toLocaleDateString(undefined, { - year: "numeric", - month: "long", - day: "numeric", - })); - - return timeArray; - }, - getTimestamp: function(){ - return Date.now(); - }, - fetch: function (url, options = {method: "GET"}) { - return new Promise((resolve, reject) => { - let req = new XMLHttpRequest(); - - req.onreadystatechange = function () { - - if (req.readyState === req.DONE) { - if (req.status === 200) { - resolve(req); - - } else { - console.log("Error", req.status, req.statusText); - reject(); - } - } - }; - - req.open(options.method, url); - req.send(); - }); - } - } + timeArray.push(new Date(currentDate).toLocaleTimeString(undefined, { + hour12: false, + })); + + timeArray.push(new Date(currentDate).toLocaleDateString(undefined, { + year: "numeric", + month: "long", + day: "numeric", + })); + + return timeArray; + }, + getTimestamp: function(){ + return Date.now(); + }, + fetch: function (url, options = {method: "GET"}) { + return new Promise((resolve, reject) => { + let req = new XMLHttpRequest(); + + req.onreadystatechange = function () { + + if (req.readyState === req.DONE) { + if (req.status === 200) { + resolve(req); + + } else { + console.log("Error", req.status, req.statusText); + reject(); + } + } + }; + + req.open(options.method, url); + req.send(); + }); + } + } } diff --git a/scripts/system/domainChat/qml_widgets/TemplateChatMessage.qml b/scripts/system/domainChat/qml_widgets/TemplateChatMessage.qml index 0f97a614ae..124f1888c6 100644 --- a/scripts/system/domainChat/qml_widgets/TemplateChatMessage.qml +++ b/scripts/system/domainChat/qml_widgets/TemplateChatMessage.qml @@ -3,176 +3,176 @@ import QtQuick.Controls 2.15 import QtQuick.Layouts 1.3 Component { - id: template_chat_message + id: template_chat_message - Rectangle { - property int index: delegateIndex + Rectangle { + property int index: delegateIndex - height: Math.max(65, children[1].height + 30) - color: index % 2 === 0 ? "transparent" : Qt.rgba(0.15,0.15,0.15,1) - width: listview.parent.parent.width - Layout.fillWidth: true + height: Math.max(65, children[1].height + 30) + color: index % 2 === 0 ? "transparent" : Qt.rgba(0.15,0.15,0.15,1) + width: listview.parent.parent.width + Layout.fillWidth: true - Item { - width: parent.width - 10 - anchors.horizontalCenter: parent.horizontalCenter - height: 22 + Item { + width: parent.width - 10 + anchors.horizontalCenter: parent.horizontalCenter + height: 22 - TextEdit { - text: delegateUsername; - color: "lightgray"; - readOnly: true; - selectByMouse: true; - selectByKeyboard: true; - } + TextEdit { + text: delegateUsername; + color: "lightgray"; + readOnly: true; + selectByMouse: true; + selectByKeyboard: true; + } - Text { - anchors.right: parent.right; - text: delegateDate; - color: "lightgray"; - } - } + Text { + anchors.right: parent.right; + text: delegateDate; + color: "lightgray"; + } + } - Flow { - anchors.top: parent.children[0].bottom; - width: parent.width; - x: 5 - id: messageBoxFlow + Flow { + anchors.top: parent.children[0].bottom; + width: parent.width; + x: 5 + id: messageBoxFlow - Repeater { - model: delegateText; + Repeater { + model: delegateText; - Item { - width: parent.width; - height: children[0].contentHeight; + Item { + width: parent.width; + height: children[0].contentHeight; - TextEdit { - text: model.value || "" - font.pointSize: 12 - wrapMode: TextEdit.WordWrap - width: parent.width * 0.8 - visible: model.type === 'text' || model.type === 'mention'; - readOnly: true - selectByMouse: true - selectByKeyboard: true + TextEdit { + text: model.value || "" + font.pointSize: 12 + wrapMode: TextEdit.WordWrap + width: parent.width * 0.8 + visible: model.type === 'text' || model.type === 'mention'; + readOnly: true + selectByMouse: true + selectByKeyboard: true - color: { - switch (model.type) { - case "mention": - return "purple"; - default: - return "white"; - } - } - } + color: { + switch (model.type) { + case "mention": + return "purple"; + default: + return "white"; + } + } + } - RowLayout { - width: urlTypeTextDisplay.width; - visible: model.type === 'url'; + RowLayout { + width: urlTypeTextDisplay.width; + visible: model.type === 'url'; - TextEdit { - id: urlTypeTextDisplay; - text: model.value || ""; - font.pointSize: 12; - wrapMode: Text.Wrap; - color: "#4EBAFD"; - font.underline: true; - width: parent.width; - readOnly: true - selectByMouse: true - selectByKeyboard: true + TextEdit { + id: urlTypeTextDisplay; + text: model.value || ""; + font.pointSize: 12; + wrapMode: Text.Wrap; + color: "#4EBAFD"; + font.underline: true; + width: parent.width; + readOnly: true + selectByMouse: true + selectByKeyboard: true - MouseArea { - anchors.fill: parent; + MouseArea { + anchors.fill: parent; - onClicked: { - Window.openWebBrowser(model.value); - } - } - } + onClicked: { + Window.openWebBrowser(model.value); + } + } + } - Text { - text: "🗗"; - font.pointSize: 10; - wrapMode: Text.Wrap; - color: "white"; + Text { + text: "🗗"; + font.pointSize: 10; + wrapMode: Text.Wrap; + color: "white"; - MouseArea { - anchors.fill: parent; + MouseArea { + anchors.fill: parent; - onClicked: { - Qt.openUrlExternally(model.value); - } - } - } - } + onClicked: { + Qt.openUrlExternally(model.value); + } + } + } + } - RowLayout { - visible: model.type === 'overteLocation'; - width: Math.min(messageBoxFlow.width, children[0].children[1].contentWidth + 35); - height: 20; - Layout.leftMargin: 5 - Layout.rightMargin: 5 + RowLayout { + visible: model.type === 'overteLocation'; + width: Math.min(messageBoxFlow.width, children[0].children[1].contentWidth + 35); + height: 20; + Layout.leftMargin: 5 + Layout.rightMargin: 5 - Rectangle { - width: parent.width; - height: 20; - color: "lightgray" - radius: 2; + Rectangle { + width: parent.width; + height: 20; + color: "lightgray" + radius: 2; - Image { - source: "../img/ui/world_black.png" - width: 18; - height: 18; - sourceSize.width: 18 - sourceSize.height: 18 - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: 2 - anchors.rightMargin: 10 - } + Image { + source: "../img/ui/world_black.png" + width: 18; + height: 18; + sourceSize.width: 18 + sourceSize.height: 18 + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 2 + anchors.rightMargin: 10 + } - TextEdit { - text: model.type === 'overteLocation' ? model.value.split('hifi://')[1].split('/')[0] : ''; - color: "black" - font.pointSize: 12 - x: parent.children[0].width + 5; - anchors.verticalCenter: parent.verticalCenter - readOnly: true - selectByMouse: true - selectByKeyboard: true - } + TextEdit { + text: model.type === 'overteLocation' ? model.value.split('hifi://')[1].split('/')[0] : ''; + color: "black" + font.pointSize: 12 + x: parent.children[0].width + 5; + anchors.verticalCenter: parent.verticalCenter + readOnly: true + selectByMouse: true + selectByKeyboard: true + } - MouseArea { - anchors.fill: parent; + MouseArea { + anchors.fill: parent; - onClicked: { - Window.openUrl(model.value); - } - } - } - } + onClicked: { + Window.openUrl(model.value); + } + } + } + } - Item { - Layout.fillWidth: true; - visible: model.type === 'messageEnd'; - } + Item { + Layout.fillWidth: true; + visible: model.type === 'messageEnd'; + } - Item { - visible: model.type === 'imageEmbed'; - width: messageBoxFlow.width; - height: 200 + Item { + visible: model.type === 'imageEmbed'; + width: messageBoxFlow.width; + height: 200 - AnimatedImage { - source: model.type === 'imageEmbed' ? model.value : '' - height: Math.min(sourceSize.height, 200); - fillMode: Image.PreserveAspectFit - } - } + AnimatedImage { + source: model.type === 'imageEmbed' ? model.value : '' + height: Math.min(sourceSize.height, 200); + fillMode: Image.PreserveAspectFit + } + } - } - } - } - } + } + } + } + } } \ No newline at end of file diff --git a/scripts/system/domainChat/qml_widgets/TemplateNotification.qml b/scripts/system/domainChat/qml_widgets/TemplateNotification.qml index 4b9797d7f4..3c4fcdb240 100644 --- a/scripts/system/domainChat/qml_widgets/TemplateNotification.qml +++ b/scripts/system/domainChat/qml_widgets/TemplateNotification.qml @@ -3,39 +3,39 @@ import QtQuick.Controls 2.15 import QtQuick.Layouts 1.3 Component { - id: template_notification + id: template_notification - Rectangle { - color: "#171717" - width: parent.width - height: 40 + Rectangle { + color: "#171717" + width: parent.width + height: 40 - RowLayout { - width: parent.width - height: parent.height + RowLayout { + width: parent.width + height: parent.height - Rectangle { - height: parent.height - width: 5 - color: "#505186" - } + Rectangle { + height: parent.height + width: 5 + color: "#505186" + } - Repeater { - model: delegateText + Repeater { + model: delegateText - TextEdit { - visible: model.value != undefined; - text: model.value || "" - color: "white" - font.pointSize: 12 - readOnly: true - selectByMouse: true - selectByKeyboard: true - height: root.height - wrapMode: Text.Wrap - font.italic: true - } - } - } - } + TextEdit { + visible: model.value != undefined; + text: model.value || "" + color: "white" + font.pointSize: 12 + readOnly: true + selectByMouse: true + selectByKeyboard: true + height: root.height + wrapMode: Text.Wrap + font.italic: true + } + } + } + } }