Skip to content
Snippets Groups Projects
Unverified Commit 3185df08 authored by Ming Rui Zhang's avatar Ming Rui Zhang Committed by Sébastien Blin
Browse files

messageListView: add typing indicator

Gitlab: #552

Change-Id: I0a4dc3b61a22aafb40d8a301033c59d2cc02bc79
parent 02a80519
No related branches found
No related tags found
No related merge requests found
......@@ -169,5 +169,6 @@
<file>src/commoncomponents/GeneratedMessageDelegate.qml</file>
<file>src/commoncomponents/DataTransferMessageDelegate.qml</file>
<file>src/mainview/components/ScrollToBottomButton.qml</file>
<file>src/commoncomponents/TypingDots.qml</file>
</qresource>
</RCC>
/*
* Copyright (C) 2021 by Savoir-faire Linux
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15
import net.jami.Constants 1.1
Row {
id: root
property int currentRect: 0
spacing: 5
Timer {
repeat: true
running: true
interval: JamiTheme.typingDotsAnimationInterval
onTriggered: {
if (root.currentRect < 2)
root.currentRect ++
else
root.currentRect = 0
}
}
Repeater {
model: 3
Rectangle {
id: circleRect
radius: JamiTheme.typingDotsRadius
width: JamiTheme.typingDotsSize
height: JamiTheme.typingDotsSize
color: JamiTheme.typingDotsNormalColor
states: State {
id: enlargeState
name: "enlarge"
when: root.currentRect === index
}
transitions: [
Transition {
to: "enlarge"
ParallelAnimation {
NumberAnimation {
from: 1.0
to: 1.3
target: circleRect
duration: JamiTheme.typingDotsAnimationInterval
property: "scale"
}
ColorAnimation {
from: JamiTheme.typingDotsNormalColor
to: JamiTheme.typingDotsEnlargeColor
target: circleRect
property: "color"
duration: JamiTheme.typingDotsAnimationInterval
}
}
},
Transition {
from: "enlarge"
ParallelAnimation {
NumberAnimation {
from: 1.3
to: 1.0
target: circleRect
duration: JamiTheme.typingDotsAnimationInterval
property: "scale"
}
ColorAnimation {
from: JamiTheme.typingDotsEnlargeColor
to: JamiTheme.typingDotsNormalColor
target: circleRect
property: "color"
duration: JamiTheme.typingDotsAnimationInterval
}
}
}
]
}
}
}
......@@ -79,7 +79,7 @@ Item {
}
}
TextMetrics {
Text {
id: globalTextMetrics
}
......@@ -87,6 +87,6 @@ Item {
globalTextMetrics.font = font
globalTextMetrics.text = text
return globalTextMetrics.boundingRect
return Qt.size(globalTextMetrics.contentWidth, globalTextMetrics.contentHeight)
}
}
......@@ -257,6 +257,10 @@ Item {
// Chatview footer
property string jumpToLatest: qsTr("Jump to latest")
property string typeIndicatorSingle: qsTr("{} is typing…")
property string typeIndicatorPlural: qsTr("{} are typing…")
property string typeIndicatorMax: qsTr("Several people are typing…")
property string typeIndicatorAnd: qsTr(" and ")
// ConnectToAccountManager
property string enterJAMSURL: qsTr("Enter Jami Account Management Server (JAMS) URL")
......
......@@ -172,6 +172,10 @@ Item {
// Files To Send Container
property color removeFileButtonColor: Qt.rgba(96, 95, 97, 0.5)
// TypingDots
property color typingDotsNormalColor: darkTheme ? "#686b72" : "lightgrey"
property color typingDotsEnlargeColor: darkTheme ? "white" : Qt.darker("lightgrey", 3.0)
// Font.
property color faddedFontColor: darkTheme? "#c0c0c0" : "#a0a0a0"
property color faddedLastInteractionFontColor: darkTheme ? "#c0c0c0" : "#505050"
......@@ -284,6 +288,11 @@ Item {
property real chatViewFooterTextAreaMaximumHeight: 130
property real chatViewScrollToBottomButtonBottomMargin: 8
// TypingDots
property real typingDotsAnimationInterval: 500
property real typingDotsRadius: 30
property real typingDotsSize: 8
// MessageWebView File Transfer Container
property real filesToSendContainerSpacing: 5
property real filesToSendContainerPadding: 10
......
......@@ -151,6 +151,7 @@ ColumnLayout {
- marginSize / 2
onSendMessagesRequired: root.sendMessageButtonClicked()
onTextChanged: MessagesAdapter.userIsComposing(text ? true : false)
}
PushButton {
......
......@@ -273,4 +273,84 @@ ListView {
onClicked: root.ScrollBar.vertical.position =
1.0 - root.ScrollBar.vertical.size
}
header: Control {
id: typeIndicatorContainer
topPadding: 3
width: root.width
height: typeIndicatorNameText.contentHeight + topPadding
visible: MessagesAdapter.currentConvComposingList.length
TypingDots {
id: typingDots
anchors.left: typeIndicatorContainer.left
anchors.leftMargin: 5
anchors.verticalCenter: typeIndicatorContainer.verticalCenter
}
Text {
id: typeIndicatorNameText
anchors.left: typingDots.right
anchors.leftMargin: 5
anchors.verticalCenter: typeIndicatorContainer.verticalCenter
width: {
var textSize = text ? JamiQmlUtils.getTextBoundingRect(font, text).width : 0
var typingContentWidth = typingDots.width + typingDots.anchors.leftMargin
+ typeIndicatorNameText.anchors.leftMargin
+ typeIndicatorEndingText.contentWidth
return Math.min(typeIndicatorContainer.width - 5 - typingContentWidth, textSize)
}
font.pointSize: 8
font.bold: Font.DemiBold
elide: Text.ElideRight
color: JamiTheme.textColor
text: {
var finalText = ""
var nameList = MessagesAdapter.currentConvComposingList
if (nameList.length > 4)
return ""
if (nameList.length === 1)
return nameList[0]
for (var i = 0; i < nameList.length; i++) {
finalText += nameList[i]
if (i === nameList.length - 2)
finalText += JamiStrings.typeIndicatorAnd
else if (i !== nameList.length - 1)
finalText += ", "
}
return finalText
}
}
Text {
id: typeIndicatorEndingText
anchors.left: typeIndicatorNameText.right
anchors.verticalCenter: typeIndicatorContainer.verticalCenter
font.pointSize: 8
color: JamiTheme.textColor
text: {
var nameList = MessagesAdapter.currentConvComposingList
if (nameList.length > 4)
return JamiStrings.typeIndicatorMax
if (nameList.length === 1)
return JamiStrings.typeIndicatorSingle.replace("{}", "")
return JamiStrings.typeIndicatorPlural.replace("{}", "")
}
}
}
}
......@@ -111,7 +111,8 @@ Control {
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
cursorShape: root.opacity ? Qt.PointingHandCursor :
Qt.ArrowCursor
onClicked: root.clicked()
}
......
......@@ -54,6 +54,7 @@ MessagesAdapter::MessagesAdapter(AppSettingsManager* settingsManager,
const auto& conversation = lrcInstance_->getConversationFromConvUid(convId);
filteredMsgListModel_->setSourceModel(conversation.interactions.get());
set_messageListModel(QVariant::fromValue(filteredMsgListModel_));
set_currentConvComposingList({});
});
connect(previewEngine_, &PreviewEngine::infoReady, this, &MessagesAdapter::onPreviewInfoReady);
......@@ -113,6 +114,12 @@ MessagesAdapter::connectConversationModel()
this,
&MessagesAdapter::onConversationMessagesLoaded,
Qt::UniqueConnection);
QObject::connect(currentConversationModel,
&ConversationModel::composingStatusChanged,
this,
&MessagesAdapter::onComposingStatusChanged,
Qt::UniqueConnection);
}
void
......@@ -414,6 +421,22 @@ MessagesAdapter::onMessageLinkified(const QString& messageId, const QString& lin
conversation.interactions->linkifyMessage(messageId, linkified);
}
void
MessagesAdapter::onComposingStatusChanged(const QString& convId,
const QString& contactUri,
bool isComposing)
{
if (lrcInstance_->get_selectedConvUid() == convId) {
auto name = lrcInstance_->getCurrentContactModel()->bestNameForContact(contactUri);
if (isComposing)
currentConvComposingList_.append(name);
else
currentConvComposingList_.removeOne(name);
Q_EMIT currentConvComposingListChanged();
}
}
bool
MessagesAdapter::isLocalImage(const QString& msg)
{
......
......@@ -60,6 +60,7 @@ class MessagesAdapter final : public QmlAdapterBase
{
Q_OBJECT
QML_RO_PROPERTY(QVariant, messageListModel)
QML_RO_PROPERTY(QList<QString>, currentConvComposingList)
public:
explicit MessagesAdapter(AppSettingsManager* settingsManager,
......@@ -121,6 +122,9 @@ private Q_SLOTS:
void onPreviewInfoReady(QString messageIndex, QVariantMap urlInMessage);
void onConversationMessagesLoaded(uint32_t requestId, const QString& convId);
void onMessageLinkified(const QString& messageId, const QString& linkified);
void onComposingStatusChanged(const QString& convId,
const QString& contactUri,
bool isComposing);
private:
AppSettingsManager* settingsManager_;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment