mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-22 14:18:09 +00:00
unstable (chat refactoring)
This commit is contained in:
parent
14aa6a9cf1
commit
aeabbb7cde
8 changed files with 142 additions and 134 deletions
|
|
@ -267,6 +267,7 @@
|
|||
<file>ui/modules/Linphone/Calls/CallControls.qml</file>
|
||||
<file>ui/modules/Linphone/Calls/Calls.qml</file>
|
||||
<file>ui/modules/Linphone/CardBlock.qml</file>
|
||||
<file>ui/modules/Linphone/Chat/Chat.js</file>
|
||||
<file>ui/modules/Linphone/Chat/Chat.qml</file>
|
||||
<file>ui/modules/Linphone/Chat/Event.qml</file>
|
||||
<file>ui/modules/Linphone/Chat/FileMessage.qml</file>
|
||||
|
|
|
|||
80
linphone-desktop/ui/modules/Linphone/Chat/Chat.js
Normal file
80
linphone-desktop/ui/modules/Linphone/Chat/Chat.js
Normal file
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue