fix: Create only one instance of `StatusChatInput` (#10928)
* Chat input area preserved properties * Fix emoji/gif/stickers popups open/close logic
This commit is contained in:
parent
dc20651a97
commit
bc4492b53a
|
@ -21,6 +21,7 @@ QtObject:
|
||||||
isUntrustworthy: bool
|
isUntrustworthy: bool
|
||||||
isContact: bool
|
isContact: bool
|
||||||
active: bool
|
active: bool
|
||||||
|
blocked: bool
|
||||||
|
|
||||||
proc delete*(self: ChatDetails) =
|
proc delete*(self: ChatDetails) =
|
||||||
self.QObject.delete
|
self.QObject.delete
|
||||||
|
@ -32,7 +33,7 @@ QtObject:
|
||||||
proc setChatDetails*(self: ChatDetails, id: string, `type`: int, belongsToCommunity,
|
proc setChatDetails*(self: ChatDetails, id: string, `type`: int, belongsToCommunity,
|
||||||
isUsersListAvailable: bool, name, icon: string, color, description,
|
isUsersListAvailable: bool, name, icon: string, color, description,
|
||||||
emoji: string, hasUnreadMessages: bool, notificationsCount: int, muted: bool, position: int,
|
emoji: string, hasUnreadMessages: bool, notificationsCount: int, muted: bool, position: int,
|
||||||
isUntrustworthy: bool, isContact: bool = false) =
|
isUntrustworthy: bool, isContact: bool = false, blocked: bool = false) =
|
||||||
self.id = id
|
self.id = id
|
||||||
self.`type` = `type`
|
self.`type` = `type`
|
||||||
self.belongsToCommunity = belongsToCommunity
|
self.belongsToCommunity = belongsToCommunity
|
||||||
|
@ -49,6 +50,7 @@ QtObject:
|
||||||
self.isUntrustworthy = isUntrustworthy
|
self.isUntrustworthy = isUntrustworthy
|
||||||
self.isContact = isContact
|
self.isContact = isContact
|
||||||
self.active = false
|
self.active = false
|
||||||
|
self.blocked = blocked
|
||||||
|
|
||||||
proc getId(self: ChatDetails): string {.slot.} =
|
proc getId(self: ChatDetails): string {.slot.} =
|
||||||
return self.id
|
return self.id
|
||||||
|
@ -201,3 +203,14 @@ QtObject:
|
||||||
proc setActive*(self: ChatDetails, value: bool) =
|
proc setActive*(self: ChatDetails, value: bool) =
|
||||||
self.active = value
|
self.active = value
|
||||||
self.activeChanged()
|
self.activeChanged()
|
||||||
|
|
||||||
|
proc blockedChanged(self: ChatDetails) {.signal.}
|
||||||
|
proc getBlocked(self: ChatDetails): bool {.slot.} =
|
||||||
|
return self.blocked
|
||||||
|
QtProperty[bool] blocked:
|
||||||
|
read = getBlocked
|
||||||
|
notify = blockedChanged
|
||||||
|
|
||||||
|
proc setBlocked*(self: ChatDetails, value: bool) =
|
||||||
|
self.blocked = value
|
||||||
|
self.blockedChanged()
|
||||||
|
|
|
@ -142,11 +142,13 @@ proc init*(self: Controller) =
|
||||||
var args = ContactArgs(e)
|
var args = ContactArgs(e)
|
||||||
if (args.contactId == self.chatId):
|
if (args.contactId == self.chatId):
|
||||||
self.delegate.onMutualContactChanged()
|
self.delegate.onMutualContactChanged()
|
||||||
|
self.delegate.onContactDetailsUpdated(args.contactId)
|
||||||
|
|
||||||
self.events.on(SIGNAL_CONTACT_UNBLOCKED) do(e: Args):
|
self.events.on(SIGNAL_CONTACT_UNBLOCKED) do(e: Args):
|
||||||
var args = ContactArgs(e)
|
var args = ContactArgs(e)
|
||||||
if (args.contactId == self.chatId):
|
if (args.contactId == self.chatId):
|
||||||
self.delegate.onMutualContactChanged()
|
self.delegate.onMutualContactChanged()
|
||||||
|
self.delegate.onContactDetailsUpdated(args.contactId)
|
||||||
|
|
||||||
self.events.on(SIGNAL_MESSAGE_DELETION) do(e: Args):
|
self.events.on(SIGNAL_MESSAGE_DELETION) do(e: Args):
|
||||||
let args = MessageDeletedArgs(e)
|
let args = MessageDeletedArgs(e)
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
import NimQml
|
|
||||||
|
|
||||||
QtObject:
|
|
||||||
type
|
|
||||||
Item* = ref object of QObject
|
|
||||||
|
|
||||||
proc setup(self: Item) =
|
|
||||||
self.QObject.setup
|
|
||||||
|
|
||||||
proc delete*(self: Item) =
|
|
||||||
self.QObject.delete
|
|
||||||
|
|
||||||
proc newItem*(): Item =
|
|
||||||
new(result, delete)
|
|
||||||
result.setup()
|
|
||||||
|
|
||||||
proc id*(self: Item): string {.slot.} =
|
|
||||||
self.id
|
|
||||||
|
|
||||||
QtProperty[string] id:
|
|
||||||
read = id
|
|
|
@ -1,17 +0,0 @@
|
||||||
import NimQml
|
|
||||||
import item
|
|
||||||
|
|
||||||
QtObject:
|
|
||||||
type
|
|
||||||
Model* = ref object of QAbstractListModel
|
|
||||||
sections: seq[Item]
|
|
||||||
|
|
||||||
proc setup(self: Model) =
|
|
||||||
self.QAbstractListModel.setup
|
|
||||||
|
|
||||||
proc delete*(self: Model) =
|
|
||||||
self.QAbstractListModel.delete
|
|
||||||
|
|
||||||
proc newModel*(): Model =
|
|
||||||
new(result, delete)
|
|
||||||
result.setup
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import NimQml
|
||||||
|
|
||||||
|
QtObject:
|
||||||
|
type
|
||||||
|
PreservedProperties* = ref object of QObject
|
||||||
|
text: string
|
||||||
|
replyMessageId: string
|
||||||
|
fileUrlsAndSourcesJson: string
|
||||||
|
|
||||||
|
proc setup(self: PreservedProperties) =
|
||||||
|
self.QObject.setup
|
||||||
|
self.fileUrlsAndSourcesJson = "[]"
|
||||||
|
|
||||||
|
proc delete*(self: PreservedProperties) =
|
||||||
|
self.QObject.delete
|
||||||
|
|
||||||
|
proc newPreservedProperties*(): PreservedProperties =
|
||||||
|
new(result, delete)
|
||||||
|
result.QObject.setup
|
||||||
|
|
||||||
|
proc textChanged*(self: PreservedProperties) {.signal.}
|
||||||
|
proc setText*(self: PreservedProperties, value: string) {.slot.} =
|
||||||
|
if self.text == value:
|
||||||
|
return
|
||||||
|
self.text = value
|
||||||
|
self.textChanged()
|
||||||
|
proc getText*(self: PreservedProperties): string {.slot.} =
|
||||||
|
result = self.text
|
||||||
|
QtProperty[string] text:
|
||||||
|
read = getText
|
||||||
|
write = setText
|
||||||
|
notify = textChanged
|
||||||
|
|
||||||
|
proc replyMessageIdChanged*(self: PreservedProperties) {.signal.}
|
||||||
|
proc setReplyMessageId*(self: PreservedProperties, value: string) {.slot.}=
|
||||||
|
if self.replyMessageId == value:
|
||||||
|
return
|
||||||
|
self.replyMessageId = value
|
||||||
|
self.replyMessageIdChanged()
|
||||||
|
proc getReplyMessageId*(self: PreservedProperties): string {.slot.} =
|
||||||
|
result = self.replyMessageId
|
||||||
|
QtProperty[string] replyMessageId:
|
||||||
|
read = getReplyMessageId
|
||||||
|
write = setReplyMessageId
|
||||||
|
notify = replyMessageIdChanged
|
||||||
|
|
||||||
|
proc fileUrlsAndSourcesJsonChanged*(self: PreservedProperties) {.signal.}
|
||||||
|
proc setFileUrlsAndSourcesJson*(self: PreservedProperties, value: string) {.slot.}=
|
||||||
|
if self.fileUrlsAndSourcesJson == value:
|
||||||
|
return
|
||||||
|
self.fileUrlsAndSourcesJson = value
|
||||||
|
self.fileUrlsAndSourcesJsonChanged()
|
||||||
|
proc getFileUrlsAndSourcesJson*(self: PreservedProperties): string {.slot.} =
|
||||||
|
result = self.fileUrlsAndSourcesJson
|
||||||
|
QtProperty[string] fileUrlsAndSourcesJson:
|
||||||
|
read = getFileUrlsAndSourcesJson
|
||||||
|
write = setFileUrlsAndSourcesJson
|
||||||
|
notify = fileUrlsAndSourcesJsonChanged
|
|
@ -1,32 +1,38 @@
|
||||||
import NimQml
|
import NimQml
|
||||||
import ./model
|
|
||||||
import ./io_interface
|
import ./io_interface
|
||||||
import ./gif_column_model
|
import ./gif_column_model
|
||||||
|
import ./preserved_properties
|
||||||
import ../../../../../../app_service/service/gif/dto
|
import ../../../../../../app_service/service/gif/dto
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type
|
type
|
||||||
View* = ref object of QObject
|
View* = ref object of QObject
|
||||||
delegate: io_interface.AccessInterface
|
delegate: io_interface.AccessInterface
|
||||||
model: Model
|
|
||||||
gifColumnAModel: GifColumnModel
|
gifColumnAModel: GifColumnModel
|
||||||
gifColumnBModel: GifColumnModel
|
gifColumnBModel: GifColumnModel
|
||||||
gifColumnCModel: GifColumnModel
|
gifColumnCModel: GifColumnModel
|
||||||
gifLoading: bool
|
gifLoading: bool
|
||||||
|
preservedProperties: PreservedProperties
|
||||||
|
preservedPropertiesVariant: QVariant
|
||||||
|
|
||||||
proc delete*(self: View) =
|
proc delete*(self: View) =
|
||||||
self.model.delete
|
|
||||||
self.QObject.delete
|
self.QObject.delete
|
||||||
|
self.gifColumnAModel.delete
|
||||||
|
self.gifColumnBModel.delete
|
||||||
|
self.gifColumnCModel.delete
|
||||||
|
self.preservedProperties.delete
|
||||||
|
self.preservedPropertiesVariant.delete
|
||||||
|
|
||||||
proc newView*(delegate: io_interface.AccessInterface): View =
|
proc newView*(delegate: io_interface.AccessInterface): View =
|
||||||
new(result, delete)
|
new(result, delete)
|
||||||
result.QObject.setup
|
result.QObject.setup
|
||||||
result.delegate = delegate
|
result.delegate = delegate
|
||||||
result.model = newModel()
|
|
||||||
result.gifColumnAModel = newGifColumnModel()
|
result.gifColumnAModel = newGifColumnModel()
|
||||||
result.gifColumnBModel = newGifColumnModel()
|
result.gifColumnBModel = newGifColumnModel()
|
||||||
result.gifColumnCModel = newGifColumnModel()
|
result.gifColumnCModel = newGifColumnModel()
|
||||||
result.gifLoading = false
|
result.gifLoading = false
|
||||||
|
result.preservedProperties = newPreservedProperties()
|
||||||
|
result.preservedPropertiesVariant = newQVariant(result.preservedProperties)
|
||||||
|
|
||||||
proc load*(self: View) =
|
proc load*(self: View) =
|
||||||
self.delegate.viewDidLoad()
|
self.delegate.viewDidLoad()
|
||||||
|
@ -171,3 +177,9 @@ QtObject:
|
||||||
proc isFavorite*(self: View, id: string): bool {.slot.} =
|
proc isFavorite*(self: View, id: string): bool {.slot.} =
|
||||||
let gifItem = self.findGifDto(id)
|
let gifItem = self.findGifDto(id)
|
||||||
return self.delegate.isFavorite(gifItem)
|
return self.delegate.isFavorite(gifItem)
|
||||||
|
|
||||||
|
proc getPreservedProperties(self: View): QVariant {.slot.} =
|
||||||
|
return self.preservedPropertiesVariant
|
||||||
|
|
||||||
|
QtProperty[QVariant] preservedProperties:
|
||||||
|
read = getPreservedProperties
|
||||||
|
|
|
@ -358,6 +358,7 @@ method onContactDetailsUpdated*(self: Module, contactId: string) =
|
||||||
if(self.controller.getMyChatId() == contactId):
|
if(self.controller.getMyChatId() == contactId):
|
||||||
self.view.updateChatDetailsNameAndIcon(updatedContact.defaultDisplayName, updatedContact.icon)
|
self.view.updateChatDetailsNameAndIcon(updatedContact.defaultDisplayName, updatedContact.icon)
|
||||||
self.view.updateTrustStatus(updatedContact.dto.trustStatus == TrustStatus.Untrustworthy)
|
self.view.updateTrustStatus(updatedContact.dto.trustStatus == TrustStatus.Untrustworthy)
|
||||||
|
self.view.updateChatBlocked(updatedContact.dto.blocked)
|
||||||
|
|
||||||
method onNotificationsUpdated*(self: Module, hasUnreadMessages: bool, notificationCount: int) =
|
method onNotificationsUpdated*(self: Module, hasUnreadMessages: bool, notificationCount: int) =
|
||||||
self.view.updateChatDetailsNotifications(hasUnreadMessages, notificationCount)
|
self.view.updateChatDetailsNotifications(hasUnreadMessages, notificationCount)
|
||||||
|
|
|
@ -147,3 +147,6 @@ QtObject:
|
||||||
|
|
||||||
proc downloadMessages*(self: View, filePath: string) {.slot.} =
|
proc downloadMessages*(self: View, filePath: string) {.slot.} =
|
||||||
self.delegate.downloadMessages(filePath)
|
self.delegate.downloadMessages(filePath)
|
||||||
|
|
||||||
|
proc updateChatBlocked*(self: View, blocked: bool) =
|
||||||
|
self.chatDetails.setBlocked(blocked)
|
||||||
|
|
|
@ -62,7 +62,7 @@ Rectangle {
|
||||||
anchors.bottomMargin: 8
|
anchors.bottomMargin: 8
|
||||||
font.pixelSize: 13
|
font.pixelSize: 13
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: Theme.palette.white
|
color: Theme.palette.directColor1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import QtQuick 2.14
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.14
|
import QtQuick.Controls 2.15
|
||||||
import QtQml 2.14
|
import QtQuick.Layouts 1.15
|
||||||
|
import QtQml 2.15
|
||||||
|
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
import StatusQ.Components 0.1
|
import StatusQ.Components 0.1
|
||||||
|
@ -18,6 +19,7 @@ import "../controls"
|
||||||
import "../popups"
|
import "../popups"
|
||||||
import "../panels"
|
import "../panels"
|
||||||
import "../../Wallet"
|
import "../../Wallet"
|
||||||
|
import "../stores"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
@ -40,7 +42,6 @@ Item {
|
||||||
readonly property var contactDetails: rootStore ? rootStore.oneToOneChatContact : null
|
readonly property var contactDetails: rootStore ? rootStore.oneToOneChatContact : null
|
||||||
readonly property bool isUserAdded: root.contactDetails && root.contactDetails.isAdded
|
readonly property bool isUserAdded: root.contactDetails && root.contactDetails.isAdded
|
||||||
|
|
||||||
signal openAppSearch()
|
|
||||||
signal openStickerPackPopup(string stickerPackId)
|
signal openStickerPackPopup(string stickerPackId)
|
||||||
|
|
||||||
function requestAddressForTransaction(address, amount, tokenAddress, tokenDecimals = 18) {
|
function requestAddressForTransaction(address, amount, tokenAddress, tokenDecimals = 18) {
|
||||||
|
@ -98,6 +99,84 @@ Item {
|
||||||
root.createChatPropertiesStore.resetProperties()
|
root.createChatPropertiesStore.resetProperties()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
readonly property var activeChatContentModule: d.getChatContentModule(root.activeChatId)
|
||||||
|
|
||||||
|
readonly property UsersStore activeUsersStore: UsersStore {
|
||||||
|
usersModule: !!d.activeChatContentModule ? d.activeChatContentModule.usersModule : null
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property MessageStore activeMessagesStore: MessageStore {
|
||||||
|
messageModule: d.activeChatContentModule ? d.activeChatContentModule.messagesModule : null
|
||||||
|
chatSectionModule: root.rootStore.chatCommunitySectionModule
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChatContentModule(chatId) {
|
||||||
|
root.parentModule.prepareChatContentModuleForChatId(chatId)
|
||||||
|
return root.parentModule.getChatContentModule()
|
||||||
|
}
|
||||||
|
|
||||||
|
function showReplyArea(messageId) {
|
||||||
|
const obj = d.activeMessagesStore.getMessageByIdAsJson(messageId)
|
||||||
|
if (!obj)
|
||||||
|
return
|
||||||
|
chatInput.showReplyArea(messageId,
|
||||||
|
obj.senderDisplayName,
|
||||||
|
obj.messageText,
|
||||||
|
obj.contentType,
|
||||||
|
obj.messageImage,
|
||||||
|
obj.albumMessageImages,
|
||||||
|
obj.albumImagesCount,
|
||||||
|
obj.sticker)
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreInputReply() {
|
||||||
|
const replyMessageId = d.activeChatContentModule.inputAreaModule.preservedProperties.replyMessageId
|
||||||
|
if (replyMessageId)
|
||||||
|
d.showReplyArea(replyMessageId)
|
||||||
|
else
|
||||||
|
chatInput.resetReplyArea()
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreInputAttachments() {
|
||||||
|
const filesJson = d.activeChatContentModule.inputAreaModule.preservedProperties.fileUrlsAndSourcesJson
|
||||||
|
let filesList = []
|
||||||
|
if (filesJson) {
|
||||||
|
try {
|
||||||
|
filesList = JSON.parse(filesJson)
|
||||||
|
} catch(e) {
|
||||||
|
console.error("failed to parse preserved fileUrlsAndSources")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chatInput.resetImageArea()
|
||||||
|
chatInput.validateImagesAndShowImageArea(filesList)
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreInputState() {
|
||||||
|
|
||||||
|
if (!d.activeChatContentModule) {
|
||||||
|
chatInput.textInput.text = ""
|
||||||
|
chatInput.resetReplyArea()
|
||||||
|
chatInput.resetImageArea()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore message text
|
||||||
|
chatInput.textInput.text = d.activeChatContentModule.inputAreaModule.preservedProperties.text
|
||||||
|
chatInput.textInput.cursorPosition = chatInput.textInput.length
|
||||||
|
|
||||||
|
d.restoreInputReply()
|
||||||
|
d.restoreInputAttachments()
|
||||||
|
}
|
||||||
|
|
||||||
|
onActiveChatContentModuleChanged: {
|
||||||
|
// Call later to make sure activeUsersStore and activeMessagesStore bindings are updated
|
||||||
|
Qt.callLater(d.restoreInputState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EmptyChatPanel {
|
EmptyChatPanel {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: root.activeChatId === "" || root.chatsCount == 0
|
visible: root.activeChatId === "" || root.chatsCount == 0
|
||||||
|
@ -107,46 +186,150 @@ Item {
|
||||||
|
|
||||||
// This is kind of a solution for applying backend refactored changes with the minimal qml changes.
|
// This is kind of a solution for applying backend refactored changes with the minimal qml changes.
|
||||||
// The best would be if we made qml to follow the struct we have on the backend side.
|
// The best would be if we made qml to follow the struct we have on the backend side.
|
||||||
Repeater {
|
|
||||||
id: chatRepeater
|
|
||||||
model: parentModule && parentModule.model
|
|
||||||
|
|
||||||
ChatContentView {
|
ColumnLayout {
|
||||||
width: parent.width
|
anchors.fill: parent
|
||||||
height: parent.height
|
spacing: 0
|
||||||
visible: !root.rootStore.openCreateChat && isActiveChannel
|
|
||||||
chatId: model.itemId
|
Item {
|
||||||
chatType: model.type
|
Layout.fillWidth: true
|
||||||
chatMessagesLoader.active: model.loaderActive
|
Layout.fillHeight: true
|
||||||
rootStore: root.rootStore
|
|
||||||
contactsStore: root.contactsStore
|
Repeater {
|
||||||
|
id: chatRepeater
|
||||||
|
model: parentModule && parentModule.model
|
||||||
|
|
||||||
|
ChatContentView {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
visible: !root.rootStore.openCreateChat && model.active
|
||||||
|
chatId: model.itemId
|
||||||
|
chatType: model.type
|
||||||
|
chatMessagesLoader.active: model.loaderActive
|
||||||
|
rootStore: root.rootStore
|
||||||
|
contactsStore: root.contactsStore
|
||||||
|
emojiPopup: root.emojiPopup
|
||||||
|
stickersPopup: root.stickersPopup
|
||||||
|
stickersLoaded: root.stickersLoaded
|
||||||
|
isBlocked: model.blocked
|
||||||
|
onOpenStickerPackPopup: {
|
||||||
|
root.openStickerPackPopup(stickerPackId)
|
||||||
|
}
|
||||||
|
onShowReplyArea: (messageId) => {
|
||||||
|
d.showReplyArea(messageId)
|
||||||
|
}
|
||||||
|
onForceInputFocus: {
|
||||||
|
chatInput.forceInputActiveFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
chatContentModule = d.getChatContentModule(model.itemId)
|
||||||
|
chatSectionModule = root.parentModule
|
||||||
|
root.checkForCreateChatOptions(model.itemId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusChatInput {
|
||||||
|
id: chatInput
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: Style.current.smallPadding
|
||||||
|
|
||||||
|
enabled: root.rootStore.sectionDetails.joined && !root.rootStore.sectionDetails.amIBanned &&
|
||||||
|
root.rootStore.isUserAllowedToSendMessage
|
||||||
|
|
||||||
|
store: root.rootStore
|
||||||
|
usersStore: d.usersStore
|
||||||
|
|
||||||
|
textInput.placeholderText: {
|
||||||
|
if (d.activeChatContentModule.chatDetails.blocked)
|
||||||
|
return qsTr("This user has been blocked.")
|
||||||
|
if (!root.rootStore.sectionDetails.joined || root.rootStore.sectionDetails.amIBanned)
|
||||||
|
return qsTr("You need to join this community to send messages")
|
||||||
|
return root.rootStore.chatInputPlaceHolderText
|
||||||
|
}
|
||||||
|
|
||||||
emojiPopup: root.emojiPopup
|
emojiPopup: root.emojiPopup
|
||||||
stickersPopup: root.stickersPopup
|
stickersPopup: root.stickersPopup
|
||||||
sendTransactionNoEnsModal: cmpSendTransactionNoEns
|
isContactBlocked: d.activeChatContentModule.chatDetails.blocked
|
||||||
receiveTransactionModal: cmpReceiveTransaction
|
chatType: root.activeChatType
|
||||||
sendTransactionWithEnsModal: cmpSendTransactionWithEns
|
suggestions.suggestionFilter.addSystemSuggestions: chatType === Constants.chatType.communityChat
|
||||||
stickersLoaded: root.stickersLoaded
|
|
||||||
isBlocked: model.blocked
|
textInput.onTextChanged: {
|
||||||
isActiveChannel: model.active
|
d.activeChatContentModule.inputAreaModule.preservedProperties.text = textInput.text
|
||||||
onOpenStickerPackPopup: {
|
|
||||||
root.openStickerPackPopup(stickerPackId)
|
|
||||||
}
|
}
|
||||||
onOpenAppSearch: {
|
|
||||||
root.openAppSearch();
|
onReplyMessageIdChanged: {
|
||||||
|
d.activeChatContentModule.inputAreaModule.preservedProperties.replyMessageId = replyMessageId
|
||||||
}
|
}
|
||||||
Component.onCompleted: {
|
|
||||||
parentModule.prepareChatContentModuleForChatId(model.itemId)
|
onFileUrlsAndSourcesChanged: {
|
||||||
chatContentModule = parentModule.getChatContentModule()
|
d.activeChatContentModule.inputAreaModule.preservedProperties.fileUrlsAndSourcesJson = JSON.stringify(chatInput.fileUrlsAndSources)
|
||||||
chatSectionModule = root.parentModule
|
}
|
||||||
root.checkForCreateChatOptions(model.itemId)
|
|
||||||
|
onSendTransactionCommandButtonClicked: {
|
||||||
|
if (!d.activeChatContentModule) {
|
||||||
|
console.warn("error on sending transaction command - chat content module is not set")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Utils.isEnsVerified(d.activeChatContentModule.getMyChatId())) {
|
||||||
|
Global.openPopup(cmpSendTransactionWithEns)
|
||||||
|
} else {
|
||||||
|
Global.openPopup(cmpSendTransactionNoEns)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onReceiveTransactionCommandButtonClicked: {
|
||||||
|
Global.openPopup(cmpReceiveTransaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
onStickerSelected: {
|
||||||
|
root.rootStore.sendSticker(d.activeChatContentModule.getMyChatId(),
|
||||||
|
hashId,
|
||||||
|
chatInput.isReply ? chatInput.replyMessageId : "",
|
||||||
|
packId,
|
||||||
|
url)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onSendMessage: {
|
||||||
|
if (!d.activeChatContentModule) {
|
||||||
|
console.debug("error on sending message - chat content module is not set")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.rootStore.sendMessage(d.activeChatContentModule.getMyChatId(),
|
||||||
|
event,
|
||||||
|
chatInput.getTextWithPublicKeys(),
|
||||||
|
chatInput.isReply? chatInput.replyMessageId : "",
|
||||||
|
chatInput.fileUrlsAndSources
|
||||||
|
))
|
||||||
|
{
|
||||||
|
Global.playSendMessageSound()
|
||||||
|
|
||||||
|
chatInput.textInput.clear();
|
||||||
|
chatInput.textInput.textFormat = TextEdit.PlainText;
|
||||||
|
chatInput.textInput.textFormat = TextEdit.RichText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnblockChat: {
|
||||||
|
d.activeChatContentModule.unblockChat()
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyUpPress: {
|
||||||
|
d.activeMessagesStore.setEditModeOnLastMessage(root.rootStore.userProfileInst.pubKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: cmpSendTransactionNoEns
|
id: cmpSendTransactionNoEns
|
||||||
ChatCommandModal {
|
ChatCommandModal {
|
||||||
id: sendTransactionNoEns
|
|
||||||
store: root.rootStore
|
store: root.rootStore
|
||||||
contactsStore: root.contactsStore
|
contactsStore: root.contactsStore
|
||||||
onClosed: {
|
onClosed: {
|
||||||
|
@ -178,7 +361,6 @@ Item {
|
||||||
Component {
|
Component {
|
||||||
id: cmpReceiveTransaction
|
id: cmpReceiveTransaction
|
||||||
ChatCommandModal {
|
ChatCommandModal {
|
||||||
id: receiveTransaction
|
|
||||||
store: root.rootStore
|
store: root.rootStore
|
||||||
contactsStore: root.contactsStore
|
contactsStore: root.contactsStore
|
||||||
onClosed: {
|
onClosed: {
|
||||||
|
@ -209,7 +391,6 @@ Item {
|
||||||
Component {
|
Component {
|
||||||
id: cmpSendTransactionWithEns
|
id: cmpSendTransactionWithEns
|
||||||
SendModal {
|
SendModal {
|
||||||
id: sendTransactionWithEns
|
|
||||||
onClosed: {
|
onClosed: {
|
||||||
destroy()
|
destroy()
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,16 +24,12 @@ import "../stores"
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: root
|
id: root
|
||||||
objectName: "chatContentViewColumn"
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
// Important:
|
// Important: each chat/channel has its own ChatContentModule
|
||||||
// Each chat/channel has its own ChatContentModule
|
|
||||||
property var chatContentModule
|
property var chatContentModule
|
||||||
property var chatSectionModule
|
property var chatSectionModule
|
||||||
property var rootStore
|
property var rootStore
|
||||||
property var contactsStore
|
property var contactsStore
|
||||||
property bool isActiveChannel: false
|
|
||||||
property string chatId
|
property string chatId
|
||||||
property int chatType: Constants.chatType.unknown
|
property int chatType: Constants.chatType.unknown
|
||||||
|
|
||||||
|
@ -43,22 +39,27 @@ ColumnLayout {
|
||||||
property var stickersPopup
|
property var stickersPopup
|
||||||
property UsersStore usersStore: UsersStore {}
|
property UsersStore usersStore: UsersStore {}
|
||||||
|
|
||||||
onChatContentModuleChanged: if (!!chatContentModule) {
|
|
||||||
root.usersStore.usersModule = root.chatContentModule.usersModule
|
|
||||||
}
|
|
||||||
|
|
||||||
signal openAppSearch()
|
|
||||||
signal openStickerPackPopup(string stickerPackId)
|
signal openStickerPackPopup(string stickerPackId)
|
||||||
|
|
||||||
property Component sendTransactionNoEnsModal
|
|
||||||
property Component receiveTransactionModal
|
|
||||||
property Component sendTransactionWithEnsModal
|
|
||||||
|
|
||||||
property bool isBlocked: false
|
property bool isBlocked: false
|
||||||
property bool isUserAllowedToSendMessage: root.rootStore.isUserAllowedToSendMessage
|
property bool isUserAllowedToSendMessage: root.rootStore.isUserAllowedToSendMessage
|
||||||
property string chatInputPlaceholder: root.rootStore.chatInputPlaceHolderText
|
|
||||||
property bool stickersLoaded: false
|
property bool stickersLoaded: false
|
||||||
|
|
||||||
|
readonly property var messageStore: MessageStore {
|
||||||
|
messageModule: chatContentModule ? chatContentModule.messagesModule : null
|
||||||
|
chatSectionModule: root.rootStore.chatCommunitySectionModule
|
||||||
|
}
|
||||||
|
|
||||||
|
signal showReplyArea(messageId: string)
|
||||||
|
signal forceInputFocus()
|
||||||
|
|
||||||
|
objectName: "chatContentViewColumn"
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
onChatContentModuleChanged: if (!!chatContentModule) {
|
||||||
|
root.usersStore.usersModule = root.chatContentModule.usersModule
|
||||||
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
active: root.isBlocked
|
active: root.isBlocked
|
||||||
|
@ -69,184 +70,33 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property var messageStore: MessageStore {
|
Loader {
|
||||||
messageModule: chatContentModule ? chatContentModule.messagesModule : null
|
id: chatMessagesLoader
|
||||||
chatSectionModule: root.rootStore.chatCommunitySectionModule
|
|
||||||
}
|
|
||||||
|
|
||||||
QtObject {
|
|
||||||
id: d
|
|
||||||
|
|
||||||
readonly property string blockedText: qsTr("This user has been blocked.")
|
|
||||||
|
|
||||||
function showReplyArea(messageId) {
|
|
||||||
let obj = messageStore.getMessageByIdAsJson(messageId)
|
|
||||||
if (!obj) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (inputAreaLoader.item) {
|
|
||||||
inputAreaLoader.item.chatInput.showReplyArea(messageId, obj.senderDisplayName, obj.messageText, obj.contentType, obj.messageImage, obj.albumMessageImages, obj.albumImagesCount, obj.sticker)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
clip: true
|
|
||||||
|
|
||||||
Loader {
|
sourceComponent: ChatMessagesView {
|
||||||
id: chatMessagesLoader
|
chatContentModule: root.chatContentModule
|
||||||
Layout.fillWidth: true
|
rootStore: root.rootStore
|
||||||
Layout.fillHeight: true
|
contactsStore: root.contactsStore
|
||||||
|
messageStore: root.messageStore
|
||||||
sourceComponent: ChatMessagesView {
|
emojiPopup: root.emojiPopup
|
||||||
chatContentModule: root.chatContentModule
|
stickersPopup: root.stickersPopup
|
||||||
rootStore: root.rootStore
|
usersStore: root.usersStore
|
||||||
contactsStore: root.contactsStore
|
stickersLoaded: root.stickersLoaded
|
||||||
messageStore: root.messageStore
|
chatId: root.chatId
|
||||||
emojiPopup: root.emojiPopup
|
isOneToOne: root.chatType === Constants.chatType.oneToOne
|
||||||
stickersPopup: root.stickersPopup
|
isChatBlocked: root.isBlocked || !root.isUserAllowedToSendMessage
|
||||||
usersStore: root.usersStore
|
channelEmoji: !chatContentModule ? "" : (chatContentModule.chatDetails.emoji || "")
|
||||||
stickersLoaded: root.stickersLoaded
|
onShowReplyArea: (messageId, senderId) => {
|
||||||
chatId: root.chatId
|
root.showReplyArea(messageId)
|
||||||
isOneToOne: root.chatType === Constants.chatType.oneToOne
|
|
||||||
isContactBlocked: root.isBlocked
|
|
||||||
isChatBlocked: root.isBlocked || !root.isUserAllowedToSendMessage
|
|
||||||
channelEmoji: !chatContentModule ? "" : (chatContentModule.chatDetails.emoji || "")
|
|
||||||
isActiveChannel: root.isActiveChannel
|
|
||||||
onShowReplyArea: (messageId, senderId) => {
|
|
||||||
d.showReplyArea(messageId)
|
|
||||||
}
|
|
||||||
onOpenStickerPackPopup: {
|
|
||||||
root.openStickerPackPopup(stickerPackId);
|
|
||||||
}
|
|
||||||
onEditModeChanged: {
|
|
||||||
if (!editModeOn && inputAreaLoader.item)
|
|
||||||
inputAreaLoader.item.chatInput.forceInputActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
onOpenStickerPackPopup: {
|
||||||
|
root.openStickerPackPopup(stickerPackId);
|
||||||
Loader {
|
|
||||||
id: inputAreaLoader
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
active: root.isActiveChannel
|
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
property string preservedText
|
|
||||||
Binding on preservedText {
|
|
||||||
when: inputAreaLoader.item != null
|
|
||||||
value: inputAreaLoader.item ? inputAreaLoader.item.chatInput.textInput.text : inputAreaLoader.preservedText
|
|
||||||
restoreMode: Binding.RestoreNone
|
|
||||||
delayed: true
|
|
||||||
}
|
}
|
||||||
|
onEditModeChanged: {
|
||||||
// FIXME: `StatusChatInput` is way too heavy
|
if (!editModeOn)
|
||||||
// see: https://github.com/status-im/status-desktop/pull/10343#issuecomment-1515103756
|
root.forceInputFocus()
|
||||||
sourceComponent: Item {
|
|
||||||
id: inputArea
|
|
||||||
implicitHeight: chatInput.implicitHeight
|
|
||||||
+ chatInput.anchors.topMargin
|
|
||||||
+ chatInput.anchors.bottomMargin
|
|
||||||
|
|
||||||
readonly property alias chatInput: chatInput
|
|
||||||
|
|
||||||
StatusChatInput {
|
|
||||||
id: chatInput
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Style.current.smallPadding
|
|
||||||
|
|
||||||
// We enable the component if the contact is blocked, because if we disable it, the `Unban` button
|
|
||||||
// becomes disabled. All the local components inside already disable themselves when blocked
|
|
||||||
enabled: root.isBlocked ||
|
|
||||||
(root.rootStore.sectionDetails.joined && !root.rootStore.sectionDetails.amIBanned &&
|
|
||||||
root.isUserAllowedToSendMessage)
|
|
||||||
|
|
||||||
store: root.rootStore
|
|
||||||
usersStore: root.usersStore
|
|
||||||
|
|
||||||
textInput.text: inputAreaLoader.preservedText
|
|
||||||
textInput.placeholderText: root.isBlocked ? d.blockedText : root.chatInputPlaceholder
|
|
||||||
emojiPopup: root.emojiPopup
|
|
||||||
stickersPopup: root.stickersPopup
|
|
||||||
isContactBlocked: root.isBlocked
|
|
||||||
isActiveChannel: root.isActiveChannel
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
chatType: root.chatType
|
|
||||||
suggestions.suggestionFilter.addSystemSuggestions: chatType === Constants.chatType.communityChat
|
|
||||||
|
|
||||||
Binding on chatInputPlaceholder {
|
|
||||||
when: root.isBlocked
|
|
||||||
value: d.blockedText
|
|
||||||
}
|
|
||||||
|
|
||||||
Binding on chatInputPlaceholder {
|
|
||||||
when: !root.rootStore.sectionDetails.joined || root.rootStore.sectionDetails.amIBanned
|
|
||||||
value: qsTr("You need to join this community to send messages")
|
|
||||||
}
|
|
||||||
|
|
||||||
onSendTransactionCommandButtonClicked: {
|
|
||||||
if(!chatContentModule) {
|
|
||||||
console.debug("error on sending transaction command - chat content module is not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Utils.isEnsVerified(chatContentModule.getMyChatId())) {
|
|
||||||
Global.openPopup(root.sendTransactionWithEnsModal)
|
|
||||||
} else {
|
|
||||||
Global.openPopup(root.sendTransactionNoEnsModal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onReceiveTransactionCommandButtonClicked: {
|
|
||||||
Global.openPopup(root.receiveTransactionModal)
|
|
||||||
}
|
|
||||||
onStickerSelected: {
|
|
||||||
root.rootStore.sendSticker(chatContentModule.getMyChatId(),
|
|
||||||
hashId,
|
|
||||||
chatInput.isReply ? chatInput.replyMessageId : "",
|
|
||||||
packId,
|
|
||||||
url)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
onSendMessage: {
|
|
||||||
if (!chatContentModule) {
|
|
||||||
console.debug("error on sending message - chat content module is not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if(root.rootStore.sendMessage(chatContentModule.getMyChatId(),
|
|
||||||
event,
|
|
||||||
chatInput.getTextWithPublicKeys(),
|
|
||||||
chatInput.isReply? chatInput.replyMessageId : "",
|
|
||||||
chatInput.fileUrlsAndSources
|
|
||||||
))
|
|
||||||
{
|
|
||||||
Global.playSendMessageSound()
|
|
||||||
|
|
||||||
chatInput.textInput.clear();
|
|
||||||
chatInput.textInput.textFormat = TextEdit.PlainText;
|
|
||||||
chatInput.textInput.textFormat = TextEdit.RichText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onUnblockChat: {
|
|
||||||
chatContentModule.unblockChat()
|
|
||||||
}
|
|
||||||
onKeyUpPress: messageStore.setEditModeOnLastMessage(root.rootStore.userProfileInst.pubKey)
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
Qt.callLater(() => {
|
|
||||||
forceInputActiveFocus()
|
|
||||||
textInput.cursorPosition = textInput.length
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -251,7 +251,6 @@ Item {
|
||||||
chatLogView: ListView.view
|
chatLogView: ListView.view
|
||||||
chatContentModule: root.chatContentModule
|
chatContentModule: root.chatContentModule
|
||||||
|
|
||||||
isActiveChannel: root.isActiveChannel
|
|
||||||
isChatBlocked: root.isChatBlocked
|
isChatBlocked: root.isChatBlocked
|
||||||
|
|
||||||
chatId: root.chatId
|
chatId: root.chatId
|
||||||
|
|
|
@ -89,9 +89,6 @@ StatusSectionLayout {
|
||||||
onOpenStickerPackPopup: {
|
onOpenStickerPackPopup: {
|
||||||
Global.openPopup(statusStickerPackClickPopup, {packId: stickerPackId, store: root.stickersPopup.store} )
|
Global.openPopup(statusStickerPackClickPopup, {packId: stickerPackId, store: root.stickersPopup.store} )
|
||||||
}
|
}
|
||||||
onOpenAppSearch: {
|
|
||||||
root.openAppSearch();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showRightPanel: {
|
showRightPanel: {
|
||||||
|
|
|
@ -433,7 +433,6 @@ Item {
|
||||||
enabled: communityData.amISectionAdmin
|
enabled: communityData.amISectionAdmin
|
||||||
acceptedButtons: Qt.RightButton
|
acceptedButtons: Qt.RightButton
|
||||||
onTapped: {
|
onTapped: {
|
||||||
console.log("<<< tapped")
|
|
||||||
adminPopupMenu.showInviteButton = true
|
adminPopupMenu.showInviteButton = true
|
||||||
adminPopupMenu.x = eventPoint.position.x + 4
|
adminPopupMenu.x = eventPoint.position.x + 4
|
||||||
adminPopupMenu.y = eventPoint.position.y + 4
|
adminPopupMenu.y = eventPoint.position.y + 4
|
||||||
|
|
|
@ -36,19 +36,16 @@ Rectangle {
|
||||||
property var emojiPopup: null
|
property var emojiPopup: null
|
||||||
property var stickersPopup: null
|
property var stickersPopup: null
|
||||||
// Use this to only enable the Connections only when this Input opens the Emoji popup
|
// Use this to only enable the Connections only when this Input opens the Emoji popup
|
||||||
property bool emojiPopupOpened: false
|
|
||||||
property bool stickersPopupOpened: false
|
|
||||||
property bool closeGifPopupAfterSelection: true
|
property bool closeGifPopupAfterSelection: true
|
||||||
|
|
||||||
property bool emojiEvent: false
|
property bool emojiEvent: false
|
||||||
property bool isColonPressed: false
|
property bool isColonPressed: false
|
||||||
property bool isReply: false
|
property bool isReply: false
|
||||||
property string replyMessageId: replyArea.messageId
|
readonly property string replyMessageId: replyArea.messageId
|
||||||
|
|
||||||
property bool isImage: false
|
property bool isImage: false
|
||||||
property bool isEdit: false
|
property bool isEdit: false
|
||||||
property bool isContactBlocked: false
|
property bool isContactBlocked: false
|
||||||
property bool isActiveChannel: false
|
|
||||||
|
|
||||||
property int messageLimit: 2000
|
property int messageLimit: 2000
|
||||||
property int messageLimitVisible: 200
|
property int messageLimitVisible: 200
|
||||||
|
@ -131,36 +128,52 @@ Rectangle {
|
||||||
property int leftOfMentionIndex: -1
|
property int leftOfMentionIndex: -1
|
||||||
property int rightOfMentionIndex: -1
|
property int rightOfMentionIndex: -1
|
||||||
readonly property int nbEmojisInClipboard: StatusQUtils.Emoji.nbEmojis(QClipboardProxy.html)
|
readonly property int nbEmojisInClipboard: StatusQUtils.Emoji.nbEmojis(QClipboardProxy.html)
|
||||||
|
|
||||||
|
property bool emojiPopupOpened: false
|
||||||
|
property bool stickersPopupOpened: false
|
||||||
|
|
||||||
|
// common popups are emoji, jif and stickers
|
||||||
|
// Put controlWidth as argument with default value for binding
|
||||||
|
function getCommonPopupRelativePosition(popup, popupParent, controlWidth = control.width) {
|
||||||
|
const controlX = controlWidth - emojiPopup.width - Style.current.halfPadding
|
||||||
|
const controlY = -emojiPopup.height
|
||||||
|
return popupParent.mapFromItem(control, controlX, controlY)
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property point emojiPopupPosition: getCommonPopupRelativePosition(emojiPopup, emojiBtn)
|
||||||
|
readonly property point stickersPopupPosition: getCommonPopupRelativePosition(stickersPopup, stickersBtn)
|
||||||
|
|
||||||
readonly property StateGroup emojiPopupTakeover: StateGroup {
|
readonly property StateGroup emojiPopupTakeover: StateGroup {
|
||||||
states: State {
|
states: State {
|
||||||
when: control.emojiPopupOpened
|
when: d.emojiPopupOpened
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: emojiPopup
|
target: emojiPopup
|
||||||
|
|
||||||
parent: control
|
parent: emojiBtn
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
||||||
x: control.width - emojiPopup.width - Style.current.halfPadding
|
x: d.emojiPopupPosition.x
|
||||||
y: -emojiPopup.height
|
y: d.emojiPopupPosition.y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
readonly property StateGroup stickersPopupTakeover: StateGroup {
|
readonly property StateGroup stickersPopupTakeover: StateGroup {
|
||||||
states: State {
|
states: State {
|
||||||
when: control.stickersPopupOpened
|
when: d.stickersPopupOpened
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: stickersPopup
|
target: stickersPopup
|
||||||
|
|
||||||
parent: control
|
parent: stickersBtn
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
||||||
x: control.width - stickersPopup.width - Style.current.halfPadding
|
x: d.stickersPopupPosition.x
|
||||||
y: -stickersPopup.height
|
y: d.stickersPopupPosition.y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property bool chatCommandsPopupOpen: false
|
property bool chatCommandsPopupOpen: false
|
||||||
|
property Menu textFormatMenu: null
|
||||||
|
|
||||||
function copyMentions(start, end) {
|
function copyMentions(start, end) {
|
||||||
copiedMentionsPos = []
|
copiedMentionsPos = []
|
||||||
|
@ -268,30 +281,8 @@ Rectangle {
|
||||||
messageInputField.insert(start, text.replace(/\n/g, "<br/>"));
|
messageInputField.insert(start, text.replace(/\n/g, "<br/>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
function togglePopup(popup, btn) {
|
|
||||||
if (popup !== control.stickersPopup) {
|
|
||||||
control.stickersPopup.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (popup !== gifPopup) {
|
|
||||||
gifPopup.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (popup !== emojiPopup) {
|
|
||||||
emojiPopup.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (popup.opened) {
|
|
||||||
popup.close()
|
|
||||||
btn.highlighted = false
|
|
||||||
} else {
|
|
||||||
popup.open()
|
|
||||||
btn.highlighted = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
enabled: control.emojiPopupOpened
|
enabled: d.emojiPopupOpened
|
||||||
target: emojiPopup
|
target: emojiPopup
|
||||||
|
|
||||||
function onEmojiSelected(text: string, atCursor: bool) {
|
function onEmojiSelected(text: string, atCursor: bool) {
|
||||||
|
@ -300,13 +291,12 @@ Rectangle {
|
||||||
messageInputField.forceActiveFocus();
|
messageInputField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
function onClosed() {
|
function onClosed() {
|
||||||
emojiBtn.highlighted = false
|
d.emojiPopupOpened = false
|
||||||
control.emojiPopupOpened = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
enabled: control.stickersPopupOpened
|
enabled: d.stickersPopupOpened
|
||||||
target: control.stickersPopup
|
target: control.stickersPopup
|
||||||
|
|
||||||
function onStickerSelected(hashId: string, packId: string, url: string ) {
|
function onStickerSelected(hashId: string, packId: string, url: string ) {
|
||||||
|
@ -315,15 +305,14 @@ Rectangle {
|
||||||
messageInputField.forceActiveFocus();
|
messageInputField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
function onClosed() {
|
function onClosed() {
|
||||||
stickersBtn.highlighted = false
|
d.stickersPopupOpened = false
|
||||||
control.stickersPopupOpened = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property var mentionsPos: []
|
property var mentionsPos: []
|
||||||
|
|
||||||
function isUploadFilePressed(event) {
|
function isUploadFilePressed(event) {
|
||||||
return (event.key === Qt.Key_U) && (event.modifiers & Qt.ControlModifier) && imageBtn.visible && !imageDialog.visible
|
return (event.key === Qt.Key_U) && (event.modifiers & Qt.ControlModifier) && imageBtn.visible && !imageBtn.highlighted
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkTextInsert() {
|
function checkTextInsert() {
|
||||||
|
@ -354,7 +343,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
if (event) {
|
if (event) {
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
messageTooLongDialog.open()
|
console.error("Attempting to send a message exceeding length limit")
|
||||||
}
|
}
|
||||||
} else if (event.key === Qt.Key_Escape && control.isReply) {
|
} else if (event.key === Qt.Key_Escape && control.isReply) {
|
||||||
control.isReply = false
|
control.isReply = false
|
||||||
|
@ -664,8 +653,8 @@ Rectangle {
|
||||||
if ((event.modifiers & Qt.ControlModifier) || (event.modifiers & Qt.MetaModifier)) // these are likely shortcuts with no meaningful text
|
if ((event.modifiers & Qt.ControlModifier) || (event.modifiers & Qt.MetaModifier)) // these are likely shortcuts with no meaningful text
|
||||||
return
|
return
|
||||||
|
|
||||||
if (event.key === Qt.Key_Backspace && textFormatMenu.opened) {
|
if (event.key === Qt.Key_Backspace && d.textFormatMenu.opened) {
|
||||||
textFormatMenu.close()
|
d.textFormatMenu.close()
|
||||||
}
|
}
|
||||||
// the text doesn't get registered to the textarea fast enough
|
// the text doesn't get registered to the textarea fast enough
|
||||||
// we can only get it in the `released` event
|
// we can only get it in the `released` event
|
||||||
|
@ -883,19 +872,27 @@ Rectangle {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideExtendedArea() {
|
function resetImageArea() {
|
||||||
isImage = false;
|
isImage = false;
|
||||||
isReply = false;
|
|
||||||
control.fileUrlsAndSources = []
|
control.fileUrlsAndSources = []
|
||||||
imageArea.imageSource = [];
|
imageArea.imageSource = [];
|
||||||
replyArea.userName = ""
|
|
||||||
replyArea.message = ""
|
|
||||||
for (let i=0; i<validators.children.length; i++) {
|
for (let i=0; i<validators.children.length; i++) {
|
||||||
const validator = validators.children[i]
|
const validator = validators.children[i]
|
||||||
validator.images = []
|
validator.images = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetReplyArea() {
|
||||||
|
isReply = false;
|
||||||
|
replyArea.userName = ""
|
||||||
|
replyArea.message = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideExtendedArea() {
|
||||||
|
resetImageArea()
|
||||||
|
resetReplyArea()
|
||||||
|
}
|
||||||
|
|
||||||
function validateImages(imagePaths) {
|
function validateImages(imagePaths) {
|
||||||
if (!imagePaths || !imagePaths.length) {
|
if (!imagePaths || !imagePaths.length) {
|
||||||
return []
|
return []
|
||||||
|
@ -912,7 +909,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
function showImageArea(imagePathsOrData) {
|
function showImageArea(imagePathsOrData) {
|
||||||
isImage = true;
|
isImage = imagePathsOrData.length > 0
|
||||||
imageArea.imageSource = imagePathsOrData
|
imageArea.imageSource = imagePathsOrData
|
||||||
control.fileUrlsAndSources = imageArea.imageSource
|
control.fileUrlsAndSources = imageArea.imageSource
|
||||||
}
|
}
|
||||||
|
@ -921,12 +918,8 @@ Rectangle {
|
||||||
// Returns true if the images were valid and added
|
// Returns true if the images were valid and added
|
||||||
function validateImagesAndShowImageArea(imagePaths) {
|
function validateImagesAndShowImageArea(imagePaths) {
|
||||||
const validImages = validateImages(imagePaths)
|
const validImages = validateImages(imagePaths)
|
||||||
|
showImageArea(validImages)
|
||||||
if (validImages.length > 0) {
|
return isImage
|
||||||
showImageArea(validImages)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showReplyArea(messageId, userName, message, contentType, image, album, albumCount, sticker) {
|
function showReplyArea(messageId, userName, message, contentType, image, album, albumCount, sticker) {
|
||||||
|
@ -947,7 +940,6 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
enabled: control.isActiveChannel
|
|
||||||
target: Global.dragArea
|
target: Global.dragArea
|
||||||
ignoreUnknownSignals: true
|
ignoreUnknownSignals: true
|
||||||
function onDroppedOnValidScreen(drop) {
|
function onDroppedOnValidScreen(drop) {
|
||||||
|
@ -963,30 +955,25 @@ Rectangle {
|
||||||
messageInputField.forceActiveFocus();
|
messageInputField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDialog {
|
Component {
|
||||||
id: imageDialog
|
id: imageDialogComponent
|
||||||
title: qsTr("Please choose an image")
|
|
||||||
folder: shortcuts.pictures
|
|
||||||
selectMultiple: true
|
|
||||||
nameFilters: [
|
|
||||||
qsTr("Image files (%1)").arg(Constants.acceptedDragNDropImageExtensions.map(img => "*" + img).join(" "))
|
|
||||||
]
|
|
||||||
onAccepted: {
|
|
||||||
imageBtn.highlighted = false
|
|
||||||
validateImagesAndShowImageArea(imageDialog.fileUrls)
|
|
||||||
messageInputField.forceActiveFocus();
|
|
||||||
}
|
|
||||||
onRejected: {
|
|
||||||
imageBtn.highlighted = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageDialog {
|
FileDialog {
|
||||||
id: messageTooLongDialog
|
title: qsTr("Please choose an image")
|
||||||
title: qsTr("Your message is too long.")
|
folder: shortcuts.pictures
|
||||||
icon: StandardIcon.Critical
|
selectMultiple: true
|
||||||
text: qsTr("Please make your message shorter. We have set the limit to 2000 characters to be courteous of others.")
|
nameFilters: [
|
||||||
standardButtons: StandardButton.Ok
|
qsTr("Image files (%1)").arg(Constants.acceptedDragNDropImageExtensions.map(img => "*" + img).join(" "))
|
||||||
|
]
|
||||||
|
onAccepted: {
|
||||||
|
imageBtn.highlighted = false
|
||||||
|
validateImagesAndShowImageArea(fileUrls)
|
||||||
|
messageInputField.forceActiveFocus()
|
||||||
|
}
|
||||||
|
onRejected: {
|
||||||
|
imageBtn.highlighted = false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusEmojiSuggestionPopup {
|
StatusEmojiSuggestionPopup {
|
||||||
|
@ -1026,51 +1013,59 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: chatCommandsPopup
|
id: chatCommandsPopupComponent
|
||||||
|
|
||||||
ChatCommandsPopup {
|
ChatCommandsPopup {
|
||||||
x: (parent.width - control.width - Style.current.halfPadding)
|
id: chatCommandsPopup
|
||||||
y: 716
|
x: 8
|
||||||
|
y: -height
|
||||||
onSendTransactionCommandButtonClicked: {
|
onSendTransactionCommandButtonClicked: {
|
||||||
control.sendTransactionCommandButtonClicked();
|
control.sendTransactionCommandButtonClicked()
|
||||||
chatCommandsPopup.close();
|
close()
|
||||||
}
|
}
|
||||||
onReceiveTransactionCommandButtonClicked: {
|
onReceiveTransactionCommandButtonClicked: {
|
||||||
control.receiveTransactionCommandButtonClicked();
|
control.receiveTransactionCommandButtonClicked()
|
||||||
chatCommandsPopup.close();
|
close()
|
||||||
}
|
}
|
||||||
onClosed: {
|
onClosed: {
|
||||||
chatCommandsBtnLoader.highlighted = false;
|
chatCommandsBtn.highlighted = false
|
||||||
destroy();
|
destroy()
|
||||||
|
}
|
||||||
|
onOpened: {
|
||||||
|
chatCommandsBtn.highlighted = true
|
||||||
}
|
}
|
||||||
Component.onDestruction: {
|
Component.onDestruction: {
|
||||||
if (d.chatCommandsPopupOpen)
|
if (d.chatCommandsPopupOpen)
|
||||||
d.chatCommandsPopupOpen = false;
|
d.chatCommandsPopupOpen = false;
|
||||||
}
|
}
|
||||||
onOpened: {
|
|
||||||
chatCommandsBtnLoader.highlighted = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusGifPopup {
|
Component {
|
||||||
id: gifPopup
|
id: gifPopupComponent
|
||||||
width: 360
|
|
||||||
height: 440
|
StatusGifPopup {
|
||||||
x: control.width - width - Style.current.halfPadding
|
readonly property point relativePosition: d.getCommonPopupRelativePosition(this, parent)
|
||||||
y: -height
|
|
||||||
gifSelected: function (event, url) {
|
width: 360
|
||||||
messageInputField.text += "\n" + url
|
height: 440
|
||||||
control.sendMessage(event)
|
x: relativePosition.x
|
||||||
control.isReply = false
|
y: relativePosition.y
|
||||||
gifBtn.highlighted = false
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
||||||
messageInputField.forceActiveFocus()
|
|
||||||
if (control.closeGifPopupAfterSelection)
|
gifSelected: function (event, url) {
|
||||||
gifPopup.close()
|
messageInputField.text += "\n" + url
|
||||||
}
|
control.sendMessage(event)
|
||||||
onClosed: {
|
control.isReply = false
|
||||||
gifBtn.highlighted = false
|
messageInputField.forceActiveFocus()
|
||||||
|
if (control.closeGifPopupAfterSelection)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
onClosed: {
|
||||||
|
gifBtn.popup = null
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1091,12 +1086,12 @@ Rectangle {
|
||||||
Layout.bottomMargin: 4
|
Layout.bottomMargin: 4
|
||||||
icon.name: "chat-commands"
|
icon.name: "chat-commands"
|
||||||
type: StatusQ.StatusFlatRoundButton.Type.Tertiary
|
type: StatusQ.StatusFlatRoundButton.Type.Tertiary
|
||||||
visible: RootStore.isWalletEnabled && !isEdit && control.chatType === Constants.chatType.oneToOne
|
|
||||||
enabled: !control.isContactBlocked
|
enabled: !control.isContactBlocked
|
||||||
onClicked: {
|
onClicked: {
|
||||||
d.chatCommandsPopupOpen ? Global.closePopup() : Global.openPopup(chatCommandsPopup);
|
d.chatCommandsPopupOpen ? Global.closePopup() : Global.openPopup(chatCommandsPopup);
|
||||||
d.chatCommandsPopupOpen = !d.chatCommandsPopupOpen;
|
d.chatCommandsPopupOpen = !d.chatCommandsPopupOpen;
|
||||||
}
|
}
|
||||||
|
visible: RootStore.isWalletEnabled && !isEdit && control.chatType === Constants.chatType.oneToOne
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1112,7 +1107,8 @@ Rectangle {
|
||||||
enabled: !control.isContactBlocked
|
enabled: !control.isContactBlocked
|
||||||
onClicked: {
|
onClicked: {
|
||||||
highlighted = true
|
highlighted = true
|
||||||
imageDialog.open()
|
const popup = imageDialogComponent.createObject(control)
|
||||||
|
popup.open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1123,7 +1119,6 @@ Rectangle {
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
|
||||||
implicitHeight: inputLayout.implicitHeight + inputLayout.anchors.topMargin + inputLayout.anchors.bottomMargin
|
implicitHeight: inputLayout.implicitHeight + inputLayout.anchors.topMargin + inputLayout.anchors.bottomMargin
|
||||||
implicitWidth: inputLayout.implicitWidth + inputLayout.anchors.leftMargin + inputLayout.anchors.rightMargin
|
implicitWidth: inputLayout.implicitWidth + inputLayout.anchors.leftMargin + inputLayout.anchors.rightMargin
|
||||||
|
|
||||||
|
@ -1131,60 +1126,67 @@ Rectangle {
|
||||||
color: isEdit ? Theme.palette.statusChatInput.secondaryBackgroundColor : Style.current.inputBackground
|
color: isEdit ? Theme.palette.statusChatInput.secondaryBackgroundColor : Style.current.inputBackground
|
||||||
radius: 20
|
radius: 20
|
||||||
|
|
||||||
StatusTextFormatMenu {
|
Component {
|
||||||
id: textFormatMenu
|
id: textFormatMenuComponent
|
||||||
|
|
||||||
StatusChatInputTextFormationAction {
|
StatusTextFormatMenu {
|
||||||
wrapper: "**"
|
id: textFormatMenu
|
||||||
icon.name: "bold"
|
|
||||||
text: qsTr("Bold")
|
|
||||||
selectedTextWithFormationChars: RootStore.getSelectedTextWithFormationChars(messageInputField)
|
|
||||||
onActionTriggered: checked ?
|
|
||||||
unwrapSelection(wrapper, RootStore.getSelectedTextWithFormationChars(messageInputField)) :
|
|
||||||
wrapSelection(wrapper)
|
|
||||||
}
|
|
||||||
StatusChatInputTextFormationAction {
|
|
||||||
wrapper: "*"
|
|
||||||
icon.name: "italic"
|
|
||||||
text: qsTr("Italic")
|
|
||||||
selectedTextWithFormationChars: RootStore.getSelectedTextWithFormationChars(messageInputField)
|
|
||||||
checked: (surroundedBy("*") && !surroundedBy("**")) || surroundedBy("***")
|
|
||||||
onActionTriggered: checked ?
|
|
||||||
unwrapSelection(wrapper, RootStore.getSelectedTextWithFormationChars(messageInputField)) :
|
|
||||||
wrapSelection(wrapper)
|
|
||||||
}
|
|
||||||
StatusChatInputTextFormationAction {
|
|
||||||
wrapper: "~~"
|
|
||||||
icon.name: "strikethrough"
|
|
||||||
text: qsTr("Strikethrough")
|
|
||||||
selectedTextWithFormationChars: RootStore.getSelectedTextWithFormationChars(messageInputField)
|
|
||||||
onActionTriggered: checked ?
|
|
||||||
unwrapSelection(wrapper, RootStore.getSelectedTextWithFormationChars(messageInputField)) :
|
|
||||||
wrapSelection(wrapper)
|
|
||||||
}
|
|
||||||
StatusChatInputTextFormationAction {
|
|
||||||
wrapper: "`"
|
|
||||||
icon.name: "code"
|
|
||||||
text: qsTr("Code")
|
|
||||||
selectedTextWithFormationChars: RootStore.getSelectedTextWithFormationChars(messageInputField)
|
|
||||||
onActionTriggered: checked ?
|
|
||||||
unwrapSelection(wrapper, RootStore.getSelectedTextWithFormationChars(messageInputField)) :
|
|
||||||
wrapSelection(wrapper)
|
|
||||||
}
|
|
||||||
StatusChatInputTextFormationAction {
|
|
||||||
wrapper: "> "
|
|
||||||
icon.name: "quote"
|
|
||||||
text: qsTr("Quote")
|
|
||||||
checked: messageInputField.selectedText && isSelectedLinePrefixedBy(messageInputField.selectionStart, wrapper)
|
|
||||||
|
|
||||||
onActionTriggered: checked
|
onClosed: {
|
||||||
? unprefixSelectedLine(wrapper)
|
messageInputField.deselect()
|
||||||
: prefixSelectedLine(wrapper)
|
destroy()
|
||||||
}
|
}
|
||||||
onClosed: {
|
|
||||||
messageInputField.deselect();
|
StatusChatInputTextFormationAction {
|
||||||
|
wrapper: "**"
|
||||||
|
icon.name: "bold"
|
||||||
|
text: qsTr("Bold")
|
||||||
|
selectedTextWithFormationChars: RootStore.getSelectedTextWithFormationChars(messageInputField)
|
||||||
|
onActionTriggered: checked ?
|
||||||
|
unwrapSelection(wrapper, RootStore.getSelectedTextWithFormationChars(messageInputField)) :
|
||||||
|
wrapSelection(wrapper)
|
||||||
|
}
|
||||||
|
StatusChatInputTextFormationAction {
|
||||||
|
wrapper: "*"
|
||||||
|
icon.name: "italic"
|
||||||
|
text: qsTr("Italic")
|
||||||
|
selectedTextWithFormationChars: RootStore.getSelectedTextWithFormationChars(messageInputField)
|
||||||
|
checked: (surroundedBy("*") && !surroundedBy("**")) || surroundedBy("***")
|
||||||
|
onActionTriggered: checked ?
|
||||||
|
unwrapSelection(wrapper, RootStore.getSelectedTextWithFormationChars(messageInputField)) :
|
||||||
|
wrapSelection(wrapper)
|
||||||
|
}
|
||||||
|
StatusChatInputTextFormationAction {
|
||||||
|
wrapper: "~~"
|
||||||
|
icon.name: "strikethrough"
|
||||||
|
text: qsTr("Strikethrough")
|
||||||
|
selectedTextWithFormationChars: RootStore.getSelectedTextWithFormationChars(messageInputField)
|
||||||
|
onActionTriggered: checked ?
|
||||||
|
unwrapSelection(wrapper, RootStore.getSelectedTextWithFormationChars(messageInputField)) :
|
||||||
|
wrapSelection(wrapper)
|
||||||
|
}
|
||||||
|
StatusChatInputTextFormationAction {
|
||||||
|
wrapper: "`"
|
||||||
|
icon.name: "code"
|
||||||
|
text: qsTr("Code")
|
||||||
|
selectedTextWithFormationChars: RootStore.getSelectedTextWithFormationChars(messageInputField)
|
||||||
|
onActionTriggered: checked ?
|
||||||
|
unwrapSelection(wrapper, RootStore.getSelectedTextWithFormationChars(messageInputField)) :
|
||||||
|
wrapSelection(wrapper)
|
||||||
|
}
|
||||||
|
StatusChatInputTextFormationAction {
|
||||||
|
wrapper: "> "
|
||||||
|
icon.name: "quote"
|
||||||
|
text: qsTr("Quote")
|
||||||
|
checked: messageInputField.selectedText && isSelectedLinePrefixedBy(messageInputField.selectionStart, wrapper)
|
||||||
|
|
||||||
|
onActionTriggered: checked
|
||||||
|
? unprefixSelectedLine(wrapper)
|
||||||
|
: prefixSelectedLine(wrapper)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: validators
|
id: validators
|
||||||
anchors.bottom: control.imageErrorMessageLocation === StatusChatInput.ImageErrorMessageLocation.Top ? parent.top : undefined
|
anchors.bottom: control.imageErrorMessageLocation === StatusChatInput.ImageErrorMessageLocation.Top ? parent.top : undefined
|
||||||
|
@ -1244,8 +1246,7 @@ Rectangle {
|
||||||
if (control.fileUrlsAndSources.length > index && control.fileUrlsAndSources[index]) {
|
if (control.fileUrlsAndSources.length > index && control.fileUrlsAndSources[index]) {
|
||||||
control.fileUrlsAndSources.splice(index, 1)
|
control.fileUrlsAndSources.splice(index, 1)
|
||||||
}
|
}
|
||||||
isImage = control.fileUrlsAndSources.length > 0
|
showImageArea(control.fileUrlsAndSources)
|
||||||
validateImages(control.fileUrlsAndSources)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1359,12 +1360,14 @@ Rectangle {
|
||||||
if (messageInputField.selectedText.trim() !== "") {
|
if (messageInputField.selectedText.trim() !== "") {
|
||||||
// If it's a double click, just check the mouse position
|
// If it's a double click, just check the mouse position
|
||||||
// If it's a mouse select, use the start and end position average)
|
// If it's a mouse select, use the start and end position average)
|
||||||
let x = now < messageInputField.lastClick + 500 ? x = event.x :
|
const x = now < messageInputField.lastClick + 500
|
||||||
(messageInputField.cursorRectangle.x + event.x) / 2
|
? event.x
|
||||||
x -= textFormatMenu.width / 2
|
: (messageInputField.cursorRectangle.x + event.x) / 2
|
||||||
|
let menu = Global.openMenu(textFormatMenuComponent, messageInput, {})
|
||||||
textFormatMenu.popup(x, messageInputField.y - textFormatMenu.height - 5)
|
menu.x = x - menu.width / 2
|
||||||
messageInputField.forceActiveFocus();
|
menu.y = messageInputField.y - menu.height - 5
|
||||||
|
d.textFormatMenu = menu
|
||||||
|
messageInputField.forceActiveFocus()
|
||||||
}
|
}
|
||||||
lastClick = now
|
lastClick = now
|
||||||
}
|
}
|
||||||
|
@ -1466,15 +1469,22 @@ Rectangle {
|
||||||
: Theme.palette.baseColor1
|
: Theme.palette.baseColor1
|
||||||
type: StatusQ.StatusFlatRoundButton.Type.Tertiary
|
type: StatusQ.StatusFlatRoundButton.Type.Tertiary
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
highlighted: d.emojiPopupOpened
|
||||||
onClicked: {
|
onClicked: {
|
||||||
control.emojiPopupOpened = true
|
if (d.emojiPopupOpened) {
|
||||||
|
emojiPopup.close()
|
||||||
togglePopup(emojiPopup, emojiBtn)
|
return
|
||||||
|
}
|
||||||
|
d.emojiPopupOpened = true
|
||||||
|
emojiPopup.open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusQ.StatusFlatRoundButton {
|
StatusQ.StatusFlatRoundButton {
|
||||||
id: gifBtn
|
id: gifBtn
|
||||||
|
|
||||||
|
property Popup popup
|
||||||
|
|
||||||
objectName: "gifPopupButton"
|
objectName: "gifPopupButton"
|
||||||
implicitHeight: 32
|
implicitHeight: 32
|
||||||
implicitWidth: 32
|
implicitWidth: 32
|
||||||
|
@ -1484,7 +1494,15 @@ Rectangle {
|
||||||
: Theme.palette.baseColor1
|
: Theme.palette.baseColor1
|
||||||
type: StatusQ.StatusFlatRoundButton.Type.Tertiary
|
type: StatusQ.StatusFlatRoundButton.Type.Tertiary
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
onClicked: togglePopup(gifPopup, gifBtn)
|
highlighted: popup && popup.opened
|
||||||
|
onClicked: {
|
||||||
|
if (popup) {
|
||||||
|
popup.close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
popup = gifPopupComponent.createObject(gifBtn)
|
||||||
|
popup.open()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusQ.StatusFlatRoundButton {
|
StatusQ.StatusFlatRoundButton {
|
||||||
|
@ -1499,10 +1517,14 @@ Rectangle {
|
||||||
type: StatusQ.StatusFlatRoundButton.Type.Tertiary
|
type: StatusQ.StatusFlatRoundButton.Type.Tertiary
|
||||||
visible: !isEdit && emojiBtn.visible
|
visible: !isEdit && emojiBtn.visible
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
highlighted: d.stickersPopupOpened
|
||||||
onClicked: {
|
onClicked: {
|
||||||
control.stickersPopupOpened = true
|
if (d.stickersPopupOpened) {
|
||||||
|
control.stickersPopup.close()
|
||||||
togglePopup(control.stickersPopup, stickersBtn)
|
return
|
||||||
|
}
|
||||||
|
d.stickersPopupOpened = true
|
||||||
|
control.stickersPopup.open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1512,8 +1534,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusQ.StatusButton {
|
StatusQ.StatusButton {
|
||||||
id: unblockBtn
|
Layout.fillHeight: true
|
||||||
Layout.alignment: Qt.AlignBottom
|
|
||||||
Layout.bottomMargin: 4
|
Layout.bottomMargin: 4
|
||||||
visible: control.isContactBlocked
|
visible: control.isContactBlocked
|
||||||
text: qsTr("Unblock")
|
text: qsTr("Unblock")
|
||||||
|
|
|
@ -86,8 +86,6 @@ Row {
|
||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
imageArea.imageRemoved(index)
|
imageArea.imageRemoved(index)
|
||||||
const tmp = imageArea.imageSource.filter((url, idx) => idx !== index)
|
|
||||||
rptImages.model = tmp
|
|
||||||
}
|
}
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: buttonMouseArea
|
id: buttonMouseArea
|
||||||
|
|
|
@ -43,7 +43,6 @@ Popup {
|
||||||
|
|
||||||
modal: false
|
modal: false
|
||||||
width: 360
|
width: 360
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
radius: Style.current.radius
|
radius: Style.current.radius
|
||||||
|
|
|
@ -8,12 +8,14 @@ import shared.panels 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool selected: false
|
property bool selected: false
|
||||||
property bool useIconInsteadOfImage: false
|
property bool useIconInsteadOfImage: false
|
||||||
property url source: Style.svg("history")
|
property url source: Style.svg("history")
|
||||||
signal clicked
|
signal clicked
|
||||||
height: 24
|
|
||||||
width: 24
|
implicitHeight: 24
|
||||||
|
implicitWidth: 24
|
||||||
|
|
||||||
RoundedImage {
|
RoundedImage {
|
||||||
visible: !useIconInsteadOfImage
|
visible: !useIconInsteadOfImage
|
||||||
|
@ -25,6 +27,7 @@ Item {
|
||||||
root.clicked()
|
root.clicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RoundedIcon {
|
RoundedIcon {
|
||||||
id: iconIcon
|
id: iconIcon
|
||||||
visible: useIconInsteadOfImage
|
visible: useIconInsteadOfImage
|
||||||
|
|
|
@ -280,13 +280,12 @@ Popup {
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
leftPadding: 0
|
padding: 0
|
||||||
rightPadding: 0
|
contentHeight: availableHeight
|
||||||
contentWidth: availableWidth
|
|
||||||
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
|
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
width: scrollView.availableWidth
|
height: scrollView.availableHeight
|
||||||
spacing: footerContent.spacing
|
spacing: footerContent.spacing
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
|
@ -298,8 +297,7 @@ Popup {
|
||||||
delegate: StatusStickerPackIconWithIndicator {
|
delegate: StatusStickerPackIconWithIndicator {
|
||||||
id: packIconWithIndicator
|
id: packIconWithIndicator
|
||||||
visible: installed
|
visible: installed
|
||||||
Layout.preferredWidth: 24
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.preferredHeight: 24
|
|
||||||
selected: stickerPackListView.selectedPackId === packId
|
selected: stickerPackListView.selectedPackId === packId
|
||||||
source: thumbnail
|
source: thumbnail
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
@ -314,6 +312,7 @@ Popup {
|
||||||
model: d.stickerPacksLoading ? 7 : 0
|
model: d.stickerPacksLoading ? 7 : 0
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.preferredWidth: 24
|
Layout.preferredWidth: 24
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
radius: width / 2
|
radius: width / 2
|
||||||
|
|
|
@ -25,7 +25,6 @@ Loader {
|
||||||
property var chatContentModule
|
property var chatContentModule
|
||||||
|
|
||||||
property string channelEmoji
|
property string channelEmoji
|
||||||
property bool isActiveChannel: false
|
|
||||||
|
|
||||||
property var chatLogView
|
property var chatLogView
|
||||||
property var emojiPopup
|
property var emojiPopup
|
||||||
|
|
Loading…
Reference in New Issue