feat(Chat): display clickable links, sanitize them and XSS protect
Closes #458
This commit is contained in:
parent
6d6f67476b
commit
716258156e
|
@ -21,3 +21,4 @@ npm-shrinkwrap.json
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
TODO
|
||||||
|
|
|
@ -18,7 +18,7 @@ proc renderInline(self: ChatMessageList, elem: TextItem): string =
|
||||||
of "code": result = fmt("<span style=\"background-color: #1a356b; color: #FFFFFF\">{elem.literal}</span> ")
|
of "code": result = fmt("<span style=\"background-color: #1a356b; color: #FFFFFF\">{elem.literal}</span> ")
|
||||||
of "emph": result = fmt("<span style=\"font-style: italic;\">{elem.literal}</span> ")
|
of "emph": result = fmt("<span style=\"font-style: italic;\">{elem.literal}</span> ")
|
||||||
of "strong": result = fmt("<span style=\"font-weight: bold;\">{elem.literal}</span> ")
|
of "strong": result = fmt("<span style=\"font-weight: bold;\">{elem.literal}</span> ")
|
||||||
of "link": result = "TODO: write safe link here: " & elem.destination
|
of "link": result = elem.destination
|
||||||
of "mention": result = fmt("<span style=\"color: #000000;\">{self.mention(elem.literal)}</span> ")
|
of "mention": result = fmt("<span style=\"color: #000000;\">{self.mention(elem.literal)}</span> ")
|
||||||
|
|
||||||
# See render-block in status-react/src/status_im/ui/screens/chat/message/message.cljs
|
# See render-block in status-react/src/status_im/ui/screens/chat/message/message.cljs
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import QtQuick 2.13
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.13
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.13
|
import QtQuick.Controls 2.12
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import Qt.labs.platform 1.1
|
||||||
import "../../../../shared"
|
import "../../../../shared"
|
||||||
|
import "../../../../shared/xss.js" as XSS
|
||||||
import "../../../../imports"
|
import "../../../../imports"
|
||||||
import "../components"
|
import "../components"
|
||||||
|
|
||||||
|
@ -36,6 +39,23 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function linkify(inputText) {
|
||||||
|
//URLs starting with http://, https://, or ftp://
|
||||||
|
var replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
|
||||||
|
replacedText = inputText.replace(replacePattern1, "<a href='$1'>$1</a>");
|
||||||
|
|
||||||
|
//URLs starting with "www." (without // before it, or it'd re-link the ones done above).
|
||||||
|
var replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
|
||||||
|
replacedText = replacedText.replace(replacePattern2, "$1<a href='http://$2'>$2</a>");
|
||||||
|
|
||||||
|
replacedText = XSS.filterXSS(replacedText)
|
||||||
|
return replacedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfilePopup {
|
||||||
|
id: profilePopup
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: channelIdentifier
|
id: channelIdentifier
|
||||||
visible: authorCurrentMsg == ""
|
visible: authorCurrentMsg == ""
|
||||||
|
@ -236,7 +256,8 @@ Item {
|
||||||
|
|
||||||
StyledTextEdit {
|
StyledTextEdit {
|
||||||
id: chatText
|
id: chatText
|
||||||
text: message
|
text: linkify(message)
|
||||||
|
textFormat: TextEdit.RichText
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: parent.chatHorizontalPadding
|
anchors.leftMargin: parent.chatHorizontalPadding
|
||||||
anchors.right: message.length > 52 ? parent.right : undefined
|
anchors.right: message.length > 52 ? parent.right : undefined
|
||||||
|
@ -250,7 +271,12 @@ Item {
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
color: !isCurrentUser ? Theme.black : Theme.white
|
color: !isCurrentUser ? Theme.black : Theme.white
|
||||||
visible: contentType == Constants.messageType
|
visible: contentType == Constants.messageType
|
||||||
textFormat: TextEdit.RichText
|
onLinkActivated: Qt.openUrlExternally(link)
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.NoButton // we don't want to eat clicks on the Text
|
||||||
|
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import QtQuick.Controls 2.13
|
||||||
import QtQuick.Layouts 1.13
|
import QtQuick.Layouts 1.13
|
||||||
import "../../../../imports"
|
import "../../../../imports"
|
||||||
import "../../../../shared"
|
import "../../../../shared"
|
||||||
|
import "../../../../shared/xss.js" as XSS
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: helpContainer
|
id: helpContainer
|
||||||
|
@ -11,6 +12,19 @@ Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
function linkify(inputText) {
|
||||||
|
//URLs starting with http://, https://, or ftp://
|
||||||
|
var replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
|
||||||
|
replacedText = inputText.replace(replacePattern1, "<a href='$1'>$1</a>");
|
||||||
|
|
||||||
|
//URLs starting with "www." (without // before it, or it'd re-link the ones done above).
|
||||||
|
var replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
|
||||||
|
replacedText = replacedText.replace(replacePattern2, "$1<a href='http://$2'>$2</a>");
|
||||||
|
|
||||||
|
replacedText = XSS.filterXSS(replacedText)
|
||||||
|
return replacedText;
|
||||||
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
id: element8
|
id: element8
|
||||||
text: qsTr("Help menus: FAQ, Glossary, etc.")
|
text: qsTr("Help menus: FAQ, Glossary, etc.")
|
||||||
|
@ -24,7 +38,7 @@ Item {
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "<a href='https://status.im/docs/FAQs.html'>Frequently asked questions</a>"
|
text: linkify(link)
|
||||||
onLinkActivated: Qt.openUrlExternally(link)
|
onLinkActivated: Qt.openUrlExternally(link)
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue