fix(StatusChatInput): New handler method for large messages

Instead of silently cutting off user text beyond the 2000
char limit, handle it more gracefully within certain limits.

Closes #11767
This commit is contained in:
Shinnok 2023-09-22 17:25:38 +03:00 committed by GitHub
parent 57cfc425fc
commit e532abb448
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 66 additions and 70 deletions

View File

@ -44,8 +44,9 @@ Rectangle {
property bool isImage: false
property bool isEdit: false
property int messageLimit: 2000
property int messageLimitVisible: 200
readonly property int messageLimit: 2000 // actual message limit, we don't allow sending more than that
readonly property int messageLimitSoft: 200 // we start showing a char counter when this no. of chars left in the message
readonly property int messageLimitHard: 20000 // still cut-off attempts to paste beyond this limit, for app usability reasons
property int chatType
@ -55,7 +56,7 @@ Rectangle {
property var fileUrlsAndSources: []
property var imageErrorMessageLocation: StatusChatInput.ImageErrorMessageLocation.Top // TODO: Remove this proeprty?
property var imageErrorMessageLocation: StatusChatInput.ImageErrorMessageLocation.Top // TODO: Remove this property?
property alias suggestions: suggestionsBox
@ -274,7 +275,7 @@ Rectangle {
}
function insertInTextInput(start, text) {
// Repace new lines with entities because `insert` gets rid of them
// Replace new lines with entities because `insert` gets rid of them
messageInputField.insert(start, text.replace(/\n/g, "<br/>"));
}
@ -325,26 +326,33 @@ Rectangle {
}
function onKeyPress(event) {
// get text without HTML formatting
const messageLength = messageInputField.getText(0, messageInputField.length).length;
if (event.modifiers === Qt.NoModifier && (event.key === Qt.Key_Enter || event.key === Qt.Key_Return)) {
if (checkTextInsert()) {
event.accepted = true;
return
}
if (messageInputField.length <= messageLimit) {
if (messageLength <= messageLimit) {
checkForInlineEmojis(true);
control.sendMessage(event)
control.sendMessage(event);
control.hideExtendedArea();
event.accepted = true
event.accepted = true;
return;
}
if (event) {
event.accepted = true
console.error("Attempting to send a message exceeding length limit")
else {
// pop-up a warning message when trying to send a message over the limit
messageLengthLimitTooltip.open();
event.accepted = true;
return;
}
} else if (event.key === Qt.Key_Escape && control.isReply) {
control.isReply = false
event.accepted = true
}
if (event.key === Qt.Key_Escape && control.isReply) {
control.isReply = false;
event.accepted = true;
return;
}
const symbolPressed = event.text.length > 0 &&
@ -427,6 +435,16 @@ Rectangle {
validateImagesAndShowImageArea([clipboardImage])
event.accepted = true
} else if (QClipboardProxy.hasText) {
const clipboardText = Utils.plainText(QClipboardProxy.text)
// prevent repetitive & huge clipboard paste, where huge is total char count > than messageLimitHard
const selectionLength = messageInputField.selectionEnd - messageInputField.selectionStart;
if ((messageLength + clipboardText.length - selectionLength) > control.messageLimitHard)
{
messageLengthLimitTooltip.open();
event.accepted = true;
return;
}
messageInputField.remove(messageInputField.selectionStart, messageInputField.selectionEnd)
// cursor position must be stored in a helper property because setting readonly to true causes change
@ -434,7 +452,6 @@ Rectangle {
d.copyTextStart = messageInputField.cursorPosition
messageInputField.readOnly = true
const clipboardText = Utils.plainText(QClipboardProxy.text)
const copiedText = Utils.plainText(d.copiedTextPlain)
if (copiedText === clipboardText) {
if (d.copiedTextPlain.includes("@")) {
@ -467,6 +484,7 @@ Rectangle {
("<div style='white-space: pre-wrap'>" + StatusQUtils.StringUtils.escapeHtml(QClipboardProxy.text) + "</div>")
: StatusQUtils.Emoji.deparse(QClipboardProxy.html)));
}
event.accepted = true
}
}
@ -529,7 +547,7 @@ Rectangle {
function unwrapSelection(unwrapWith, selectedTextWithFormationChars) {
if (messageInputField.selectionStart - messageInputField.selectionEnd === 0) return
// calulate the new selection start and end positions
// Calculate the new selection start and end positions
var newSelectionStart = messageInputField.selectionStart - unwrapWith.length
var newSelectionEnd = messageInputField.selectionEnd-messageInputField.selectionStart + newSelectionStart
@ -601,51 +619,6 @@ Rectangle {
return result
}
function setFormatInInput(formationFunction, startTag, endTag, formationChar, numFormationChars) {
const inputText = getFormattedText()
const plainInputText = messageInputField.getText(0, messageInputField.length)
let lengthDifference
try {
const result = formationFunction(inputText)
if (!result) {
return
}
const parsed = JSON.parse(result)
let substring
let nbEmojis
parsed.forEach(function (match) {
match[1] += 1
const truncatedInputText = inputText.substring(0, match[1] + numFormationChars)
const truncatedPlainText = plainInputText.substring(0, messageInputField.cursorPosition)
const lengthDifference = truncatedInputText.length - truncatedPlainText.length
nbEmojis = StatusQUtils.Emoji.nbEmojis(truncatedInputText)
match[1] += (nbEmojis * -2)
match[0] += (nbEmojis * -2)
substring = inputText.substring(match[0], match[1])
if (plainInputText.charAt(match[0] - 1) !== formationChar) {
match[0] -= lengthDifference
match[1] -= lengthDifference
} else {
match[1] -= lengthDifference
}
messageInputField.remove(match[0], match[1])
insertInTextInput(match[0], `${startTag}${substring}${endTag}`)
})
} catch (e) {
//
}
}
function onRelease(event) {
if ((event.modifiers & Qt.ControlModifier) || (event.modifiers & Qt.MetaModifier)) // these are likely shortcuts with no meaningful text
return
@ -910,7 +883,7 @@ Rectangle {
control.fileUrlsAndSources = imageArea.imageSource
}
// Use this to validate and show the images. The concatanation of previous selected images is done automatically
// Use this to validate and show the images. The concatenation of previous selected images is done automatically
// Returns true if the images were valid and added
function validateImagesAndShowImageArea(imagePaths) {
const validImages = validateImages(imagePaths)
@ -1068,16 +1041,21 @@ Rectangle {
Rectangle {
id: messageInput
readonly property int defaultInputFieldHeight: 40
Layout.fillWidth: true
implicitHeight: inputLayout.implicitHeight + inputLayout.anchors.topMargin + inputLayout.anchors.bottomMargin
implicitWidth: inputLayout.implicitWidth + inputLayout.anchors.leftMargin + inputLayout.anchors.rightMargin
color: isEdit ? Theme.palette.statusChatInput.secondaryBackgroundColor : Style.current.inputBackground
radius: 20
StatusQ.StatusToolTip {
id: messageLengthLimitTooltip
text: messageInputField.length >= control.messageLimitHard ? qsTr("Please reduce the message length")
: qsTr("Maximum message character count is %n", "", control.messageLimit)
orientation: StatusQ.StatusToolTip.Orientation.Top
timeout: 3000 // show for 3 seconds
}
Component {
id: textFormatMenuComponent
@ -1203,7 +1181,7 @@ Rectangle {
RowLayout {
Layout.fillWidth: true
Layout.minimumHeight: (messageInputField.contentHeight + messageInputField.topPadding + messageInputField.bottomPadding)
Layout.maximumHeight: 112
Layout.maximumHeight: 200
spacing: Style.current.radius
StatusScrollView {
id: inputScrollView
@ -1294,9 +1272,10 @@ Rectangle {
} else {
checkForInlineEmojis()
}
} else {
const removeFrom = (cursorPosition < messageLimit) ? cursorWhenPressed : messageLimit;
} else if (length > control.messageLimitHard) {
const removeFrom = (cursorPosition < messageLimitHard) ? cursorWhenPressed : messageLimitHard;
remove(removeFrom, cursorPosition);
messageLengthLimitTooltip.open();
}
d.updateMentionsPositions()
@ -1400,9 +1379,26 @@ Rectangle {
property int remainingChars: -1
leftPadding: Style.current.halfPadding
rightPadding: Style.current.halfPadding
visible: ((messageInputField.length >= control.messageLimitVisible) && (messageInputField.length <= control.messageLimit))
color: (remainingChars <= messageLimitVisible) ? Style.current.danger : Style.current.textColor
visible: messageInputField.length >= control.messageLimit - control.messageLimitSoft
color: {
if (remainingChars >= 0)
return Style.current.textColor
else
return Style.current.danger
}
text: visible ? remainingChars.toString() : ""
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onEntered: {
messageLengthLimitTooltip.open()
}
onExited: {
messageLengthLimitTooltip.hide()
}
}
}
Row {