import QtQuick 2.13 import QtQuick.Controls 2.13 import QtGraphicalEffects 1.13 import QtQml.Models 2.13 import QtQuick.Layouts 1.13 import utils 1.0 import shared 1.0 import shared.views 1.0 import shared.status 1.0 import shared.popups 1.0 import shared.controls 1.0 import shared.views.chat 1.0 import "../Chat/stores" import "../Chat/popups" import "stores" import "panels" ScrollView { id: root property RootStore store: RootStore { } property var chatSectionModule Layout.fillWidth: true Layout.fillHeight: true contentHeight: chatLogView.contentHeight + 140 clip: true ScrollBar.horizontal.policy: ScrollBar.AlwaysOff property var rootStore property var messageStore property var onActivated: function () { store.setActiveChannelToTimeline() statusUpdateInput.textInput.forceActiveFocus(Qt.MouseFocusReason) } Component.onCompleted: { statusUpdateInput.textInput.forceActiveFocus(Qt.MouseFocusReason) } function openProfilePopup(userNameParam, fromAuthorParam, identiconParam, textParam, nicknameParam, parentPopup){ var popup = profilePopupComponent.createObject(root); if(parentPopup){ popup.parentPopup = parentPopup; } popup.openPopup(userProfile.pubKey !== fromAuthorParam, userNameParam, fromAuthorParam, identiconParam, textParam, nicknameParam); } StatusImageModal { id: imagePopup onClicked: { close() } } property Component profilePopupComponent: ProfilePopup { id: profilePopup store: root.store onClosed: { if(profilePopup.parentPopup){ profilePopup.parentPopup.close(); } destroy() } } Item { id: timelineContainer width: 624 anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom // TODO: Replace this with StatusQ component once it lives there. StatusChatInput { id: statusUpdateInput anchors.top: parent.top anchors.topMargin: 40 chatType: Constants.chatType.profile imageErrorMessageLocation: StatusChatInput.ImageErrorMessageLocation.Bottom z: 1 onSendMessage: { if (statusUpdateInput.fileUrls.length > 0){ statusUpdateInput.fileUrls.forEach(url => { root.store.sendImage(url); }) } var msg = root.store.getPlainTextFromRichText(Emoji.deparse(statusUpdateInput.textInput.text)) if (msg.length > 0){ msg = statusUpdateInput.interpretMessage(msg) root.store.sendMessage(msg, Utils.isOnlyEmoji(msg) ? Constants.messageContentType.emojiType : Constants.messageContentType.messageType); statusUpdateInput.textInput.text = ""; if(event) event.accepted = true sendMessageSound.stop() Qt.callLater(sendMessageSound.play); } } } EmptyTimelinePanel { id: emptyTimeline anchors.top: statusUpdateInput.bottom anchors.topMargin: 40 anchors.horizontalCenter: parent.horizontalCenter visible: chatLogView.count === 0 } ListView { id: chatLogView anchors.top: statusUpdateInput.bottom anchors.topMargin: Style.current.bigPadding anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom spacing: Style.current.halfPadding flickDeceleration: 10000 interactive: false model: messageListDelegate section.property: "sectionIdentifier" section.criteria: ViewSection.FullString Connections { target: root.store.chatsModelInst.messageView onMessagesLoaded: { Qt.callLater(chatLogView.positionViewAtBeginning) } } } Timer { id: ageUpdateTimer property int epoch: 0 running: true repeat: true interval: 60000 // 1 min onTriggered: epoch = epoch + 1 } DelegateModelGeneralized { id: messageListDelegate lessThan: [ function(left, right) { return left.clock > right.clock } ] model: root.store.chatsModelInst.messageView.messageList // TODO: Replace with StatusQ component once it lives there. delegate: MessageView { id: msgDelegate // Not Refactored Yet // rootStore: root.rootStore // messageStore: root.messageStore // fromAuthor: model.fromAuthor // chatId: model.chatId // userName: model.userName // alias: model.alias // localName: model.localName // message: model.message // plainText: model.plainText // identicon: model.identicon // isCurrentUser: model.isCurrentUser // timestamp: model.timestamp // sticker: model.sticker // contentType: model.contentType // outgoingStatus: model.outgoingStatus // responseTo: model.responseTo // authorCurrentMsg: msgDelegate.ListView.section // authorPrevMsg: msgDelegate.ListView.previousSection // imageClick: imagePopup.openPopup.bind(imagePopup) // messageId: model.messageId // emojiReactions: model.emojiReactions // isStatusUpdate: true // statusAgeEpoch: ageUpdateTimer.epoch // // This is used in order to have access to the previous message and determine the timestamp // // we can't rely on the index because the sequence of messages is not ordered on the nim side // prevMessageIndex: { // // This is used in order to have access to the previous message and determine the timestamp // // we can't rely on the index because the sequence of messages is not ordered on the nim side // if(msgDelegate.DelegateModel.itemsIndex > 0){ // return messageListDelegate.items.get(msgDelegate.DelegateModel.itemsIndex - 1).model.index // } // return -1; // } // timeout: model.timeout // messageContextMenu: msgCntxtMenu // Component.onCompleted: { // messageStore.fromAuthor = model.fromAuthor; // messageStore.chatId = model.chatId; // messageStore.userName = model.userName; // messageStore.alias = model.alias; // messageStore.localName = model.localName; // messageStore.message = model.message; // messageStore.plainText = model.plainText; // messageStore.identicon = model.identicon; // messageStore.isCurrentUser = model.isCurrentUser; // messageStore.timestamp = model.timestamp; // messageStore.sticker = model.sticker; // messageStore.contentType = model.contentType; // messageStore.outgoingStatus = model.outgoingStatus; // messageStore.responseTo = model.responseTo; // messageStore.authorCurrentMsg = msgDelegate.ListView.section; // messageStore.authorPrevMsg = msgDelegate.ListView.previousSection; // messageStore.imageClick = imagePopup.openPopup.bind(imagePopup); // messageStore.messageId = model.messageId; // messageStore.emojiReactions = model.emojiReactions; // messageStore.isStatusUpdate = true; // messageStore.statusAgeEpoch = ageUpdateTimer.epoch; // // This is used in order to have access to the previous message and determine the timestamp // // we can't rely on the index because the sequence of messages is not ordered on the nim side // messageStore.prevMessageIndex = // // This is used in order to have access to the previous message and determine the timestamp // // we can't rely on the index because the sequence of messages is not ordered on the nim side // (msgDelegate.DelegateModel.itemsIndex > 0) ? // messageListDelegate.items.get(msgDelegate.DelegateModel.itemsIndex - 1).model.index : -1; // messageStore.timeout = model.timeout; // messageStore.messageContextMenu = msgCntxtMenu; // } MessageContextMenuView { id: msgCntxtMenu store: root.store chatSectionModule: root.chatSectionModule reactionModel: EmojiReactions { } } } } Loader { active: root.store.chatsModelInst.messageView.loadingMessages // TODO: replace with StatusLoadingIndicator sourceComponent: LoadingAnimation {} anchors.right: timelineContainer.right anchors.top: statusUpdateInput.bottom anchors.rightMargin: Style.current.padding anchors.topMargin: Style.current.padding } } }