From aeabbb7cde005b42a98dee2354418bdf5037d20a Mon Sep 17 00:00:00 2001 From: Ronan Abhamon Date: Fri, 10 Mar 2017 11:34:23 +0100 Subject: [PATCH] unstable (chat refactoring) --- linphone-desktop/resources.qrc | 1 + .../ui/modules/Linphone/Chat/Chat.js | 80 +++++++++++ .../ui/modules/Linphone/Chat/Chat.qml | 131 ++++-------------- .../ui/modules/Linphone/Chat/FileMessage.qml | 4 +- .../modules/Linphone/Chat/IncomingMessage.qml | 4 +- .../ui/modules/Linphone/Chat/Message.js | 40 +++--- .../ui/modules/Linphone/Chat/Message.qml | 2 +- .../ui/views/App/Main/Assistant.qml | 14 +- 8 files changed, 142 insertions(+), 134 deletions(-) create mode 100644 linphone-desktop/ui/modules/Linphone/Chat/Chat.js diff --git a/linphone-desktop/resources.qrc b/linphone-desktop/resources.qrc index 0f6448c77..ec9e890cb 100644 --- a/linphone-desktop/resources.qrc +++ b/linphone-desktop/resources.qrc @@ -267,6 +267,7 @@ ui/modules/Linphone/Calls/CallControls.qml ui/modules/Linphone/Calls/Calls.qml ui/modules/Linphone/CardBlock.qml + ui/modules/Linphone/Chat/Chat.js ui/modules/Linphone/Chat/Chat.qml ui/modules/Linphone/Chat/Event.qml ui/modules/Linphone/Chat/FileMessage.qml diff --git a/linphone-desktop/ui/modules/Linphone/Chat/Chat.js b/linphone-desktop/ui/modules/Linphone/Chat/Chat.js new file mode 100644 index 000000000..95a76ece2 --- /dev/null +++ b/linphone-desktop/ui/modules/Linphone/Chat/Chat.js @@ -0,0 +1,80 @@ +// ============================================================================= +// `Chat.qml` Logic. +// ============================================================================= + +function initView () { + chat.tryToLoadMoreEntries = false + chat.bindToEnd = true + chat.positionViewAtEnd() +} + +function loadMoreEntries () { + if (chat.atYBeginning && !chat.tryToLoadMoreEntries) { + chat.tryToLoadMoreEntries = true + chat.positionViewAtBeginning() + proxyModel.loadMoreEntries() + } +} + +function getComponentFromEntry (chatEntry) { + if (chatEntry.fileName) { + return 'FileMessage.qml' + } + + if (chatEntry.type === ChatModel.CallEntry) { + return 'Event.qml' + } + + return chatEntry.isOutgoing ? 'OutgoingMessage.qml' : 'IncomingMessage.qml' +} + +function handleCurrentItemChanged (currentItem) { + // Go to last item only! + if (!chat.bindToEnd || !currentItem || chat.currentIndex + 1 !== chat.count) { + return + } + + var chatHeight = chat.height + var messageY = currentItem.mapToItem(chat.contentItem, 0, 0).y + var messageHeight = currentItem.height + console.log(chat.contentY, chatHeight, messageY) + if (chat.contentY <= messageY) { + chat.contentY = messageY + } + chat.currentIndex = -1 +} + +function handleFilesDropped (files) { + chat.bindToEnd = true + files.forEach(proxyModel.sendFileMessage) +} + +function handleMoreEntriesLoaded (n) { + chat.positionViewAtIndex(n - 1, ListView.Beginning) + chat.tryToLoadMoreEntries = false +} + +function handleMovementEnded () { + if (chat.atYEnd) { + chat.bindToEnd = true + } +} + +function handleMovementStarted () { + chat.bindToEnd = false +} + +function handleDataChanged (_, bottomRight) { + var n = chat.count + var index = bottomRight.row + + if (chat.bindToEnd && index + 1 === n) { + chat.currentIndex = index + } +} + +function sendMessage (text) { + textArea.text = '' + chat.bindToEnd = true + proxyModel.sendMessage(text) +} diff --git a/linphone-desktop/ui/modules/Linphone/Chat/Chat.qml b/linphone-desktop/ui/modules/Linphone/Chat/Chat.qml index 1a1dc63b7..728021ffd 100644 --- a/linphone-desktop/ui/modules/Linphone/Chat/Chat.qml +++ b/linphone-desktop/ui/modules/Linphone/Chat/Chat.qml @@ -5,16 +5,14 @@ import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 -import Utils 1.0 + +import 'Chat.js' as Logic // ============================================================================= Rectangle { property alias proxyModel: chat.model - property bool _bindToEnd: false - property var _contactObserver: SipAddressesModel.getContactObserver(proxyModel.sipAddress) - // --------------------------------------------------------------------------- signal messageToSend (string text) @@ -32,30 +30,17 @@ Rectangle { // ----------------------------------------------------------------------- - property bool _tryToLoadMoreEntries: true - - // ----------------------------------------------------------------------- - - function _loadMoreEntries () { - if (atYBeginning && !_tryToLoadMoreEntries) { - _tryToLoadMoreEntries = true - positionViewAtBeginning() - proxyModel.loadMoreEntries() - } - } - - function _initView () { - _tryToLoadMoreEntries = false - _bindToEnd = true - - positionViewAtEnd() - } + property bool bindToEnd: false + property bool tryToLoadMoreEntries: true + property var contactObserver: SipAddressesModel.getContactObserver(proxyModel.sipAddress) // ----------------------------------------------------------------------- Layout.fillHeight: true Layout.fillWidth: true + highlightFollowsCurrentItem: false + section { criteria: ViewSection.FullString delegate: sectionHeading @@ -64,32 +49,12 @@ Rectangle { // ----------------------------------------------------------------------- - Component.onCompleted: { - function goToEnd () { - return Utils.setTimeout(chat, 100, function () { - if (_bindToEnd) { - positionViewAtEnd() - } + Component.onCompleted: Logic.initView() - return goToEnd() - }) - } - goToEnd() - - // First render. - _initView() - } - - // ----------------------------------------------------------------------- - - onMovementStarted: _bindToEnd = false - onMovementEnded: { - if (atYEnd) { - _bindToEnd = true - } - } - - onContentYChanged: _loadMoreEntries() + onContentYChanged: Logic.loadMoreEntries() + onCurrentItemChanged: Logic.handleCurrentItemChanged(currentItem) + onMovementEnded: Logic.handleMovementEnded() + onMovementStarted: Logic.handleMovementStarted() // ----------------------------------------------------------------------- @@ -99,12 +64,9 @@ Rectangle { // When the view is changed (for example `Calls` -> `Messages`), // the position is set at end and it can be possible to load // more entries. - onEntryTypeFilterChanged: chat._initView() - - onMoreEntriesLoaded: { - chat.positionViewAtIndex(n - 1, ListView.Beginning) - chat._tryToLoadMoreEntries = false - } + onEntryTypeFilterChanged: Logic.initView() + onMoreEntriesLoaded: Logic.handleMoreEntriesLoaded(n) + onDataChanged: Logic.handleDataChanged(topLeft, bottomRight) } // ----------------------------------------------------------------------- @@ -171,45 +133,18 @@ Rectangle { leftMargin: ChatStyle.entry.leftMargin right: parent ? parent.right : undefined - // Ugly. I admit it, but it exists a problem, without these - // lines the extra content message is truncated. - // I have no other solution at this moment with `anchors` - // properties... The messages use the `implicitWidth/Height` - // and `width/Height` attrs and is not easy to found a fix. rightMargin: ChatStyle.entry.deleteIconSize + ChatStyle.entry.message.extraContent.spacing + ChatStyle.entry.message.extraContent.rightMargin + ChatStyle.entry.message.extraContent.leftMargin + ChatStyle.entry.message.outgoing.sendIconSize } + color: ChatStyle.color implicitHeight: layout.height + ChatStyle.entry.bottomMargin // --------------------------------------------------------------------- - // Avoid the use of explicit qrc paths. - Component { - id: event - Event {} - } - - Component { - id: incomingMessage - IncomingMessage {} - } - - Component { - id: outgoingMessage - OutgoingMessage {} - } - - Component { - id: fileMessage - FileMessage {} - } - - // --------------------------------------------------------------------- - MouseArea { id: mouseArea @@ -228,29 +163,22 @@ Rectangle { Layout.alignment: Qt.AlignTop Layout.preferredHeight: ChatStyle.entry.lineHeight Layout.preferredWidth: ChatStyle.entry.time.width + color: ChatStyle.entry.time.color font.pointSize: ChatStyle.entry.time.fontSize + text: $chatEntry.timestamp.toLocaleString( Qt.locale(App.locale), 'hh:mm' ) + verticalAlignment: Text.AlignVCenter } // Display content. Loader { Layout.fillWidth: true - sourceComponent: { - if ($chatEntry.fileName) { - return fileMessage - } - - if ($chatEntry.type === ChatModel.CallEntry) { - return event - } - - return $chatEntry.isOutgoing ? outgoingMessage : incomingMessage - } + source: Logic.getComponentFromEntry($chatEntry) } } } @@ -263,27 +191,22 @@ Rectangle { Borders { Layout.fillWidth: true - Layout.preferredHeight: ChatStyle.sendArea.height + - ChatStyle.sendArea.border.width + Layout.preferredHeight: ChatStyle.sendArea.height + ChatStyle.sendArea.border.width + borderColor: ChatStyle.sendArea.border.color topWidth: ChatStyle.sendArea.border.width DroppableTextArea { + id: textArea + anchors.fill: parent + dropEnabled: SettingsModel.fileTransferUrl.length > 0 dropDisabledReason: qsTr('noFileTransferUrl') placeholderText: qsTr('newMessagePlaceholder') - onDropped: { - _bindToEnd = true - files.forEach(proxyModel.sendFileMessage) - } - - onValidText: { - this.text = '' - _bindToEnd = true - proxyModel.sendMessage(text) - } + onDropped: Logic.handleFilesDropped(files) + onValidText: Logic.sendMessage(text) } } } diff --git a/linphone-desktop/ui/modules/Linphone/Chat/FileMessage.qml b/linphone-desktop/ui/modules/Linphone/Chat/FileMessage.qml index c86569c9d..cad3ad97e 100644 --- a/linphone-desktop/ui/modules/Linphone/Chat/FileMessage.qml +++ b/linphone-desktop/ui/modules/Linphone/Chat/FileMessage.qml @@ -27,8 +27,8 @@ Row { height: ChatStyle.entry.message.incoming.avatarSize width: ChatStyle.entry.message.incoming.avatarSize - image: _contactObserver.contact ? _contactObserver.contact.avatar : '' - username: LinphoneUtils.getContactUsername(_contactObserver.contact || proxyModel.sipAddress) + image: chat.contactObserver.contact ? chat.contactObserver.contact.avatar : '' + username: LinphoneUtils.getContactUsername(chat.contactObserver.contact || proxyModel.sipAddress) } } diff --git a/linphone-desktop/ui/modules/Linphone/Chat/IncomingMessage.qml b/linphone-desktop/ui/modules/Linphone/Chat/IncomingMessage.qml index d33d343a0..e5a4ce76a 100644 --- a/linphone-desktop/ui/modules/Linphone/Chat/IncomingMessage.qml +++ b/linphone-desktop/ui/modules/Linphone/Chat/IncomingMessage.qml @@ -20,8 +20,8 @@ RowLayout { Avatar { anchors.centerIn: parent height: ChatStyle.entry.message.incoming.avatarSize - image: _contactObserver.contact ? _contactObserver.contact.avatar : '' - username: LinphoneUtils.getContactUsername(_contactObserver.contact || proxyModel.sipAddress) + image: chat.contactObserver.contact ? chat.contactObserver.contact.avatar : '' + username: LinphoneUtils.getContactUsername(chat.contactObserver.contact || proxyModel.sipAddress) width: ChatStyle.entry.message.incoming.avatarSize // The avatar is only visible for the first message of a incoming messages sequence. diff --git a/linphone-desktop/ui/modules/Linphone/Chat/Message.js b/linphone-desktop/ui/modules/Linphone/Chat/Message.js index cc5f2b032..e4d41cc40 100644 --- a/linphone-desktop/ui/modules/Linphone/Chat/Message.js +++ b/linphone-desktop/ui/modules/Linphone/Chat/Message.js @@ -1,25 +1,29 @@ +// ============================================================================= +// `Message.qml` Logic. +// ============================================================================= + // See: `ensureVisible` on http://doc.qt.io/qt-5/qml-qtquick-textedit.html function ensureVisible (cursor) { - // Case 1: No focused. - if (!message.activeFocus) { - return - } + // Case 1: No focused. + if (!message.activeFocus) { + return + } - // Case 2: Scroll up. - var contentItem = chat.contentItem - var contentY = chat.contentY - var messageY = message.mapToItem(contentItem, 0, 0).y + cursor.y + // Case 2: Scroll up. + var contentItem = chat.contentItem + var contentY = chat.contentY + var messageY = message.mapToItem(contentItem, 0, 0).y + cursor.y - if (contentY >= messageY) { - chat.contentY = messageY - return - } + if (contentY >= messageY) { + chat.contentY = messageY + return + } - // Case 3: Scroll down. - var chatHeight = chat.height - var cursorHeight = cursor.height + // Case 3: Scroll down. + var chatHeight = chat.height + var cursorHeight = cursor.height - if (contentY + chatHeight <= messageY + cursorHeight) { - chat.contentY = messageY + cursorHeight - chatHeight - } + if (contentY + chatHeight <= messageY + cursorHeight) { + chat.contentY = messageY + cursorHeight - chatHeight + } } diff --git a/linphone-desktop/ui/modules/Linphone/Chat/Message.qml b/linphone-desktop/ui/modules/Linphone/Chat/Message.qml index cad6d2135..64a138649 100644 --- a/linphone-desktop/ui/modules/Linphone/Chat/Message.qml +++ b/linphone-desktop/ui/modules/Linphone/Chat/Message.qml @@ -59,7 +59,7 @@ Item { // See http://doc.qt.io/qt-5/qml-qtquick-text.html#textFormat-prop // and http://doc.qt.io/qt-5/richtext-html-subset.html textFormat: Text.RichText // To supports links and imgs. - wrapMode: TextEdit.WordWrap + wrapMode: TextEdit.Wrap onCursorRectangleChanged: Logic.ensureVisible(cursorRectangle) onLinkActivated: Qt.openUrlExternally(link) diff --git a/linphone-desktop/ui/views/App/Main/Assistant.qml b/linphone-desktop/ui/views/App/Main/Assistant.qml index 34956c9da..7dccc13ce 100644 --- a/linphone-desktop/ui/views/App/Main/Assistant.qml +++ b/linphone-desktop/ui/views/App/Main/Assistant.qml @@ -9,7 +9,7 @@ import App.Styles 1.0 // ============================================================================= Item { - id: assistant + id: assistant readonly property string viewsPath: 'qrc:/ui/views/App/Main/Assistant/' @@ -54,8 +54,8 @@ Item { YAnimator { duration: AssistantStyle.stackAnimation.duration easing.type: Easing.OutBack - from: stack.height + AssistantStyle.bottomMargin - to: 0 + from: stack.height + AssistantStyle.bottomMargin + to: 0 } } @@ -66,7 +66,7 @@ Item { from: 0 to: stack.width + AssistantStyle.rightMargin } - } + } pushEnter: Transition { XAnimator { @@ -81,9 +81,9 @@ Item { YAnimator { duration: AssistantStyle.stackAnimation.duration easing.type: Easing.OutBack - from: 0 - to: stack.height + AssistantStyle.bottomMargin + from: 0 + to: stack.height + AssistantStyle.bottomMargin } - } + } } }