2020-08-24 22:01:00 +00:00
|
|
|
import QtGraphicalEffects 1.0
|
|
|
|
import QtQuick 2.7
|
|
|
|
import QtQuick.Controls 2.4
|
|
|
|
import QtQuick.Controls.Material 2.0
|
|
|
|
import QtQuick.Layouts 1.3
|
|
|
|
|
|
|
|
import "../opaque" as Opaque
|
|
|
|
import "../opaque/controls" as Awesome
|
|
|
|
import "../opaque/fonts/Twemoji.js" as T
|
|
|
|
import "../utils.js" as Utils
|
|
|
|
import "../widgets"
|
|
|
|
import "../opaque/theme"
|
|
|
|
import "../const"
|
|
|
|
import "../opaque/fonts"
|
|
|
|
|
|
|
|
|
|
|
|
ColumnLayout {
|
|
|
|
id: root
|
|
|
|
|
|
|
|
property int state: Const.state_disconnected
|
|
|
|
property string authorization: ""
|
|
|
|
|
|
|
|
signal sendClicked(string messageText)
|
|
|
|
|
|
|
|
|
|
|
|
function updateState() {
|
|
|
|
|
|
|
|
// TODO unapproved
|
|
|
|
if (root.authorization == Const.auth_blocked) {
|
|
|
|
statusSeperator.color = Theme.messageStatusBlockedColor
|
|
|
|
//: Peer is blocked
|
|
|
|
statusText.text = qsTr("peer-blocked-message")
|
|
|
|
statusText.color = Theme.messageStatusBlockedTextColor
|
|
|
|
txtMessage.enabled = false
|
|
|
|
btnSend.enabled = false
|
|
|
|
btnAttach.enabled = false
|
|
|
|
btnEmoji.enabled = false
|
|
|
|
} else if ( (Utils.isGroup(gcd.selectedConversation) && root.state == Const.state_synced) || (Utils.isPeer(gcd.selectedConversation) && root.state == Const.state_authenticated) ) {
|
|
|
|
statusSeperator.color = Theme.messageStatusNormalColor
|
|
|
|
statusText.text = ""
|
|
|
|
txtMessage.enabled = true
|
|
|
|
btnSend.enabled = true
|
|
|
|
btnSend.enabled = true
|
|
|
|
btnAttach.enabled = true
|
|
|
|
btnEmoji.enabled = true
|
|
|
|
} else {
|
|
|
|
statusSeperator.color = Theme.messageStatusAlertColor
|
|
|
|
//: Peer is offline, messages can't be delivered right now
|
|
|
|
statusText.text = qsTr("peer-offline-message")
|
|
|
|
statusText.color = Theme.messageStatusAlertTextColor
|
|
|
|
txtMessage.enabled = false
|
|
|
|
btnSend.enabled = false
|
|
|
|
btnSend.enabled = false
|
|
|
|
btnAttach.enabled = false
|
|
|
|
btnEmoji.enabled = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Opaque.EmojiDrawer {
|
|
|
|
id: emojiDrawer
|
|
|
|
Layout.fillWidth: true
|
|
|
|
size: 24 * gcd.themeScale
|
|
|
|
|
|
|
|
onPicked: function(shortcode) {
|
|
|
|
if (!txtMessage.enabled) return
|
|
|
|
txtMessage.insert(txtMessage.cursorPosition, ":" + shortcode + ":")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
id: statusSeperator
|
|
|
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
|
|
|
height: statusText.visible ? statusText.height + (4 * gcd.themeScale) : 3 * gcd.themeScale
|
|
|
|
implicitHeight: height
|
|
|
|
color: Theme.dividerColor
|
|
|
|
|
|
|
|
Opaque.ScalingLabel {
|
|
|
|
id: statusText
|
|
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
text: ""
|
|
|
|
visible: text != ""
|
|
|
|
size: Theme.chatMetaTextSize
|
|
|
|
font.family: Fonts.applicationFontRegular.name
|
|
|
|
font.styleName: "Bold"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RowLayout { // THE BOTTOM DRAWER
|
|
|
|
id: rowDrawer
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
|
|
|
Rectangle { // MESSAGE ENTRY TEXTFIELD
|
|
|
|
id: rectMessage
|
|
|
|
Layout.fillWidth: true
|
|
|
|
Layout.minimumHeight: 120 * gcd.themeScale
|
|
|
|
Layout.maximumHeight: 120 * gcd.themeScale
|
|
|
|
color: Theme.backgroundMainColor
|
|
|
|
|
|
|
|
Opaque.Flickable {
|
|
|
|
id: flkMessage
|
|
|
|
anchors.fill: parent //this does nothing! bug in qt
|
|
|
|
contentWidth: txtMessage.width
|
|
|
|
contentHeight: txtMessage.height + txtma.height
|
|
|
|
|
|
|
|
TextArea {
|
|
|
|
id: txtMessage
|
|
|
|
font.pixelSize: Theme.chatSize * gcd.themeScale
|
|
|
|
text: ""
|
|
|
|
padding: 6 * gcd.themeScale
|
|
|
|
wrapMode: TextEdit.Wrap
|
2020-11-20 19:06:41 +00:00
|
|
|
textFormat: Text.PlainText
|
2020-08-24 22:01:00 +00:00
|
|
|
width: rectMessage.width
|
|
|
|
color: Theme.mainTextColor
|
|
|
|
|
|
|
|
property bool skipOneUpdate: false
|
|
|
|
property int previousCursor
|
|
|
|
|
|
|
|
Keys.onReturnPressed: { // CTRL+ENTER = LINEBREAK // TODO: Broken
|
|
|
|
if ((event.modifiers & Qt.ControlModifier) && gcd.os != "android") {
|
|
|
|
txtMessage.insert(txtMessage.cursorPosition, "<br>")
|
|
|
|
} else if (event.modifiers == Qt.NoModifier) {
|
|
|
|
btnSend.clicked()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// welcome to the emoji parser! it is horrifying code that needs to leave in <img src="[emoji]">
|
|
|
|
// while also stripping any other tag, including other images.
|
|
|
|
// TODO: this probably breaks if people actually do want to paste html
|
|
|
|
onTextChanged: {
|
|
|
|
if (gcd.os == "android") {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// we're taking advantage of TextEdit.getText()'s parsing capability, which means occasionally
|
|
|
|
// passing text into it to be filtered. this prevents recursive calls putting us into an
|
|
|
|
// infinite loop
|
|
|
|
if (skipOneUpdate) {
|
|
|
|
skipOneUpdate = false
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
previousCursor = cursorPosition
|
|
|
|
//console.log("onTextChanged() at position " + previousCursor)
|
|
|
|
|
|
|
|
//console.log("1: " + txtMessage.getText(0, txtMessage.text.length))
|
|
|
|
|
|
|
|
// convert <img> tags back to their emoji form
|
|
|
|
// Then parse out the rest of the HTML
|
|
|
|
var nt = restoreEmoji(txtMessage.text)
|
|
|
|
if (nt != txtMessage.text) {
|
|
|
|
skipOneUpdate = true
|
|
|
|
txtMessage.text = nt
|
|
|
|
}
|
|
|
|
|
|
|
|
//console.log("2: " + txtMessage.getText(0, txtMessage.text.length))
|
|
|
|
var preserveSpaces = txtMessage.text.replace(/<br \/>/g,"[:newline:]");
|
|
|
|
if (preserveSpaces != txtMessage.text) {
|
|
|
|
skipOneUpdate = true
|
|
|
|
txtMessage.text = preserveSpaces
|
|
|
|
}
|
|
|
|
// strip all HTML tags
|
|
|
|
var theText = Utils.htmlEscaped(txtMessage.getText(0, txtMessage.text.length))
|
|
|
|
//console.log("3: " + theText)
|
|
|
|
|
|
|
|
// convert emoji back to <img> tags
|
|
|
|
nt = parse(theText, 10)
|
|
|
|
//console.log("4: " + nt)
|
|
|
|
|
|
|
|
// preserve double spacing
|
|
|
|
nt = nt.replace(/\s\s/g, " ");
|
|
|
|
nt = nt.replace(/\[\:newline\:\]/g, "<br/>");
|
|
|
|
|
|
|
|
// then we actually put the updated text in
|
|
|
|
skipOneUpdate = true
|
|
|
|
txtMessage.text = nt
|
|
|
|
|
|
|
|
txtMessage.cursorPosition = previousCursor
|
|
|
|
|
|
|
|
// autoscroll down only when the scrollbar is already all the way down
|
|
|
|
if (flkMessage.contentY + flkMessage.height >= flkMessage.contentHeight - txtMessage.height && flkMessage.contentHeight > flkMessage.height) {
|
|
|
|
flkMessage.contentY = flkMessage.contentHeight - flkMessage.height
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
id: txtma
|
|
|
|
anchors.top: txtMessage.bottom
|
|
|
|
width: flkMessage.width
|
|
|
|
height: Math.max(rectMessage.height - txtMessage.height, 0)
|
|
|
|
|
|
|
|
onClicked: { txtMessage.focus = true }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ColumnLayout {
|
|
|
|
id: colRight
|
|
|
|
spacing: 0
|
|
|
|
width: 100 * gcd.themeScale
|
|
|
|
|
|
|
|
Layout.minimumWidth: width
|
|
|
|
Layout.preferredWidth: width
|
|
|
|
Layout.maximumWidth: width
|
|
|
|
|
|
|
|
Opaque.Icon { // SEND MESSAGE BUTTON
|
|
|
|
id: btnSend
|
2020-11-20 19:06:41 +00:00
|
|
|
source: gcd.assetPath + "core/send-24px.webp"
|
2020-08-24 22:01:00 +00:00
|
|
|
width: colRight.width
|
|
|
|
height: 50 * gcd.themeScale
|
|
|
|
size: 36 * gcd.themeScale
|
|
|
|
|
|
|
|
backgroundColor: enabled ? Theme.defaultButtonColor : Theme.defaultButtonDisabledColor
|
|
|
|
hilightBackgroundColor: Theme.defaultButtonActiveColor
|
|
|
|
iconColor: Theme.defaultButtonTextColor
|
|
|
|
|
|
|
|
property int nextMessageID: 1
|
|
|
|
|
|
|
|
onClicked: {
|
2020-11-20 19:06:41 +00:00
|
|
|
var txt = txtMessage.text.trim()
|
|
|
|
if (txt.length > 0) {
|
|
|
|
root.sendClicked(txt)
|
2020-08-24 22:01:00 +00:00
|
|
|
}
|
|
|
|
txtMessage.text = ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RowLayout {
|
|
|
|
Layout.alignment: Qt.AlignHCenter
|
|
|
|
spacing: 0
|
|
|
|
|
|
|
|
Opaque.Icon { // EMOJI DRAWER BUTTON
|
|
|
|
id: btnEmoji
|
2020-11-20 19:06:41 +00:00
|
|
|
source: gcd.assetPath + "core/mood-24px.webp"
|
2020-08-24 22:01:00 +00:00
|
|
|
|
|
|
|
size: 25
|
|
|
|
height: 36 * gcd.themeScale
|
|
|
|
width: 48 * gcd.themeScale
|
|
|
|
|
2020-08-31 19:48:43 +00:00
|
|
|
backgroundColor: enabled ? Theme.altButtonColor : Theme.altButtonDisabledColor
|
|
|
|
hilightBackgroundColor: backgroundColor
|
|
|
|
iconColor: enabled ? Theme.altButtonTextColor : Theme.altButtonDisabledTextColor
|
2020-08-24 22:01:00 +00:00
|
|
|
|
|
|
|
onClicked: emojiDrawer.visible ? emojiDrawer.slideclosed() : emojiDrawer.slideopen()
|
|
|
|
}
|
|
|
|
|
|
|
|
Opaque.Icon {
|
|
|
|
id: btnAttach
|
2020-11-20 19:06:41 +00:00
|
|
|
source: gcd.assetPath + "core/attach_file-24px.webp"
|
2020-08-24 22:01:00 +00:00
|
|
|
|
|
|
|
size: 25
|
|
|
|
height: 36 * gcd.themeScale
|
|
|
|
width: 48 * gcd.themeScale
|
|
|
|
|
2020-08-31 19:48:43 +00:00
|
|
|
backgroundColor: enabled ? Theme.altButtonColor : Theme.altButtonDisabledColor
|
|
|
|
hilightBackgroundColor: backgroundColor
|
|
|
|
iconColor: enabled ? Theme.altButtonTextColor : Theme.altButtonDisabledTextColor
|
2020-08-24 22:01:00 +00:00
|
|
|
|
|
|
|
onClicked: {
|
|
|
|
gcd.popup("attachments not yet implemented, sorry")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Connections {
|
|
|
|
target: gcd
|
|
|
|
|
|
|
|
onClearMessages: function() {
|
|
|
|
txtMessage.text = ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|