diff --git a/qml/components/MessageListViewItem.qml b/qml/components/MessageListViewItem.qml
index 203bf09..a2c4341 100644
--- a/qml/components/MessageListViewItem.qml
+++ b/qml/components/MessageListViewItem.qml
@@ -78,6 +78,13 @@ ListItem {
chatView.manuallyScrolledToBottom = false;
}
+ Connections {
+ target: messageOptionsDrawer
+ onCloseRequested: {
+ messageListItem.highlighted = false;
+ }
+ }
+
Loader {
id: contextMenuLoader
active: false
@@ -110,18 +117,21 @@ ListItem {
onClicked: messageListItem.editMessage()
text: qsTr("Edit Message")
}
- MenuItem {
- onClicked: {
- Clipboard.text = Functions.getMessageText(myMessage, true, userInformation.id, true);
- }
- text: qsTr("Copy Message to Clipboard")
- }
MenuItem {
onClicked: {
page.toggleMessageSelection(myMessage);
}
text: qsTr("Select Message")
}
+ MenuItem {
+ onClicked: {
+ messageOptionsDrawer.myMessage = myMessage;
+ messageOptionsDrawer.userInformation = userInformation;
+ messageListItem.highlighted = true;
+ messageOptionsDrawer.open = true;
+ }
+ text: qsTr("More Options...")
+ }
MenuItem {
onClicked: {
if (myMessage.is_pinned) {
diff --git a/qml/pages/ChatPage.qml b/qml/pages/ChatPage.qml
index 6922ab7..477d0e2 100644
--- a/qml/pages/ChatPage.qml
+++ b/qml/pages/ChatPage.qml
@@ -681,1081 +681,1213 @@ Page {
}
}
- SilicaFlickable {
- id: chatContainer
+ Drawer {
+ id: messageOptionsDrawer
- onContentYChanged: {
- // For some strange reason contentY sometimes is > 0 which doesn't make sense without a PushUpMenu (?)
- // That leads to the problem that the whole flickable is moved slightly (or sometimes considerably) up
- // which creates UX issues... As a workaround we are setting it to 0 in such cases.
- // Better solutions are highly appreciated, contributions always welcome! ;)
- if (contentY > 0) {
- contentY = 0;
- }
- }
+ property var myMessage: ({})
+ property var userInformation: ({})
+
+ signal closeRequested();
anchors.fill: parent
- contentHeight: height
- contentWidth: width
+ dock: chatPage.isPortrait ? Dock.Top : Dock.Right
- PullDownMenu {
- visible: chatInformation.id !== chatPage.myUserId && !stickerPickerLoader.active && !voiceNoteOverlayLoader.active && !messageOverlayLoader.active && !stickerSetOverlayLoader.active
- MenuItem {
- id: closeSecretChatMenuItem
- visible: chatPage.isSecretChat && chatPage.secretChatDetails.state["@type"] !== "secretChatStateClosed"
- onClicked: {
- var secretChatId = chatPage.secretChatDetails.id;
- Remorse.popupAction(chatPage, qsTr("Closing chat"), function() { tdLibWrapper.closeSecretChat(secretChatId) });
- }
- text: qsTr("Close Chat")
- }
-
- MenuItem {
- id: joinLeaveChatMenuItem
- visible: (chatPage.isSuperGroup || chatPage.isBasicGroup) && chatGroupInformation && chatGroupInformation.status["@type"] !== "chatMemberStatusBanned"
- onClicked: {
- if (chatPage.userIsMember) {
- var chatId = chatInformation.id;
- Remorse.popupAction(chatPage, qsTr("Leaving chat"), function() {
- tdLibWrapper.leaveChat(chatId);
- // this does not care about the response (ideally type "ok" without further reference) for now
- pageStack.pop(pageStack.find( function(page){ return(page._depth === 0)} ));
- });
- } else {
- tdLibWrapper.joinChat(chatInformation.id);
+ background: SilicaListView {
+ anchors.fill: parent
+ model: ListModel {
+ id: myListModel
+ property var actions: {
+ "copyToClipboard": function() { Clipboard.text = Functions.getMessageText(messageOptionsDrawer.myMessage, true, messageOptionsDrawer.userInformation.id, true) },
+ "pinMessage": function() {
+ if (messageOptionsDrawer.myMessage.is_pinned) {
+ Remorse.popupAction(page, qsTr("Message unpinned"), function() { tdLibWrapper.unpinMessage(chatPage.chatInformation.id, messageOptionsDrawer.myMessage.id);
+ pinnedMessageItem.requestCloseMessage(); } );
+ } else {
+ tdLibWrapper.pinMessage(chatPage.chatInformation.id, messageOptionsDrawer.myMessage.id);
+ }
}
}
- text: chatPage.userIsMember ? qsTr("Leave Chat") : qsTr("Join Chat")
- }
-
- MenuItem {
- id: muteChatMenuItem
- visible: chatPage.userIsMember
- onClicked: {
- var newNotificationSettings = chatInformation.notification_settings;
- if (newNotificationSettings.mute_for > 0) {
- newNotificationSettings.mute_for = 0;
- } else {
- newNotificationSettings.mute_for = 6666666;
- }
- newNotificationSettings.use_default_mute_for = false;
- tdLibWrapper.setChatNotificationSettings(chatInformation.id, newNotificationSettings);
+ property var conditions: {
+ "copyToClipboard": function() { return true; },
+ "pinMessage": function() { return canPinMessages(); }
}
- text: chatInformation.notification_settings.mute_for > 0 ? qsTr("Unmute Chat") : qsTr("Mute Chat")
- }
- MenuItem {
- id: searchInChatMenuItem
- visible: !chatPage.isSecretChat && chatOverviewItem.visible
- onClicked: {
- // This automatically shows the search field as well
- chatOverviewItem.visible = false;
- searchInChatField.focus = true;
+ property var texts: {
+ "copyToClipboard": function() { return qsTr("Copy Message to Clipboard"); },
+ "pinMessage": function() { return messageOptionsDrawer.myMessage.is_pinned ? qsTr("Unpin Message") : qsTr("Pin Message"); }
}
- text: qsTr("Search in Chat")
- }
- }
- BackgroundItem {
- id: headerMouseArea
- height: headerRow.height
- width: parent.width
- onClicked: {
- if (chatPage.isSelecting) {
- chatPage.selectedMessages = [];
- } else {
- pageStack.navigateForward();
+ ListElement {
+ elementActionName: "copyToClipboard"
+ }
+
+ ListElement {
+ elementActionName: "pinMessage"
}
}
- }
- Column {
- id: chatColumn
- width: parent.width
- height: parent.height
-
- Row {
- id: headerRow
- width: parent.width - (3 * Theme.horizontalPageMargin)
- height: chatOverviewItem.height + ( chatPage.isPortrait ? (2 * Theme.paddingMedium) : (2 * Theme.paddingSmall) )
+ header: Row {
+ width: parent.width - ( 4 * Theme.horizontalPageMargin)
+ height: messageOptionsLabel.height + Theme.paddingLarge + ( chatPage.isPortrait ? ( 2 * Theme.paddingSmall ) : 0 )
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.paddingMedium
-
- Item {
- width: chatOverviewItem.height
- height: chatOverviewItem.height
- anchors.bottom: parent.bottom
- anchors.bottomMargin: chatPage.isPortrait ? Theme.paddingMedium : Theme.paddingSmall
-
- ProfileThumbnail {
- id: chatPictureThumbnail
- replacementStringHint: chatNameText.text
- width: parent.height
- height: parent.height
-
- // Setting it directly may cause an stale state for the thumbnail in case the chat page
- // was previously loaded with a picture and now it doesn't have one. Instead setting it
- // when the ChatModel indicates a change. This also avoids flickering when the page is loaded...
- Connections {
- target: chatModel
- onSmallPhotoChanged: {
- chatPictureThumbnail.photoData = chatModel.smallPhoto;
- }
- }
- }
-
- Rectangle {
- id: chatSecretBackground
- color: Theme.rgba(Theme.overlayBackgroundColor, Theme.opacityFaint)
- width: chatPage.isPortrait ? Theme.fontSizeLarge : Theme.fontSizeMedium
- height: width
- anchors.left: parent.left
- anchors.bottom: parent.bottom
- radius: parent.width / 2
- visible: chatPage.isSecretChat
- }
-
- Image {
- source: "image://theme/icon-s-secure"
- width: chatPage.isPortrait ? Theme.fontSizeSmall : Theme.fontSizeExtraSmall
- height: width
- anchors.centerIn: chatSecretBackground
- visible: chatPage.isSecretChat
- }
-
- }
-
- Item {
- id: chatOverviewItem
- opacity: visible ? 1 : 0
- Behavior on opacity { FadeAnimation {} }
- width: parent.width - chatPictureThumbnail.width - Theme.paddingMedium
- height: chatNameText.height + chatStatusText.height
- anchors.bottom: parent.bottom
- anchors.bottomMargin: chatPage.isPortrait ? Theme.paddingMedium : Theme.paddingSmall
- Label {
- id: chatNameText
- width: Math.min(implicitWidth, parent.width)
- anchors.right: parent.right
- text: chatInformation.title !== "" ? Emoji.emojify(chatInformation.title, font.pixelSize) : qsTr("Unknown")
- textFormat: Text.StyledText
- font.pixelSize: chatPage.isPortrait ? Theme.fontSizeLarge : Theme.fontSizeMedium
- font.family: Theme.fontFamilyHeading
- color: Theme.highlightColor
- truncationMode: TruncationMode.Fade
- maximumLineCount: 1
- }
- Label {
- id: chatStatusText
- width: Math.min(implicitWidth, parent.width)
- anchors {
- right: parent.right
- bottom: parent.bottom
- }
- text: ""
- textFormat: Text.StyledText
- font.pixelSize: chatPage.isPortrait ? Theme.fontSizeExtraSmall : Theme.fontSizeTiny
- font.family: Theme.fontFamilyHeading
- color: headerMouseArea.pressed ? Theme.secondaryHighlightColor : Theme.secondaryColor
- truncationMode: TruncationMode.Fade
- maximumLineCount: 1
- }
- }
-
- Item {
- id: searchInChatItem
- visible: !chatOverviewItem.visible
- opacity: visible ? 1 : 0
- Behavior on opacity { FadeAnimation {} }
- width: parent.width - chatPictureThumbnail.width - Theme.paddingMedium
- height: searchInChatField.height
- anchors.bottom: parent.bottom
- anchors.bottomMargin: chatPage.isPortrait ? Theme.paddingSmall : 0
-
- SearchField {
- id: searchInChatField
- visible: false
- width: visible ? parent.width : 0
- placeholderText: qsTr("Search in chat...")
- active: searchInChatItem.visible
- canHide: text === ""
-
- onTextChanged: {
- searchInChatTimer.restart();
- }
-
- onHideClicked: {
- resetFocus();
- }
-
- EnterKey.iconSource: "image://theme/icon-m-enter-close"
- EnterKey.onClicked: {
- resetFocus();
- }
- }
- }
- }
-
- PinnedMessageItem {
- id: pinnedMessageItem
- onRequestShowMessage: {
- messageOverlayLoader.overlayMessage = pinnedMessageItem.pinnedMessage;
- messageOverlayLoader.active = true;
- }
- onRequestCloseMessage: {
- messageOverlayLoader.overlayMessage = undefined;
- messageOverlayLoader.active = false;
- }
- }
-
- Item {
- id: chatViewItem
- width: parent.width
- height: parent.height - headerRow.height - pinnedMessageItem.height - newMessageColumn.height - selectedMessagesActions.height
-
- property int previousHeight;
-
- Component.onCompleted: {
- previousHeight = height;
- }
-
- onHeightChanged: {
- var deltaHeight = previousHeight - height;
- chatView.contentY = chatView.contentY + deltaHeight;
- previousHeight = height;
- }
-
- Timer {
- id: chatViewCooldownTimer
- interval: 2000
- repeat: false
- running: false
- onTriggered: {
- Debug.log("[ChatPage] Cooldown completed...");
- chatView.inCooldown = false;
-
- if (!chatPage.isInitialized) {
- Debug.log("Page is initialized!");
- chatPage.isInitialized = true;
- chatView.handleScrollPositionChanged();
- }
- }
- }
-
- Loader {
- asynchronous: true
- active: chatView.blurred
- anchors.fill: chatView
- sourceComponent: Component {
- FastBlur {
- source: chatView
- radius: Theme.paddingLarge
- }
- }
- }
-
- SilicaListView {
- id: chatView
-
- visible: !blurred
- property bool blurred: messageOverlayLoader.item || stickerPickerLoader.item || voiceNoteOverlayLoader.item || inlineQuery.hasOverlay || stickerSetOverlayLoader.item
-
- anchors.fill: parent
- opacity: chatPage.loading ? 0 : 1
- Behavior on opacity { FadeAnimation {} }
- clip: true
- highlightMoveDuration: 0
- highlightResizeDuration: 0
- property int lastReadSentIndex: -1
- property bool inCooldown: false
- property bool manuallyScrolledToBottom
- property QtObject precalculatedValues: QtObject {
- readonly property alias page: chatPage
- readonly property bool showUserInfo: page.isBasicGroup || ( page.isSuperGroup && !page.isChannel)
- readonly property int profileThumbnailDimensions: showUserInfo ? Theme.itemSizeSmall : 0
- readonly property int pageMarginDouble: 2 * Theme.horizontalPageMargin
- readonly property int paddingMediumDouble: 2 * Theme.paddingMedium
- readonly property int entryWidth: chatView.width - pageMarginDouble
- readonly property int textItemWidth: entryWidth - profileThumbnailDimensions - Theme.paddingSmall
- readonly property int backgroundWidth: page.isChannel ? textItemWidth : textItemWidth - pageMarginDouble
- readonly property int backgroundRadius: textItemWidth/50
- readonly property int textColumnWidth: backgroundWidth - Theme.horizontalPageMargin
- readonly property int messageInReplyToHeight: Theme.fontSizeExtraSmall * 2.571428571 + Theme.paddingSmall;
- readonly property int webPagePreviewHeight: ( (textColumnWidth * 2 / 3) + (6 * Theme.fontSizeExtraSmall) + ( 7 * Theme.paddingSmall) )
- readonly property bool pageIsSelecting: chatPage.isSelecting
-
- }
-
- function handleScrollPositionChanged() {
- Debug.log("Current position: ", chatView.contentY);
- if (chatOverviewItem.visible && chatInformation.unread_count > 0) {
- var bottomIndex = chatView.indexAt(chatView.contentX, ( chatView.contentY + chatView.height - Theme.horizontalPageMargin ));
- if (bottomIndex > -1) {
- viewMessageTimer.queueViewMessage(bottomIndex)
- }
- } else {
- tdLibWrapper.readAllChatMentions(chatInformation.id);
- }
- manuallyScrolledToBottom = chatView.atYEnd
- }
-
- function scrollToIndex(index) {
- if(index > 0 && index < chatView.count) {
- positionViewAtIndex(index, ListView.Contain)
-// currentIndex = index;
- if(index === chatView.count - 1) {
- manuallyScrolledToBottom = true;
- }
- }
- }
-
- onContentYChanged: {
- if (!chatPage.loading && !chatView.inCooldown) {
- if (chatView.indexAt(chatView.contentX, chatView.contentY) < 10) {
- Debug.log("[ChatPage] Trying to get older history items...");
- chatView.inCooldown = true;
- chatModel.triggerLoadMoreHistory();
- } else if (chatOverviewItem.visible && chatView.indexAt(chatView.contentX, chatView.contentY) > ( count - 10)) {
- Debug.log("[ChatPage] Trying to get newer history items...");
- chatView.inCooldown = true;
- chatModel.triggerLoadMoreFuture();
- }
- }
- }
-
- onMovementEnded: {
- handleScrollPositionChanged();
- }
-
- onQuickScrollAnimatingChanged: {
- if (!quickScrollAnimating) {
- handleScrollPositionChanged();
- if(atYEnd) { // handle some false guesses from quick scroll
- chatView.scrollToIndex(chatView.count - 2)
- chatView.scrollToIndex(chatView.count - 1)
- }
- }
- }
-
- model: chatModel
- header: Component {
- Loader {
- active: !!chatPage.botInformation
- && !!chatPage.botInformation.bot_info && chatPage.botInformation.bot_info.description.length > 0
- asynchronous: true
- width: chatView.width
- sourceComponent: Component {
- Label {
- id: botInfoLabel
- topPadding: Theme.paddingLarge
- bottomPadding: Theme.paddingLarge
- leftPadding: Theme.horizontalPageMargin
- rightPadding: Theme.horizontalPageMargin
- text: Emoji.emojify(chatPage.botInformation.bot_info.description, font.pixelSize)
- font.pixelSize: Theme.fontSizeSmall
- color: Theme.highlightColor
- wrapMode: Text.Wrap
- textFormat: Text.StyledText
- horizontalAlignment: Text.AlignHCenter
- onLinkActivated: {
- var chatCommand = Functions.handleLink(link);
- if(chatCommand) {
- tdLibWrapper.sendTextMessage(chatInformation.id, chatCommand);
- }
- }
- linkColor: Theme.primaryColor
- visible: (text !== "")
- }
- }
- }
- }
-
- function getContentComponentHeight(contentType, content, parentWidth) {
- switch(contentType) {
- case "messageAnimation":
- return Functions.getVideoHeight(parentWidth, content.animation);
- case "messageAudio":
- case "messageVoiceNote":
- case "messageDocument":
- return Theme.itemSizeLarge;
- case "messageGame":
- return parentWidth * 0.66666666 + Theme.itemSizeLarge; // 2 / 3;
- case "messageLocation":
- case "messageVenue":
- return parentWidth * 0.66666666; // 2 / 3;
- case "messagePhoto":
- var biggest = content.photo.sizes[content.photo.sizes.length - 1];
- var aspectRatio = biggest.width/biggest.height;
- return Math.max(Theme.itemSizeExtraSmall, Math.min(parentWidth * 0.66666666, parentWidth / aspectRatio));
- case "messagePoll":
- return Theme.itemSizeSmall * (4 + content.poll.options);
- case "messageSticker":
- return content.sticker.height;
- case "messageVideo":
- return Functions.getVideoHeight(parentWidth, content.video);
- case "messageVideoNote":
- return parentWidth
- }
- }
-
- readonly property var delegateMessagesContent: [
- "messageAnimation",
- "messageAudio",
- // "messageContact",
- // "messageDice"
- "messageDocument",
- "messageGame",
- // "messageInvoice",
- "messageLocation",
- // "messagePassportDataSent",
- // "messagePaymentSuccessful",
- "messagePhoto",
- "messagePoll",
- // "messageProximityAlertTriggered",
- "messageSticker",
- "messageVenue",
- "messageVideo",
- "messageVideoNote",
- "messageVoiceNote"
- ]
-
- readonly property var simpleDelegateMessages: ["messageBasicGroupChatCreate",
- "messageChatAddMembers",
- "messageChatChangePhoto",
- "messageChatChangeTitle",
- "messageChatDeleteMember",
- "messageChatDeletePhoto",
- "messageChatJoinByLink",
- "messageChatSetTtl",
- "messageChatUpgradeFrom",
- // "messageContactRegistered","messageExpiredPhoto", "messageExpiredVideo","messageWebsiteConnected"
- "messageGameScore",
- "messageChatUpgradeTo",
- "messageCustomServiceAction",
- "messagePinMessage",
- "messageScreenshotTaken",
- "messageSupergroupChatCreate",
- "messageUnsupported"]
- delegate: Loader {
- width: chatView.width
- Component {
- id: messageListViewItemComponent
- MessageListViewItem {
- precalculatedValues: chatView.precalculatedValues
- chatId: chatModel.chatId
- myMessage: model.display
- messageId: model.message_id
- messageViewCount: model.view_count
- messageIndex: model.index
- hasContentComponent: !!myMessage.content && chatView.delegateMessagesContent.indexOf(model.content_type) > -1
- canReplyToMessage: chatPage.canSendMessages
- onReplyToMessage: {
- newMessageInReplyToRow.inReplyToMessage = myMessage
- newMessageTextField.focus = true
- }
- onEditMessage: {
- newMessageColumn.editMessageId = messageId
- newMessageTextField.text = Functions.getMessageText(myMessage, false, chatPage.myUserId, true)
- newMessageTextField.focus = true
- }
- }
- }
- Component {
- id: messageListViewItemSimpleComponent
- MessageListViewItemSimple {}
- }
- sourceComponent: chatView.simpleDelegateMessages.indexOf(model.content_type) > -1 ? messageListViewItemSimpleComponent : messageListViewItemComponent
- }
- VerticalScrollDecorator {}
-
- ViewPlaceholder {
- id: chatViewPlaceholder
- enabled: chatView.count === 0
- text: (chatPage.isSecretChat && !chatPage.isSecretChatReady) ? qsTr("This secret chat is not yet ready. Your chat partner needs to go online first.") : qsTr("This chat is empty.")
- }
- }
-
- Column {
- width: parent.width
- height: loadingLabel.height + loadingBusyIndicator.height + Theme.paddingMedium
- spacing: Theme.paddingMedium
+ Label {
+ id: messageOptionsLabel
+ text: qsTr("Message Options")
+ color: Theme.highlightColor
+ font.pixelSize: Theme.fontSizeLarge
+ width: parent.width - closeMessageOptionsButton.width - Theme.paddingMedium
anchors.verticalCenter: parent.verticalCenter
+ horizontalAlignment: Text.AlignRight
- opacity: chatPage.loading ? 1 : 0
- Behavior on opacity { FadeAnimation {} }
- visible: chatPage.loading
-
- InfoLabel {
- id: loadingLabel
- text: qsTr("Loading messages...")
- }
-
- BusyIndicator {
- id: loadingBusyIndicator
- anchors.horizontalCenter: parent.horizontalCenter
- running: chatPage.loading
- size: BusyIndicatorSize.Large
+ }
+ IconButton {
+ id: closeMessageOptionsButton
+ icon.source: "image://theme/icon-m-clear"
+ anchors.verticalCenter: parent.verticalCenter
+ onClicked: {
+ messageOptionsDrawer.closeRequested();
+ messageOptionsDrawer.open = false;
}
}
+ }
- Item {
- id: chatUnreadMessagesItem
- width: Theme.fontSizeHuge
- height: Theme.fontSizeHuge
- anchors.right: parent.right
- anchors.rightMargin: Theme.paddingMedium
- anchors.bottom: parent.bottom
- anchors.bottomMargin: Theme.paddingMedium
- visible: !chatPage.loading && chatInformation.unread_count > 0 && chatOverviewItem.visible
- Rectangle {
- id: chatUnreadMessagesCountBackground
- color: Theme.highlightBackgroundColor
- anchors.fill: parent
- radius: width / 2
- visible: chatUnreadMessagesItem.visible
- }
+ delegate: ListItem {
+ width: parent.width
+ visible: myListModel.actions[conditions]()
+ onClicked: {
+ myListModel.actions[elementActionName]();
+ }
+ Label {
+ width: parent.width - ( 2 * Theme.horizontalPageMargin )
+ text: myListModel.texts[elementActionName]();
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.horizontalCenter: parent.horizontalCenter
+ horizontalAlignment: Text.AlignHCenter
+ }
+ }
- Text {
- id: chatUnreadMessagesCount
- font.pixelSize: Theme.fontSizeMedium
- font.bold: true
- color: Theme.primaryColor
- anchors.centerIn: chatUnreadMessagesCountBackground
- visible: chatUnreadMessagesItem.visible
- text: chatInformation.unread_count > 99 ? "99+" : chatInformation.unread_count
+ VerticalScrollDecorator {}
+ }
+
+ SilicaFlickable {
+ id: chatContainer
+
+ onContentYChanged: {
+ // For some strange reason contentY sometimes is > 0 which doesn't make sense without a PushUpMenu (?)
+ // That leads to the problem that the whole flickable is moved slightly (or sometimes considerably) up
+ // which creates UX issues... As a workaround we are setting it to 0 in such cases.
+ // Better solutions are highly appreciated, contributions always welcome! ;)
+ if (contentY > 0) {
+ contentY = 0;
+ }
+ }
+
+ anchors.fill: parent
+ contentHeight: height
+ contentWidth: width
+
+ PullDownMenu {
+ visible: chatInformation.id !== chatPage.myUserId && !stickerPickerLoader.active && !voiceNoteOverlayLoader.active && !messageOverlayLoader.active && !stickerSetOverlayLoader.active
+ MenuItem {
+ id: closeSecretChatMenuItem
+ visible: chatPage.isSecretChat && chatPage.secretChatDetails.state["@type"] !== "secretChatStateClosed"
+ onClicked: {
+ var secretChatId = chatPage.secretChatDetails.id;
+ Remorse.popupAction(chatPage, qsTr("Closing chat"), function() { tdLibWrapper.closeSecretChat(secretChatId) });
}
- MouseArea {
- anchors.fill: parent
- onClicked: {
- chatView.scrollToIndex(chatView.count - 1 - chatInformation.unread_count)
+ text: qsTr("Close Chat")
+ }
+
+ MenuItem {
+ id: joinLeaveChatMenuItem
+ visible: (chatPage.isSuperGroup || chatPage.isBasicGroup) && chatGroupInformation && chatGroupInformation.status["@type"] !== "chatMemberStatusBanned"
+ onClicked: {
+ if (chatPage.userIsMember) {
+ var chatId = chatInformation.id;
+ Remorse.popupAction(chatPage, qsTr("Leaving chat"), function() {
+ tdLibWrapper.leaveChat(chatId);
+ // this does not care about the response (ideally type "ok" without further reference) for now
+ pageStack.pop(pageStack.find( function(page){ return(page._depth === 0)} ));
+ });
+ } else {
+ tdLibWrapper.joinChat(chatInformation.id);
}
}
+ text: chatPage.userIsMember ? qsTr("Leave Chat") : qsTr("Join Chat")
}
- Loader {
- id: stickerPickerLoader
- active: false
- asynchronous: true
- width: parent.width
- height: active ? parent.height : 0
- source: "../components/StickerPicker.qml"
- }
-
- Connections {
- target: stickerPickerLoader.item
- onStickerPicked: {
- Debug.log("Sticker picked: " + stickerId);
- tdLibWrapper.sendStickerMessage(chatInformation.id, stickerId, newMessageColumn.replyToMessageId);
- stickerPickerLoader.active = false;
- attachmentOptionsFlickable.isNeeded = false;
- newMessageInReplyToRow.inReplyToMessage = null;
- newMessageColumn.editMessageId = "0";
- }
- }
-
- Loader {
- id: messageOverlayLoader
-
- property var overlayMessage;
-
- active: false
- asynchronous: true
- width: parent.width
- height: active ? parent.height : 0
- sourceComponent: Component {
- MessageOverlayFlickable {
- overlayMessage: messageOverlayLoader.overlayMessage
- showHeader: !chatPage.isChannel
- onRequestClose: {
- messageOverlayLoader.active = false;
- }
+ MenuItem {
+ id: muteChatMenuItem
+ visible: chatPage.userIsMember
+ onClicked: {
+ var newNotificationSettings = chatInformation.notification_settings;
+ if (newNotificationSettings.mute_for > 0) {
+ newNotificationSettings.mute_for = 0;
+ } else {
+ newNotificationSettings.mute_for = 6666666;
}
+ newNotificationSettings.use_default_mute_for = false;
+ tdLibWrapper.setChatNotificationSettings(chatInformation.id, newNotificationSettings);
}
+ text: chatInformation.notification_settings.mute_for > 0 ? qsTr("Unmute Chat") : qsTr("Mute Chat")
}
- Loader {
- id: voiceNoteOverlayLoader
- active: false
- asynchronous: true
- width: parent.width
- height: active ? parent.height : 0
- source: "../components/VoiceNoteOverlay.qml"
- onActiveChanged: {
- if (!active) {
- fernschreiberUtils.stopRecordingVoiceNote();
- }
+ MenuItem {
+ id: searchInChatMenuItem
+ visible: !chatPage.isSecretChat && chatOverviewItem.visible
+ onClicked: {
+ // This automatically shows the search field as well
+ chatOverviewItem.visible = false;
+ searchInChatField.focus = true;
}
+ text: qsTr("Search in Chat")
}
+ }
- Loader {
- id: stickerSetOverlayLoader
-
- property string stickerSetId;
-
- active: false
- asynchronous: true
- width: parent.width
- height: active ? parent.height : 0
- sourceComponent: Component {
- StickerSetOverlay {
- stickerSetId: stickerSetOverlayLoader.stickerSetId
- onRequestClose: {
- stickerSetOverlayLoader.active = false;
- }
- }
+ BackgroundItem {
+ id: headerMouseArea
+ height: headerRow.height
+ width: parent.width
+ onClicked: {
+ if (chatPage.isSelecting) {
+ chatPage.selectedMessages = [];
+ } else {
+ pageStack.navigateForward();
}
-
- onActiveChanged: {
- if (active) {
- attachmentOptionsFlickable.isNeeded = false;
- }
- }
- }
-
- InlineQuery {
- id: inlineQuery
- textField: newMessageTextField
- chatId: chatInformation.id
}
}
Column {
- id: newMessageColumn
- spacing: Theme.paddingSmall
- topPadding: Theme.paddingSmall + inlineQuery.buttonPadding
- anchors.horizontalCenter: parent.horizontalCenter
- visible: height > 0
- width: parent.width - ( 2 * Theme.horizontalPageMargin )
- height: isNeeded ? implicitHeight : 0
- Behavior on height { SmoothedAnimation { duration: 200 } }
+ id: chatColumn
+ width: parent.width
+ height: parent.height
- readonly property bool isNeeded: !chatPage.isSelecting && chatPage.canSendMessages
- property string replyToMessageId: "0";
- property string editMessageId: "0";
+ Row {
+ id: headerRow
+ width: parent.width - (3 * Theme.horizontalPageMargin)
+ height: chatOverviewItem.height + ( chatPage.isPortrait ? (2 * Theme.paddingMedium) : (2 * Theme.paddingSmall) )
+ anchors.horizontalCenter: parent.horizontalCenter
+ spacing: Theme.paddingMedium
- InReplyToRow {
- onInReplyToMessageChanged: {
- if (inReplyToMessage) {
- newMessageColumn.replyToMessageId = newMessageInReplyToRow.inReplyToMessage.id.toString()
- newMessageInReplyToRow.visible = true;
- } else {
- newMessageInReplyToRow.visible = false;
- newMessageColumn.replyToMessageId = "0";
+ Item {
+ width: chatOverviewItem.height
+ height: chatOverviewItem.height
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: chatPage.isPortrait ? Theme.paddingMedium : Theme.paddingSmall
+
+ ProfileThumbnail {
+ id: chatPictureThumbnail
+ replacementStringHint: chatNameText.text
+ width: parent.height
+ height: parent.height
+
+ // Setting it directly may cause an stale state for the thumbnail in case the chat page
+ // was previously loaded with a picture and now it doesn't have one. Instead setting it
+ // when the ChatModel indicates a change. This also avoids flickering when the page is loaded...
+ Connections {
+ target: chatModel
+ onSmallPhotoChanged: {
+ chatPictureThumbnail.photoData = chatModel.smallPhoto;
+ }
+ }
+ }
+
+ Rectangle {
+ id: chatSecretBackground
+ color: Theme.rgba(Theme.overlayBackgroundColor, Theme.opacityFaint)
+ width: chatPage.isPortrait ? Theme.fontSizeLarge : Theme.fontSizeMedium
+ height: width
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ radius: parent.width / 2
+ visible: chatPage.isSecretChat
+ }
+
+ Image {
+ source: "image://theme/icon-s-secure"
+ width: chatPage.isPortrait ? Theme.fontSizeSmall : Theme.fontSizeExtraSmall
+ height: width
+ anchors.centerIn: chatSecretBackground
+ visible: chatPage.isSecretChat
+ }
+
+ }
+
+ Item {
+ id: chatOverviewItem
+ opacity: visible ? 1 : 0
+ Behavior on opacity { FadeAnimation {} }
+ width: parent.width - chatPictureThumbnail.width - Theme.paddingMedium
+ height: chatNameText.height + chatStatusText.height
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: chatPage.isPortrait ? Theme.paddingMedium : Theme.paddingSmall
+ Label {
+ id: chatNameText
+ width: Math.min(implicitWidth, parent.width)
+ anchors.right: parent.right
+ text: chatInformation.title !== "" ? Emoji.emojify(chatInformation.title, font.pixelSize) : qsTr("Unknown")
+ textFormat: Text.StyledText
+ font.pixelSize: chatPage.isPortrait ? Theme.fontSizeLarge : Theme.fontSizeMedium
+ font.family: Theme.fontFamilyHeading
+ color: Theme.highlightColor
+ truncationMode: TruncationMode.Fade
+ maximumLineCount: 1
+ }
+ Label {
+ id: chatStatusText
+ width: Math.min(implicitWidth, parent.width)
+ anchors {
+ right: parent.right
+ bottom: parent.bottom
+ }
+ text: ""
+ textFormat: Text.StyledText
+ font.pixelSize: chatPage.isPortrait ? Theme.fontSizeExtraSmall : Theme.fontSizeTiny
+ font.family: Theme.fontFamilyHeading
+ color: headerMouseArea.pressed ? Theme.secondaryHighlightColor : Theme.secondaryColor
+ truncationMode: TruncationMode.Fade
+ maximumLineCount: 1
}
}
- editable: true
+ Item {
+ id: searchInChatItem
+ visible: !chatOverviewItem.visible
+ opacity: visible ? 1 : 0
+ Behavior on opacity { FadeAnimation {} }
+ width: parent.width - chatPictureThumbnail.width - Theme.paddingMedium
+ height: searchInChatField.height
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: chatPage.isPortrait ? Theme.paddingSmall : 0
- onClearRequested: {
- newMessageInReplyToRow.inReplyToMessage = null;
+ SearchField {
+ id: searchInChatField
+ visible: false
+ width: visible ? parent.width : 0
+ placeholderText: qsTr("Search in chat...")
+ active: searchInChatItem.visible
+ canHide: text === ""
+
+ onTextChanged: {
+ searchInChatTimer.restart();
+ }
+
+ onHideClicked: {
+ resetFocus();
+ }
+
+ EnterKey.iconSource: "image://theme/icon-m-enter-close"
+ EnterKey.onClicked: {
+ resetFocus();
+ }
+ }
}
-
- id: newMessageInReplyToRow
- myUserId: chatPage.myUserId
- visible: false
}
- Flickable {
- id: attachmentOptionsFlickable
+ PinnedMessageItem {
+ id: pinnedMessageItem
+ onRequestShowMessage: {
+ messageOverlayLoader.overlayMessage = pinnedMessageItem.pinnedMessage;
+ messageOverlayLoader.active = true;
+ }
+ onRequestCloseMessage: {
+ messageOverlayLoader.overlayMessage = undefined;
+ messageOverlayLoader.active = false;
+ }
+ }
- property bool isNeeded: false
- width: chatPage.width
- x: -Theme.horizontalPageMargin
- height: isNeeded && !inlineQuery.userNameIsValid ? attachmentOptionsRow.height : 0
- Behavior on height { SmoothedAnimation { duration: 200 } }
+ Item {
+ id: chatViewItem
+ width: parent.width
+ height: parent.height - headerRow.height - pinnedMessageItem.height - newMessageColumn.height - selectedMessagesActions.height
+
+ property int previousHeight;
+
+ Component.onCompleted: {
+ previousHeight = height;
+ }
+
+ onHeightChanged: {
+ var deltaHeight = previousHeight - height;
+ chatView.contentY = chatView.contentY + deltaHeight;
+ previousHeight = height;
+ }
+
+ Timer {
+ id: chatViewCooldownTimer
+ interval: 2000
+ repeat: false
+ running: false
+ onTriggered: {
+ Debug.log("[ChatPage] Cooldown completed...");
+ chatView.inCooldown = false;
+
+ if (!chatPage.isInitialized) {
+ Debug.log("Page is initialized!");
+ chatPage.isInitialized = true;
+ chatView.handleScrollPositionChanged();
+ }
+ }
+ }
+
+ Loader {
+ asynchronous: true
+ active: chatView.blurred
+ anchors.fill: chatView
+ sourceComponent: Component {
+ FastBlur {
+ source: chatView
+ radius: Theme.paddingLarge
+ }
+ }
+ }
+
+ SilicaListView {
+ id: chatView
+
+ visible: !blurred
+ property bool blurred: messageOverlayLoader.item || stickerPickerLoader.item || voiceNoteOverlayLoader.item || inlineQuery.hasOverlay || stickerSetOverlayLoader.item
+
+ anchors.fill: parent
+ opacity: chatPage.loading ? 0 : 1
+ Behavior on opacity { FadeAnimation {} }
+ clip: true
+ highlightMoveDuration: 0
+ highlightResizeDuration: 0
+ property int lastReadSentIndex: -1
+ property bool inCooldown: false
+ property bool manuallyScrolledToBottom
+ property QtObject precalculatedValues: QtObject {
+ readonly property alias page: chatPage
+ readonly property bool showUserInfo: page.isBasicGroup || ( page.isSuperGroup && !page.isChannel)
+ readonly property int profileThumbnailDimensions: showUserInfo ? Theme.itemSizeSmall : 0
+ readonly property int pageMarginDouble: 2 * Theme.horizontalPageMargin
+ readonly property int paddingMediumDouble: 2 * Theme.paddingMedium
+ readonly property int entryWidth: chatView.width - pageMarginDouble
+ readonly property int textItemWidth: entryWidth - profileThumbnailDimensions - Theme.paddingSmall
+ readonly property int backgroundWidth: page.isChannel ? textItemWidth : textItemWidth - pageMarginDouble
+ readonly property int backgroundRadius: textItemWidth/50
+ readonly property int textColumnWidth: backgroundWidth - Theme.horizontalPageMargin
+ readonly property int messageInReplyToHeight: Theme.fontSizeExtraSmall * 2.571428571 + Theme.paddingSmall;
+ readonly property int webPagePreviewHeight: ( (textColumnWidth * 2 / 3) + (6 * Theme.fontSizeExtraSmall) + ( 7 * Theme.paddingSmall) )
+ readonly property bool pageIsSelecting: chatPage.isSelecting
+
+ }
+
+ function handleScrollPositionChanged() {
+ Debug.log("Current position: ", chatView.contentY);
+ if (chatOverviewItem.visible && chatInformation.unread_count > 0) {
+ var bottomIndex = chatView.indexAt(chatView.contentX, ( chatView.contentY + chatView.height - Theme.horizontalPageMargin ));
+ if (bottomIndex > -1) {
+ viewMessageTimer.queueViewMessage(bottomIndex)
+ }
+ } else {
+ tdLibWrapper.readAllChatMentions(chatInformation.id);
+ }
+ manuallyScrolledToBottom = chatView.atYEnd
+ }
+
+ function scrollToIndex(index) {
+ if(index > 0 && index < chatView.count) {
+ positionViewAtIndex(index, ListView.Contain)
+ // currentIndex = index;
+ if(index === chatView.count - 1) {
+ manuallyScrolledToBottom = true;
+ }
+ }
+ }
+
+ onContentYChanged: {
+ if (!chatPage.loading && !chatView.inCooldown) {
+ if (chatView.indexAt(chatView.contentX, chatView.contentY) < 10) {
+ Debug.log("[ChatPage] Trying to get older history items...");
+ chatView.inCooldown = true;
+ chatModel.triggerLoadMoreHistory();
+ } else if (chatOverviewItem.visible && chatView.indexAt(chatView.contentX, chatView.contentY) > ( count - 10)) {
+ Debug.log("[ChatPage] Trying to get newer history items...");
+ chatView.inCooldown = true;
+ chatModel.triggerLoadMoreFuture();
+ }
+ }
+ }
+
+ onMovementEnded: {
+ handleScrollPositionChanged();
+ }
+
+ onQuickScrollAnimatingChanged: {
+ if (!quickScrollAnimating) {
+ handleScrollPositionChanged();
+ if(atYEnd) { // handle some false guesses from quick scroll
+ chatView.scrollToIndex(chatView.count - 2)
+ chatView.scrollToIndex(chatView.count - 1)
+ }
+ }
+ }
+
+ model: chatModel
+ header: Component {
+ Loader {
+ active: !!chatPage.botInformation
+ && !!chatPage.botInformation.bot_info && chatPage.botInformation.bot_info.description.length > 0
+ asynchronous: true
+ width: chatView.width
+ sourceComponent: Component {
+ Label {
+ id: botInfoLabel
+ topPadding: Theme.paddingLarge
+ bottomPadding: Theme.paddingLarge
+ leftPadding: Theme.horizontalPageMargin
+ rightPadding: Theme.horizontalPageMargin
+ text: Emoji.emojify(chatPage.botInformation.bot_info.description, font.pixelSize)
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.highlightColor
+ wrapMode: Text.Wrap
+ textFormat: Text.StyledText
+ horizontalAlignment: Text.AlignHCenter
+ onLinkActivated: {
+ var chatCommand = Functions.handleLink(link);
+ if(chatCommand) {
+ tdLibWrapper.sendTextMessage(chatInformation.id, chatCommand);
+ }
+ }
+ linkColor: Theme.primaryColor
+ visible: (text !== "")
+ }
+ }
+ }
+ }
+
+ function getContentComponentHeight(contentType, content, parentWidth) {
+ switch(contentType) {
+ case "messageAnimation":
+ return Functions.getVideoHeight(parentWidth, content.animation);
+ case "messageAudio":
+ case "messageVoiceNote":
+ case "messageDocument":
+ return Theme.itemSizeLarge;
+ case "messageGame":
+ return parentWidth * 0.66666666 + Theme.itemSizeLarge; // 2 / 3;
+ case "messageLocation":
+ case "messageVenue":
+ return parentWidth * 0.66666666; // 2 / 3;
+ case "messagePhoto":
+ var biggest = content.photo.sizes[content.photo.sizes.length - 1];
+ var aspectRatio = biggest.width/biggest.height;
+ return Math.max(Theme.itemSizeExtraSmall, Math.min(parentWidth * 0.66666666, parentWidth / aspectRatio));
+ case "messagePoll":
+ return Theme.itemSizeSmall * (4 + content.poll.options);
+ case "messageSticker":
+ return content.sticker.height;
+ case "messageVideo":
+ return Functions.getVideoHeight(parentWidth, content.video);
+ case "messageVideoNote":
+ return parentWidth
+ }
+ }
+
+ readonly property var delegateMessagesContent: [
+ "messageAnimation",
+ "messageAudio",
+ // "messageContact",
+ // "messageDice"
+ "messageDocument",
+ "messageGame",
+ // "messageInvoice",
+ "messageLocation",
+ // "messagePassportDataSent",
+ // "messagePaymentSuccessful",
+ "messagePhoto",
+ "messagePoll",
+ // "messageProximityAlertTriggered",
+ "messageSticker",
+ "messageVenue",
+ "messageVideo",
+ "messageVideoNote",
+ "messageVoiceNote"
+ ]
+
+ readonly property var simpleDelegateMessages: ["messageBasicGroupChatCreate",
+ "messageChatAddMembers",
+ "messageChatChangePhoto",
+ "messageChatChangeTitle",
+ "messageChatDeleteMember",
+ "messageChatDeletePhoto",
+ "messageChatJoinByLink",
+ "messageChatSetTtl",
+ "messageChatUpgradeFrom",
+ // "messageContactRegistered","messageExpiredPhoto", "messageExpiredVideo","messageWebsiteConnected"
+ "messageGameScore",
+ "messageChatUpgradeTo",
+ "messageCustomServiceAction",
+ "messagePinMessage",
+ "messageScreenshotTaken",
+ "messageSupergroupChatCreate",
+ "messageUnsupported"]
+ delegate: Loader {
+ width: chatView.width
+ Component {
+ id: messageListViewItemComponent
+ MessageListViewItem {
+ precalculatedValues: chatView.precalculatedValues
+ chatId: chatModel.chatId
+ myMessage: model.display
+ messageId: model.message_id
+ messageViewCount: model.view_count
+ messageIndex: model.index
+ hasContentComponent: !!myMessage.content && chatView.delegateMessagesContent.indexOf(model.content_type) > -1
+ canReplyToMessage: chatPage.canSendMessages
+ onReplyToMessage: {
+ newMessageInReplyToRow.inReplyToMessage = myMessage
+ newMessageTextField.focus = true
+ }
+ onEditMessage: {
+ newMessageColumn.editMessageId = messageId
+ newMessageTextField.text = Functions.getMessageText(myMessage, false, chatPage.myUserId, true)
+ newMessageTextField.focus = true
+ }
+ }
+ }
+ Component {
+ id: messageListViewItemSimpleComponent
+ MessageListViewItemSimple {}
+ }
+ sourceComponent: chatView.simpleDelegateMessages.indexOf(model.content_type) > -1 ? messageListViewItemSimpleComponent : messageListViewItemComponent
+ }
+ VerticalScrollDecorator {}
+
+ ViewPlaceholder {
+ id: chatViewPlaceholder
+ enabled: chatView.count === 0
+ text: (chatPage.isSecretChat && !chatPage.isSecretChatReady) ? qsTr("This secret chat is not yet ready. Your chat partner needs to go online first.") : qsTr("This chat is empty.")
+ }
+ }
+
+ Column {
+ width: parent.width
+ height: loadingLabel.height + loadingBusyIndicator.height + Theme.paddingMedium
+ spacing: Theme.paddingMedium
+ anchors.verticalCenter: parent.verticalCenter
+
+ opacity: chatPage.loading ? 1 : 0
+ Behavior on opacity { FadeAnimation {} }
+ visible: chatPage.loading
+
+ InfoLabel {
+ id: loadingLabel
+ text: qsTr("Loading messages...")
+ }
+
+ BusyIndicator {
+ id: loadingBusyIndicator
+ anchors.horizontalCenter: parent.horizontalCenter
+ running: chatPage.loading
+ size: BusyIndicatorSize.Large
+ }
+ }
+
+ Item {
+ id: chatUnreadMessagesItem
+ width: Theme.fontSizeHuge
+ height: Theme.fontSizeHuge
+ anchors.right: parent.right
+ anchors.rightMargin: Theme.paddingMedium
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: Theme.paddingMedium
+ visible: !chatPage.loading && chatInformation.unread_count > 0 && chatOverviewItem.visible
+ Rectangle {
+ id: chatUnreadMessagesCountBackground
+ color: Theme.highlightBackgroundColor
+ anchors.fill: parent
+ radius: width / 2
+ visible: chatUnreadMessagesItem.visible
+ }
+
+ Text {
+ id: chatUnreadMessagesCount
+ font.pixelSize: Theme.fontSizeMedium
+ font.bold: true
+ color: Theme.primaryColor
+ anchors.centerIn: chatUnreadMessagesCountBackground
+ visible: chatUnreadMessagesItem.visible
+ text: chatInformation.unread_count > 99 ? "99+" : chatInformation.unread_count
+ }
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ chatView.scrollToIndex(chatView.count - 1 - chatInformation.unread_count)
+ }
+ }
+ }
+
+ Loader {
+ id: stickerPickerLoader
+ active: false
+ asynchronous: true
+ width: parent.width
+ height: active ? parent.height : 0
+ source: "../components/StickerPicker.qml"
+ }
+
+ Connections {
+ target: stickerPickerLoader.item
+ onStickerPicked: {
+ Debug.log("Sticker picked: " + stickerId);
+ tdLibWrapper.sendStickerMessage(chatInformation.id, stickerId, newMessageColumn.replyToMessageId);
+ stickerPickerLoader.active = false;
+ attachmentOptionsFlickable.isNeeded = false;
+ newMessageInReplyToRow.inReplyToMessage = null;
+ newMessageColumn.editMessageId = "0";
+ }
+ }
+
+ Loader {
+ id: messageOverlayLoader
+
+ property var overlayMessage;
+
+ active: false
+ asynchronous: true
+ width: parent.width
+ height: active ? parent.height : 0
+ sourceComponent: Component {
+ MessageOverlayFlickable {
+ overlayMessage: messageOverlayLoader.overlayMessage
+ showHeader: !chatPage.isChannel
+ onRequestClose: {
+ messageOverlayLoader.active = false;
+ }
+ }
+ }
+ }
+
+ Loader {
+ id: voiceNoteOverlayLoader
+ active: false
+ asynchronous: true
+ width: parent.width
+ height: active ? parent.height : 0
+ source: "../components/VoiceNoteOverlay.qml"
+ onActiveChanged: {
+ if (!active) {
+ fernschreiberUtils.stopRecordingVoiceNote();
+ }
+ }
+ }
+
+ Loader {
+ id: stickerSetOverlayLoader
+
+ property string stickerSetId;
+
+ active: false
+ asynchronous: true
+ width: parent.width
+ height: active ? parent.height : 0
+ sourceComponent: Component {
+ StickerSetOverlay {
+ stickerSetId: stickerSetOverlayLoader.stickerSetId
+ onRequestClose: {
+ stickerSetOverlayLoader.active = false;
+ }
+ }
+ }
+
+ onActiveChanged: {
+ if (active) {
+ attachmentOptionsFlickable.isNeeded = false;
+ }
+ }
+ }
+
+ InlineQuery {
+ id: inlineQuery
+ textField: newMessageTextField
+ chatId: chatInformation.id
+ }
+ }
+
+ Column {
+ id: newMessageColumn
+ spacing: Theme.paddingSmall
+ topPadding: Theme.paddingSmall + inlineQuery.buttonPadding
+ anchors.horizontalCenter: parent.horizontalCenter
visible: height > 0
- contentHeight: attachmentOptionsRow.height
- contentWidth: Math.max(width, attachmentOptionsRow.width)
- property bool fadeRight: (attachmentOptionsRow.width-contentX) > width
- property bool fadeLeft: !fadeRight && contentX > 0
- layer.enabled: fadeRight || fadeLeft
- layer.effect: OpacityRampEffectBase {
- direction: attachmentOptionsFlickable.fadeRight ? OpacityRamp.LeftToRight : OpacityRamp.RightToLeft
- source: attachmentOptionsFlickable
- slope: 1 + 6 * (chatPage.width) / Screen.width
- offset: 1 - 1 / slope
+ width: parent.width - ( 2 * Theme.horizontalPageMargin )
+ height: isNeeded ? implicitHeight : 0
+ Behavior on height { SmoothedAnimation { duration: 200 } }
+
+ readonly property bool isNeeded: !chatPage.isSelecting && chatPage.canSendMessages
+ property string replyToMessageId: "0";
+ property string editMessageId: "0";
+
+ InReplyToRow {
+ onInReplyToMessageChanged: {
+ if (inReplyToMessage) {
+ newMessageColumn.replyToMessageId = newMessageInReplyToRow.inReplyToMessage.id.toString()
+ newMessageInReplyToRow.visible = true;
+ } else {
+ newMessageInReplyToRow.visible = false;
+ newMessageColumn.replyToMessageId = "0";
+ }
+ }
+
+ editable: true
+
+ onClearRequested: {
+ newMessageInReplyToRow.inReplyToMessage = null;
+ }
+
+ id: newMessageInReplyToRow
+ myUserId: chatPage.myUserId
+ visible: false
+ }
+
+ Flickable {
+ id: attachmentOptionsFlickable
+
+ property bool isNeeded: false
+ width: chatPage.width
+ x: -Theme.horizontalPageMargin
+ height: isNeeded && !inlineQuery.userNameIsValid ? attachmentOptionsRow.height : 0
+ Behavior on height { SmoothedAnimation { duration: 200 } }
+ visible: height > 0
+ contentHeight: attachmentOptionsRow.height
+ contentWidth: Math.max(width, attachmentOptionsRow.width)
+ property bool fadeRight: (attachmentOptionsRow.width-contentX) > width
+ property bool fadeLeft: !fadeRight && contentX > 0
+ layer.enabled: fadeRight || fadeLeft
+ layer.effect: OpacityRampEffectBase {
+ direction: attachmentOptionsFlickable.fadeRight ? OpacityRamp.LeftToRight : OpacityRamp.RightToLeft
+ source: attachmentOptionsFlickable
+ slope: 1 + 6 * (chatPage.width) / Screen.width
+ offset: 1 - 1 / slope
+ }
+
+
+ Row {
+ id: attachmentOptionsRow
+
+ height: attachImageIconButton.height
+
+ anchors.right: parent.right
+ layoutDirection: Qt.RightToLeft
+ spacing: Theme.paddingMedium
+ leftPadding: Theme.horizontalPageMargin
+ rightPadding: Theme.horizontalPageMargin
+
+ IconButton {
+ id: attachImageIconButton
+ visible: chatPage.hasSendPrivilege("can_send_media_messages")
+ icon.source: "image://theme/icon-m-image"
+ onClicked: {
+ var picker = pageStack.push("Sailfish.Pickers.ImagePickerPage", {
+ allowedOrientations: chatPage.allowedOrientations
+ })
+ picker.selectedContentPropertiesChanged.connect(function(){
+ attachmentOptionsFlickable.isNeeded = false;
+ Debug.log("Selected document: ", picker.selectedContentProperties.filePath );
+ attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
+ attachmentPreviewRow.isPicture = true;
+ controlSendButton();
+ })
+ }
+ }
+ IconButton {
+ visible: chatPage.hasSendPrivilege("can_send_media_messages")
+ icon.source: "image://theme/icon-m-video"
+ onClicked: {
+ var picker = pageStack.push("Sailfish.Pickers.VideoPickerPage", {
+ allowedOrientations: chatPage.allowedOrientations
+ })
+ picker.selectedContentPropertiesChanged.connect(function(){
+ attachmentOptionsFlickable.isNeeded = false;
+ Debug.log("Selected video: ", picker.selectedContentProperties.filePath );
+ attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
+ attachmentPreviewRow.isVideo = true;
+ controlSendButton();
+ })
+ }
+ }
+ IconButton {
+ visible: chatPage.hasSendPrivilege("can_send_media_messages")
+ icon.source: "image://theme/icon-m-mic"
+ icon.sourceSize {
+ width: Theme.iconSizeMedium
+ height: Theme.iconSizeMedium
+ }
+ highlighted: down || voiceNoteOverlayLoader.active
+ onClicked: {
+ voiceNoteOverlayLoader.active = !voiceNoteOverlayLoader.active;
+ stickerPickerLoader.active = false;
+ }
+ }
+ IconButton {
+ visible: chatPage.hasSendPrivilege("can_send_media_messages")
+ icon.source: "image://theme/icon-m-document"
+ onClicked: {
+ var picker = pageStack.push("Sailfish.Pickers.FilePickerPage", {
+ allowedOrientations: chatPage.allowedOrientations
+ })
+ picker.selectedContentPropertiesChanged.connect(function(){
+ attachmentOptionsFlickable.isNeeded = false;
+ Debug.log("Selected document: ", picker.selectedContentProperties.filePath );
+ attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
+ attachmentPreviewRow.isDocument = true;
+ controlSendButton();
+ })
+ }
+ }
+ IconButton {
+ visible: chatPage.hasSendPrivilege("can_send_other_messages")
+ icon.source: "../../images/icon-m-sticker.svg"
+ icon.sourceSize {
+ width: Theme.iconSizeMedium
+ height: Theme.iconSizeMedium
+ }
+ highlighted: down || stickerPickerLoader.active
+ onClicked: {
+ stickerPickerLoader.active = !stickerPickerLoader.active;
+ voiceNoteOverlayLoader.active = false;
+ }
+ }
+ IconButton {
+ visible: !(chatPage.isPrivateChat || chatPage.isSecretChat) && chatPage.hasSendPrivilege("can_send_polls")
+ icon.source: "image://theme/icon-m-question"
+ onClicked: {
+ pageStack.push(Qt.resolvedUrl("../pages/PollCreationPage.qml"), { "chatId" : chatInformation.id, groupName: chatInformation.title});
+ attachmentOptionsFlickable.isNeeded = false;
+ }
+ }
+ IconButton {
+ visible: fernschreiberUtils.supportsGeoLocation() && newMessageTextField.text === ""
+ icon.source: "image://theme/icon-m-location"
+ icon.sourceSize {
+ width: Theme.iconSizeMedium
+ height: Theme.iconSizeMedium
+ }
+ onClicked: {
+ fernschreiberUtils.startGeoLocationUpdates();
+ attachmentOptionsFlickable.isNeeded = false;
+ attachmentPreviewRow.isLocation = true;
+ attachmentPreviewRow.attachmentDescription = qsTr("Location: Obtaining position...");
+ controlSendButton();
+ }
+ }
+ }
+
}
Row {
- id: attachmentOptionsRow
-
- height: attachImageIconButton.height
-
- anchors.right: parent.right
- layoutDirection: Qt.RightToLeft
+ id: attachmentPreviewRow
+ visible: (!!locationData || !!fileProperties) && !inlineQuery.userNameIsValid
spacing: Theme.paddingMedium
- leftPadding: Theme.horizontalPageMargin
- rightPadding: Theme.horizontalPageMargin
+ width: parent.width
+ layoutDirection: Qt.RightToLeft
+ anchors.right: parent.right
+
+ property bool isPicture: false;
+ property bool isVideo: false;
+ property bool isDocument: false;
+ property bool isVoiceNote: false;
+ property bool isLocation: false;
+ property var locationData: null;
+ property var fileProperties: null;
+ property string attachmentDescription: "";
+
+ Connections {
+ target: fernschreiberUtils
+ onNewPositionInformation: {
+ attachmentPreviewRow.locationData = positionInformation;
+ if (attachmentPreviewRow.isLocation) {
+ attachmentPreviewRow.attachmentDescription = qsTr("Location (%1/%2)").arg(attachmentPreviewRow.locationData.latitude).arg(attachmentPreviewRow.locationData.longitude);
+ }
+ }
+ }
IconButton {
- id: attachImageIconButton
- visible: chatPage.hasSendPrivilege("can_send_media_messages")
- icon.source: "image://theme/icon-m-image"
+ id: removeAttachmentsIconButton
+ icon.source: "image://theme/icon-m-clear"
onClicked: {
- var picker = pageStack.push("Sailfish.Pickers.ImagePickerPage", {
- allowedOrientations: chatPage.allowedOrientations
- })
- picker.selectedContentPropertiesChanged.connect(function(){
- attachmentOptionsFlickable.isNeeded = false;
- Debug.log("Selected document: ", picker.selectedContentProperties.filePath );
- attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
- attachmentPreviewRow.isPicture = true;
- controlSendButton();
- })
- }
- }
- IconButton {
- visible: chatPage.hasSendPrivilege("can_send_media_messages")
- icon.source: "image://theme/icon-m-video"
- onClicked: {
- var picker = pageStack.push("Sailfish.Pickers.VideoPickerPage", {
- allowedOrientations: chatPage.allowedOrientations
- })
- picker.selectedContentPropertiesChanged.connect(function(){
- attachmentOptionsFlickable.isNeeded = false;
- Debug.log("Selected video: ", picker.selectedContentProperties.filePath );
- attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
- attachmentPreviewRow.isVideo = true;
- controlSendButton();
- })
- }
- }
- IconButton {
- visible: chatPage.hasSendPrivilege("can_send_media_messages")
- icon.source: "image://theme/icon-m-mic"
- icon.sourceSize {
- width: Theme.iconSizeMedium
- height: Theme.iconSizeMedium
- }
- highlighted: down || voiceNoteOverlayLoader.active
- onClicked: {
- voiceNoteOverlayLoader.active = !voiceNoteOverlayLoader.active;
- stickerPickerLoader.active = false;
- }
- }
- IconButton {
- visible: chatPage.hasSendPrivilege("can_send_media_messages")
- icon.source: "image://theme/icon-m-document"
- onClicked: {
- var picker = pageStack.push("Sailfish.Pickers.FilePickerPage", {
- allowedOrientations: chatPage.allowedOrientations
- })
- picker.selectedContentPropertiesChanged.connect(function(){
- attachmentOptionsFlickable.isNeeded = false;
- Debug.log("Selected document: ", picker.selectedContentProperties.filePath );
- attachmentPreviewRow.fileProperties = picker.selectedContentProperties;
- attachmentPreviewRow.isDocument = true;
- controlSendButton();
- })
- }
- }
- IconButton {
- visible: chatPage.hasSendPrivilege("can_send_other_messages")
- icon.source: "../../images/icon-m-sticker.svg"
- icon.sourceSize {
- width: Theme.iconSizeMedium
- height: Theme.iconSizeMedium
- }
- highlighted: down || stickerPickerLoader.active
- onClicked: {
- stickerPickerLoader.active = !stickerPickerLoader.active;
- voiceNoteOverlayLoader.active = false;
- }
- }
- IconButton {
- visible: !(chatPage.isPrivateChat || chatPage.isSecretChat) && chatPage.hasSendPrivilege("can_send_polls")
- icon.source: "image://theme/icon-m-question"
- onClicked: {
- pageStack.push(Qt.resolvedUrl("../pages/PollCreationPage.qml"), { "chatId" : chatInformation.id, groupName: chatInformation.title});
- attachmentOptionsFlickable.isNeeded = false;
- }
- }
- IconButton {
- visible: fernschreiberUtils.supportsGeoLocation() && newMessageTextField.text === ""
- icon.source: "image://theme/icon-m-location"
- icon.sourceSize {
- width: Theme.iconSizeMedium
- height: Theme.iconSizeMedium
- }
- onClicked: {
- fernschreiberUtils.startGeoLocationUpdates();
- attachmentOptionsFlickable.isNeeded = false;
- attachmentPreviewRow.isLocation = true;
- attachmentPreviewRow.attachmentDescription = qsTr("Location: Obtaining position...");
+ clearAttachmentPreviewRow();
controlSendButton();
}
}
+
+ Thumbnail {
+ id: attachmentPreviewImage
+ width: Theme.itemSizeMedium
+ height: Theme.itemSizeMedium
+ sourceSize.width: width
+ sourceSize.height: height
+
+ fillMode: Thumbnail.PreserveAspectCrop
+ mimeType: !!attachmentPreviewRow.fileProperties ? attachmentPreviewRow.fileProperties.mimeType || "" : ""
+ source: !!attachmentPreviewRow.fileProperties ? attachmentPreviewRow.fileProperties.url || "" : ""
+ visible: attachmentPreviewRow.isPicture || attachmentPreviewRow.isVideo
+ }
+
+ Label {
+ id: attachmentPreviewText
+ font.pixelSize: Theme.fontSizeSmall
+ text: ( attachmentPreviewRow.isVoiceNote || attachmentPreviewRow.isLocation ) ? attachmentPreviewRow.attachmentDescription : ( !!attachmentPreviewRow.fileProperties ? attachmentPreviewRow.fileProperties.fileName || "" : "" );
+ anchors.verticalCenter: parent.verticalCenter
+
+ maximumLineCount: 1
+ truncationMode: TruncationMode.Fade
+ color: Theme.secondaryColor
+ visible: attachmentPreviewRow.isDocument || attachmentPreviewRow.isVoiceNote || attachmentPreviewRow.isLocation
+ }
}
- }
+ Row {
+ id: uploadStatusRow
+ visible: false
+ spacing: Theme.paddingMedium
+ width: parent.width
+ anchors.right: parent.right
+ Text {
+ id: uploadingText
+ font.pixelSize: Theme.fontSizeSmall
+ text: qsTr("Uploading...")
+ anchors.verticalCenter: parent.verticalCenter
+ color: Theme.secondaryColor
+ visible: uploadStatusRow.visible
+ }
- Row {
- id: attachmentPreviewRow
- visible: (!!locationData || !!fileProperties) && !inlineQuery.userNameIsValid
- spacing: Theme.paddingMedium
- width: parent.width
- layoutDirection: Qt.RightToLeft
- anchors.right: parent.right
+ ProgressBar {
+ id: uploadingProgressBar
+ minimumValue: 0
+ maximumValue: 100
+ value: 0
+ visible: uploadStatusRow.visible
+ width: parent.width - uploadingText.width - Theme.paddingMedium
+ }
- property bool isPicture: false;
- property bool isVideo: false;
- property bool isDocument: false;
- property bool isVoiceNote: false;
- property bool isLocation: false;
- property var locationData: null;
- property var fileProperties: null;
- property string attachmentDescription: "";
+ }
- Connections {
- target: fernschreiberUtils
- onNewPositionInformation: {
- attachmentPreviewRow.locationData = positionInformation;
- if (attachmentPreviewRow.isLocation) {
- attachmentPreviewRow.attachmentDescription = qsTr("Location (%1/%2)").arg(attachmentPreviewRow.locationData.latitude).arg(attachmentPreviewRow.locationData.longitude);
+ Column {
+ id: emojiColumn
+ width: parent.width
+ anchors.horizontalCenter: parent.horizontalCenter
+ visible: emojiProposals ? ( emojiProposals.length > 0 ? true : false ) : false
+ opacity: emojiProposals ? ( emojiProposals.length > 0 ? 1 : 0 ) : 0
+ Behavior on opacity { NumberAnimation {} }
+ spacing: Theme.paddingMedium
+
+ Flickable {
+ width: parent.width
+ height: emojiResultRow.height + Theme.paddingSmall
+ anchors.horizontalCenter: parent.horizontalCenter
+ contentWidth: emojiResultRow.width
+ clip: true
+ Row {
+ id: emojiResultRow
+ spacing: Theme.paddingMedium
+ Repeater {
+ model: emojiProposals
+
+ Item {
+ height: singleEmojiRow.height
+ width: singleEmojiRow.width
+
+ Row {
+ id: singleEmojiRow
+ spacing: Theme.paddingSmall
+
+ Image {
+ id: emojiPicture
+ source: "../js/emoji/" + modelData.file_name
+ width: Theme.fontSizeLarge
+ height: Theme.fontSizeLarge
+ }
+
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ replaceMessageText(newMessageTextField.text, newMessageTextField.cursorPosition, modelData.emoji);
+ emojiProposals = null;
+ }
+ }
+ }
+
+ }
}
}
}
- IconButton {
- id: removeAttachmentsIconButton
- icon.source: "image://theme/icon-m-clear"
- onClicked: {
- clearAttachmentPreviewRow();
- controlSendButton();
+ Column {
+ id: atMentionColumn
+ width: parent.width
+ anchors.horizontalCenter: parent.horizontalCenter
+ visible: opacity > 0
+ opacity: knownUsersRepeater.count > 0 ? 1 : 0
+ Behavior on opacity { NumberAnimation {} }
+ height: knownUsersRepeater.count > 0 ? childrenRect.height : 0
+ Behavior on height { SmoothedAnimation { duration: 200 } }
+ spacing: Theme.paddingMedium
+
+ Flickable {
+ width: parent.width
+ height: atMentionResultRow.height + Theme.paddingSmall
+ anchors.horizontalCenter: parent.horizontalCenter
+ contentWidth: atMentionResultRow.width
+ clip: true
+ Row {
+ id: atMentionResultRow
+ spacing: Theme.paddingMedium
+ Repeater {
+ id: knownUsersRepeater
+
+ Item {
+ id: knownUserItem
+ height: singleAtMentionRow.height
+ width: singleAtMentionRow.width
+
+ property string atMentionText: "@" + (user_name ? user_name : user_id + "(" + title + ")");
+
+ Row {
+ id: singleAtMentionRow
+ spacing: Theme.paddingSmall
+
+ Item {
+ width: Theme.fontSizeHuge
+ height: Theme.fontSizeHuge
+ anchors.verticalCenter: parent.verticalCenter
+ ProfileThumbnail {
+ id: atMentionThumbnail
+ replacementStringHint: title
+ width: parent.width
+ height: parent.width
+ photoData: photo_small
+ }
+ }
+
+ Column {
+ Text {
+ text: Emoji.emojify(title, Theme.fontSizeExtraSmall)
+ textFormat: Text.StyledText
+ color: Theme.primaryColor
+ font.pixelSize: Theme.fontSizeExtraSmall
+ font.bold: true
+ }
+ Text {
+ id: userHandleText
+ text: user_handle
+ textFormat: Text.StyledText
+ color: Theme.primaryColor
+ font.pixelSize: Theme.fontSizeExtraSmall
+ }
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ replaceMessageText(newMessageTextField.text, newMessageTextField.cursorPosition, knownUserItem.atMentionText);
+ knownUsersRepeater.model = undefined;
+ }
+ }
+ }
+
+ }
+ }
}
}
- Thumbnail {
- id: attachmentPreviewImage
- width: Theme.itemSizeMedium
- height: Theme.itemSizeMedium
- sourceSize.width: width
- sourceSize.height: height
-
- fillMode: Thumbnail.PreserveAspectCrop
- mimeType: !!attachmentPreviewRow.fileProperties ? attachmentPreviewRow.fileProperties.mimeType || "" : ""
- source: !!attachmentPreviewRow.fileProperties ? attachmentPreviewRow.fileProperties.url || "" : ""
- visible: attachmentPreviewRow.isPicture || attachmentPreviewRow.isVideo
- }
-
- Label {
- id: attachmentPreviewText
- font.pixelSize: Theme.fontSizeSmall
- text: ( attachmentPreviewRow.isVoiceNote || attachmentPreviewRow.isLocation ) ? attachmentPreviewRow.attachmentDescription : ( !!attachmentPreviewRow.fileProperties ? attachmentPreviewRow.fileProperties.fileName || "" : "" );
- anchors.verticalCenter: parent.verticalCenter
-
- maximumLineCount: 1
- truncationMode: TruncationMode.Fade
- color: Theme.secondaryColor
- visible: attachmentPreviewRow.isDocument || attachmentPreviewRow.isVoiceNote || attachmentPreviewRow.isLocation
- }
- }
-
- Row {
- id: uploadStatusRow
- visible: false
- spacing: Theme.paddingMedium
- width: parent.width
- anchors.right: parent.right
-
- Text {
- id: uploadingText
- font.pixelSize: Theme.fontSizeSmall
- text: qsTr("Uploading...")
- anchors.verticalCenter: parent.verticalCenter
- color: Theme.secondaryColor
- visible: uploadStatusRow.visible
- }
-
- ProgressBar {
- id: uploadingProgressBar
- minimumValue: 0
- maximumValue: 100
- value: 0
- visible: uploadStatusRow.visible
- width: parent.width - uploadingText.width - Theme.paddingMedium
- }
-
- }
-
- Column {
- id: emojiColumn
- width: parent.width
- anchors.horizontalCenter: parent.horizontalCenter
- visible: emojiProposals ? ( emojiProposals.length > 0 ? true : false ) : false
- opacity: emojiProposals ? ( emojiProposals.length > 0 ? 1 : 0 ) : 0
- Behavior on opacity { NumberAnimation {} }
- spacing: Theme.paddingMedium
-
- Flickable {
+ Row {
+ width: parent.width
+ spacing: Theme.paddingSmall
+ visible: newMessageColumn.editMessageId !== "0"
+
+ Text {
+ width: parent.width - Theme.paddingSmall - removeEditMessageIconButton.width
+
+ anchors.verticalCenter: parent.verticalCenter
+
+ id: editMessageText
+ font.pixelSize: Theme.fontSizeSmall
+ font.bold: true
+ text: qsTr("Edit Message")
+ color: Theme.secondaryColor
+ }
+
+ IconButton {
+ id: removeEditMessageIconButton
+ icon.source: "image://theme/icon-m-clear"
+ onClicked: {
+ newMessageColumn.editMessageId = "0";
+ newMessageTextField.text = "";
+ }
+ }
+ }
+
+ Row {
+ id: newMessageRow
width: parent.width
- height: emojiResultRow.height + Theme.paddingSmall
anchors.horizontalCenter: parent.horizontalCenter
- contentWidth: emojiResultRow.width
- clip: true
- Row {
- id: emojiResultRow
- spacing: Theme.paddingMedium
- Repeater {
- model: emojiProposals
- Item {
- height: singleEmojiRow.height
- width: singleEmojiRow.width
-
- Row {
- id: singleEmojiRow
- spacing: Theme.paddingSmall
-
- Image {
- id: emojiPicture
- source: "../js/emoji/" + modelData.file_name
- width: Theme.fontSizeLarge
- height: Theme.fontSizeLarge
- }
-
- }
-
- MouseArea {
- anchors.fill: parent
- onClicked: {
- replaceMessageText(newMessageTextField.text, newMessageTextField.cursorPosition, modelData.emoji);
- emojiProposals = null;
- }
+ TextArea {
+ id: newMessageTextField
+ width: parent.width - (attachmentIconButton.visible ? attachmentIconButton.width : 0) - (newMessageSendButton.visible ? newMessageSendButton.width : 0) - (cancelInlineQueryButton.visible ? cancelInlineQueryButton.width : 0)
+ height: Math.min(chatContainer.height / 3, implicitHeight)
+ anchors.verticalCenter: parent.verticalCenter
+ font.pixelSize: Theme.fontSizeSmall
+ placeholderText: qsTr("Your message")
+ labelVisible: false
+ textLeftMargin: 0
+ textTopMargin: 0
+ enabled: !attachmentPreviewRow.isLocation
+ EnterKey.onClicked: {
+ if (appSettings.sendByEnter) {
+ sendMessage();
+ newMessageTextField.text = "";
+ if(!appSettings.focusTextAreaAfterSend) {
+ newMessageTextField.focus = false;
}
}
+ }
+ EnterKey.enabled: !inlineQuery.userNameIsValid && (!appSettings.sendByEnter || text.length)
+ EnterKey.iconSource: appSettings.sendByEnter ? "image://theme/icon-m-chat" : "image://theme/icon-m-enter"
+
+ onTextChanged: {
+ controlSendButton();
+ textReplacementTimer.restart();
}
}
- }
- }
- Column {
- id: atMentionColumn
- width: parent.width
- anchors.horizontalCenter: parent.horizontalCenter
- visible: opacity > 0
- opacity: knownUsersRepeater.count > 0 ? 1 : 0
- Behavior on opacity { NumberAnimation {} }
- height: knownUsersRepeater.count > 0 ? childrenRect.height : 0
- Behavior on height { SmoothedAnimation { duration: 200 } }
- spacing: Theme.paddingMedium
-
- Flickable {
- width: parent.width
- height: atMentionResultRow.height + Theme.paddingSmall
- anchors.horizontalCenter: parent.horizontalCenter
- contentWidth: atMentionResultRow.width
- clip: true
- Row {
- id: atMentionResultRow
- spacing: Theme.paddingMedium
- Repeater {
- id: knownUsersRepeater
-
- Item {
- id: knownUserItem
- height: singleAtMentionRow.height
- width: singleAtMentionRow.width
-
- property string atMentionText: "@" + (user_name ? user_name : user_id + "(" + title + ")");
-
- Row {
- id: singleAtMentionRow
- spacing: Theme.paddingSmall
-
- Item {
- width: Theme.fontSizeHuge
- height: Theme.fontSizeHuge
- anchors.verticalCenter: parent.verticalCenter
- ProfileThumbnail {
- id: atMentionThumbnail
- replacementStringHint: title
- width: parent.width
- height: parent.width
- photoData: photo_small
- }
- }
-
- Column {
- Text {
- text: Emoji.emojify(title, Theme.fontSizeExtraSmall)
- textFormat: Text.StyledText
- color: Theme.primaryColor
- font.pixelSize: Theme.fontSizeExtraSmall
- font.bold: true
- }
- Text {
- id: userHandleText
- text: user_handle
- textFormat: Text.StyledText
- color: Theme.primaryColor
- font.pixelSize: Theme.fontSizeExtraSmall
- }
- }
- }
-
- MouseArea {
- anchors.fill: parent
- onClicked: {
- replaceMessageText(newMessageTextField.text, newMessageTextField.cursorPosition, knownUserItem.atMentionText);
- knownUsersRepeater.model = undefined;
- }
- }
+ IconButton {
+ id: attachmentIconButton
+ icon.source: "image://theme/icon-m-attach?" + (attachmentOptionsFlickable.isNeeded ? Theme.highlightColor : Theme.primaryColor)
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: Theme.paddingSmall
+ enabled: !attachmentPreviewRow.visible && !stickerSetOverlayLoader.item
+ visible: !inlineQuery.userNameIsValid
+ onClicked: {
+ if (attachmentOptionsFlickable.isNeeded) {
+ attachmentOptionsFlickable.isNeeded = false;
+ stickerPickerLoader.active = false;
+ voiceNoteOverlayLoader.active = false;
+ } else {
+ attachmentOptionsFlickable.isNeeded = true;
}
-
}
}
- }
- }
- Row {
- width: parent.width
- spacing: Theme.paddingSmall
- visible: newMessageColumn.editMessageId !== "0"
-
- Text {
- width: parent.width - Theme.paddingSmall - removeEditMessageIconButton.width
-
- anchors.verticalCenter: parent.verticalCenter
-
- id: editMessageText
- font.pixelSize: Theme.fontSizeSmall
- font.bold: true
- text: qsTr("Edit Message")
- color: Theme.secondaryColor
- }
-
- IconButton {
- id: removeEditMessageIconButton
- icon.source: "image://theme/icon-m-clear"
- onClicked: {
- newMessageColumn.editMessageId = "0";
- newMessageTextField.text = "";
- }
- }
- }
-
- Row {
- id: newMessageRow
- width: parent.width
- anchors.horizontalCenter: parent.horizontalCenter
-
- TextArea {
- id: newMessageTextField
- width: parent.width - (attachmentIconButton.visible ? attachmentIconButton.width : 0) - (newMessageSendButton.visible ? newMessageSendButton.width : 0) - (cancelInlineQueryButton.visible ? cancelInlineQueryButton.width : 0)
- height: Math.min(chatContainer.height / 3, implicitHeight)
- anchors.verticalCenter: parent.verticalCenter
- font.pixelSize: Theme.fontSizeSmall
- placeholderText: qsTr("Your message")
- labelVisible: false
- textLeftMargin: 0
- textTopMargin: 0
- enabled: !attachmentPreviewRow.isLocation
- EnterKey.onClicked: {
- if (appSettings.sendByEnter) {
+ IconButton {
+ id: newMessageSendButton
+ icon.source: "image://theme/icon-m-chat"
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: Theme.paddingSmall
+ visible: !inlineQuery.userNameIsValid && (!appSettings.sendByEnter || attachmentPreviewRow.visible)
+ enabled: false
+ onClicked: {
sendMessage();
newMessageTextField.text = "";
if(!appSettings.focusTextAreaAfterSend) {
@@ -1764,89 +1896,50 @@ Page {
}
}
- EnterKey.enabled: !inlineQuery.userNameIsValid && (!appSettings.sendByEnter || text.length)
- EnterKey.iconSource: appSettings.sendByEnter ? "image://theme/icon-m-chat" : "image://theme/icon-m-enter"
+ Item {
+ width: cancelInlineQueryButton.width
+ height: cancelInlineQueryButton.height
+ visible: inlineQuery.userNameIsValid
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: Theme.paddingSmall
- onTextChanged: {
- controlSendButton();
- textReplacementTimer.restart();
- }
- }
-
- IconButton {
- id: attachmentIconButton
- icon.source: "image://theme/icon-m-attach?" + (attachmentOptionsFlickable.isNeeded ? Theme.highlightColor : Theme.primaryColor)
- anchors.bottom: parent.bottom
- anchors.bottomMargin: Theme.paddingSmall
- enabled: !attachmentPreviewRow.visible && !stickerSetOverlayLoader.item
- visible: !inlineQuery.userNameIsValid
- onClicked: {
- if (attachmentOptionsFlickable.isNeeded) {
- attachmentOptionsFlickable.isNeeded = false;
- stickerPickerLoader.active = false;
- voiceNoteOverlayLoader.active = false;
- } else {
- attachmentOptionsFlickable.isNeeded = true;
- }
- }
- }
-
- IconButton {
- id: newMessageSendButton
- icon.source: "image://theme/icon-m-chat"
- anchors.bottom: parent.bottom
- anchors.bottomMargin: Theme.paddingSmall
- visible: !inlineQuery.userNameIsValid && (!appSettings.sendByEnter || attachmentPreviewRow.visible)
- enabled: false
- onClicked: {
- sendMessage();
- newMessageTextField.text = "";
- if(!appSettings.focusTextAreaAfterSend) {
- newMessageTextField.focus = false;
- }
- }
- }
-
- Item {
- width: cancelInlineQueryButton.width
- height: cancelInlineQueryButton.height
- visible: inlineQuery.userNameIsValid
- anchors.bottom: parent.bottom
- anchors.bottomMargin: Theme.paddingSmall
-
- IconButton {
- id: cancelInlineQueryButton
- icon.source: "image://theme/icon-m-cancel"
- visible: parent.visible
- opacity: inlineQuery.isLoading ? 0.2 : 1
- Behavior on opacity { FadeAnimation {} }
- onClicked: {
- if(inlineQuery.query !== "") {
- newMessageTextField.text = "@" + inlineQuery.userName + " "
- newMessageTextField.cursorPosition = newMessageTextField.text.length
- lostFocusTimer.start();
- } else {
+ IconButton {
+ id: cancelInlineQueryButton
+ icon.source: "image://theme/icon-m-cancel"
+ visible: parent.visible
+ opacity: inlineQuery.isLoading ? 0.2 : 1
+ Behavior on opacity { FadeAnimation {} }
+ onClicked: {
+ if(inlineQuery.query !== "") {
+ newMessageTextField.text = "@" + inlineQuery.userName + " "
+ newMessageTextField.cursorPosition = newMessageTextField.text.length
+ lostFocusTimer.start();
+ } else {
+ newMessageTextField.text = ""
+ }
+ }
+ onPressAndHold: {
newMessageTextField.text = ""
}
}
- onPressAndHold: {
- newMessageTextField.text = ""
+
+ BusyIndicator {
+ size: BusyIndicatorSize.Small
+ anchors.centerIn: parent
+ running: inlineQuery.isLoading
}
}
- BusyIndicator {
- size: BusyIndicatorSize.Small
- anchors.centerIn: parent
- running: inlineQuery.isLoading
- }
+
}
-
-
}
}
}
+
+
}
+
Loader {
id: selectedMessagesActions
asynchronous: true
diff --git a/rpm/harbour-fernschreiber.spec b/rpm/harbour-fernschreiber.spec
index 8b97fa8..999885f 100644
--- a/rpm/harbour-fernschreiber.spec
+++ b/rpm/harbour-fernschreiber.spec
@@ -12,7 +12,7 @@ Name: harbour-fernschreiber
Summary: Fernschreiber is a Telegram client for Sailfish OS
Version: 0.7.1
-Release: 2
+Release: 3
Group: Qt/Qt
License: LICENSE
URL: http://werkwolf.eu/
diff --git a/rpm/harbour-fernschreiber.yaml b/rpm/harbour-fernschreiber.yaml
index b8168df..b1a564d 100644
--- a/rpm/harbour-fernschreiber.yaml
+++ b/rpm/harbour-fernschreiber.yaml
@@ -1,7 +1,7 @@
Name: harbour-fernschreiber
Summary: Fernschreiber is a Telegram client for Sailfish OS
Version: 0.7.1
-Release: 2
+Release: 3
# The contents of the Group field should be one of the groups listed here:
# https://github.com/mer-tools/spectacle/blob/master/data/GROUPS
Group: Qt/Qt
diff --git a/translations/harbour-fernschreiber-de.ts b/translations/harbour-fernschreiber-de.ts
index 1e7fac7..f15679a 100644
--- a/translations/harbour-fernschreiber-de.ts
+++ b/translations/harbour-fernschreiber-de.ts
@@ -435,6 +435,14 @@
Standort (%1/%2)
+
+
+ Nachricht in die Zwischenablage kopieren
+
+
+
+
+
ChatSelectionPage
@@ -991,6 +999,10 @@
Nachricht losheften
+
+
+
+
MessageListViewItemSimple
diff --git a/translations/harbour-fernschreiber-en.ts b/translations/harbour-fernschreiber-en.ts
index cbd94b1..4785223 100644
--- a/translations/harbour-fernschreiber-en.ts
+++ b/translations/harbour-fernschreiber-en.ts
@@ -435,6 +435,14 @@
Location (%1/%2)
+
+
+ Copy Message to Clipboard
+
+
+
+
+
ChatSelectionPage
@@ -993,6 +1001,10 @@ messages
Unpin Message
+
+
+
+
MessageListViewItemSimple
diff --git a/translations/harbour-fernschreiber-es.ts b/translations/harbour-fernschreiber-es.ts
index 3e216ce..aa9b741 100644
--- a/translations/harbour-fernschreiber-es.ts
+++ b/translations/harbour-fernschreiber-es.ts
@@ -435,6 +435,14 @@
Ubicación (%1/%2)
+
+
+ Copiar
+
+
+
+
+
ChatSelectionPage
@@ -991,6 +999,10 @@
Desanclar mensaje
+
+
+
+
MessageListViewItemSimple
diff --git a/translations/harbour-fernschreiber-fi.ts b/translations/harbour-fernschreiber-fi.ts
index 2dbffc2..093bc77 100644
--- a/translations/harbour-fernschreiber-fi.ts
+++ b/translations/harbour-fernschreiber-fi.ts
@@ -435,6 +435,14 @@
Sijainti (%1/%2)
+
+
+ Kopioi viesti leikepöydälle
+
+
+
+
+
ChatSelectionPage
@@ -992,6 +1000,10 @@
Poista viestin kiinnitys
+
+
+
+
MessageListViewItemSimple
diff --git a/translations/harbour-fernschreiber-hu.ts b/translations/harbour-fernschreiber-hu.ts
index de21a42..31173e4 100644
--- a/translations/harbour-fernschreiber-hu.ts
+++ b/translations/harbour-fernschreiber-hu.ts
@@ -425,6 +425,14 @@
+
+
+
+
+
+
+
+
ChatSelectionPage
@@ -977,6 +985,10 @@
+
+
+
+
MessageListViewItemSimple
diff --git a/translations/harbour-fernschreiber-it.ts b/translations/harbour-fernschreiber-it.ts
index 63eec71..366d1a7 100644
--- a/translations/harbour-fernschreiber-it.ts
+++ b/translations/harbour-fernschreiber-it.ts
@@ -435,6 +435,14 @@
Posizione(%1/%2)
+
+
+ Copia messaggio nella clipboard
+
+
+
+
+
ChatSelectionPage
@@ -991,6 +999,10 @@
Togli messaggio in evidenza
+
+
+
+
MessageListViewItemSimple
diff --git a/translations/harbour-fernschreiber-pl.ts b/translations/harbour-fernschreiber-pl.ts
index dfc6fdf..f0acce1 100644
--- a/translations/harbour-fernschreiber-pl.ts
+++ b/translations/harbour-fernschreiber-pl.ts
@@ -445,6 +445,14 @@
Lokalizacja (%1/%2)
+
+
+ Skopiuj wiadomość do schowka
+
+
+
+
+
ChatSelectionPage
@@ -1005,6 +1013,10 @@
Odepnij wiadomość
+
+
+
+
MessageListViewItemSimple
diff --git a/translations/harbour-fernschreiber-ru.ts b/translations/harbour-fernschreiber-ru.ts
index f054407..84182ef 100644
--- a/translations/harbour-fernschreiber-ru.ts
+++ b/translations/harbour-fernschreiber-ru.ts
@@ -445,6 +445,14 @@
Местоположение (%1/%2)
+
+
+ Скопировать в буфер обмена
+
+
+
+
+
ChatSelectionPage
@@ -1008,6 +1016,10 @@
Открепить сообщение
+
+
+
+
MessageListViewItemSimple
diff --git a/translations/harbour-fernschreiber-sk.ts b/translations/harbour-fernschreiber-sk.ts
index 5bbc498..56cf8f9 100644
--- a/translations/harbour-fernschreiber-sk.ts
+++ b/translations/harbour-fernschreiber-sk.ts
@@ -445,6 +445,14 @@
Poloha (%1/%2)
+
+
+ Kopírovať správu do schránky
+
+
+
+
+
ChatSelectionPage
@@ -1005,6 +1013,10 @@
Odopnúť správu
+
+
+
+
MessageListViewItemSimple
diff --git a/translations/harbour-fernschreiber-sv.ts b/translations/harbour-fernschreiber-sv.ts
index fb90049..910c4b9 100644
--- a/translations/harbour-fernschreiber-sv.ts
+++ b/translations/harbour-fernschreiber-sv.ts
@@ -435,6 +435,14 @@
Plats (%1/%2)
+
+
+ Kopiera meddelandet till urklipp
+
+
+
+
+
ChatSelectionPage
@@ -991,6 +999,10 @@
Lösgör meddelandet
+
+
+
+
MessageListViewItemSimple
diff --git a/translations/harbour-fernschreiber-zh_CN.ts b/translations/harbour-fernschreiber-zh_CN.ts
index cc64d84..127afe2 100644
--- a/translations/harbour-fernschreiber-zh_CN.ts
+++ b/translations/harbour-fernschreiber-zh_CN.ts
@@ -425,6 +425,14 @@
位置 (%1/%2)
+
+
+ 复制消息到剪切板
+
+
+
+
+
ChatSelectionPage
@@ -978,6 +986,10 @@
取消置顶
+
+
+
+
MessageListViewItemSimple
diff --git a/translations/harbour-fernschreiber.ts b/translations/harbour-fernschreiber.ts
index a3c7cdd..7160831 100644
--- a/translations/harbour-fernschreiber.ts
+++ b/translations/harbour-fernschreiber.ts
@@ -435,6 +435,14 @@
+
+
+ Copy Message to Clipboard
+
+
+
+
+
ChatSelectionPage
@@ -991,6 +999,10 @@
+
+
+
+
MessageListViewItemSimple