Commit bd47b421 authored by Ronan Abhamon's avatar Ronan Abhamon

feat(ui/modules/Linphone/Timeline): display messages counter if necessary

parent 79853f99
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 40.3 (33839) - http://www.bohemiancoding.com/sketch -->
<title>statut_avatar_busy_l</title>
<desc>Created with Sketch.</desc>
<defs>
<path d="M7.9995,16 C3.581,16 0,12.4185 0,8 C0,3.582 3.581,0 7.9995,0 C12.418,0 16,3.582 16,8 C16,12.4185 12.418,16 7.9995,16 Z" id="path-1"></path>
<mask id="mask-2" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="0" y="0" width="16" height="16" fill="white">
<use xlink:href="#path-1"></use>
</mask>
</defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="statut_avatar_busy_l">
<path d="M7.9995,16 C3.581,16 0,12.4185 0,8 C0,3.582 3.581,0 7.9995,0 C12.418,0 16,3.582 16,8 C16,12.4185 12.418,16 7.9995,16 Z" id="statut_avatar_busy_s" fill="#FF5E00"></path>
<use id="statut_avatar_busy_s" stroke="#00FFFFFF" mask="url(#mask-2)" stroke-width="4" xlink:href="#path-1"></use>
</g>
</g>
</svg>
<?xml version="1.0" encoding="UTF-8"?>
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 41 (35326) - http://www.bohemiancoding.com/sketch -->
<title>chat_count</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
<g id="chat_count" stroke="#96A6B1">
<path d="M6.40657699,14.958235 C6.4063782,14.958235 4.02960743,17.4749743 1,16.7980486 C1,16.7980486 3.96659001,15.9311541 3.29347644,13.3085893 L3.27180799,13.1872479 C1.86653929,11.8936694 1,10.1230801 1,8.16948422 C1,4.20997633 4.5607827,1 8.95192433,1 C13.3432648,1 16.9036499,4.20997633 16.9036499,8.16948422 C16.9036499,12.1289921 13.3432648,15.3387695 8.95192433,15.3387695 C8.06133107,15.3387695 7.20552661,15.2072832 6.4061794,14.9638048 L6.40657699,14.958235 L6.40657699,14.958235 Z" stroke-width="1.5"></path>
<path d="M12.2018249,9.5 L5.70182494,9.5"></path>
<path d="M12.2018249,7.5 L5.70182494,7.5"></path>
</g>
</g>
</svg>
......@@ -24,6 +24,8 @@
<file>assets/images/camera_on_hovered.svg</file>
<file>assets/images/camera_on_normal.svg</file>
<file>assets/images/camera_on_pressed.svg</file>
<file>assets/images/chat_amount.svg</file>
<file>assets/images/chat_count.svg</file>
<file>assets/images/chat_error.svg</file>
<file>assets/images/chat_hovered.svg</file>
<file>assets/images/chat_normal.svg</file>
......@@ -208,6 +210,7 @@
<file>ui/modules/Linphone/Contact/Avatar.qml</file>
<file>ui/modules/Linphone/Contact/ContactDescription.qml</file>
<file>ui/modules/Linphone/Contact/Contact.qml</file>
<file>ui/modules/Linphone/Contact/MessagesCounter.qml</file>
<file>ui/modules/Linphone/Notifications/CallNotification.qml</file>
<file>ui/modules/Linphone/Notifications/Notification.qml</file>
<file>ui/modules/Linphone/Notifications/ReceivedMessageNotification.qml</file>
......@@ -220,6 +223,7 @@
<file>ui/modules/Linphone/Styles/Contact/AvatarStyle.qml</file>
<file>ui/modules/Linphone/Styles/Contact/ContactDescriptionStyle.qml</file>
<file>ui/modules/Linphone/Styles/Contact/ContactStyle.qml</file>
<file>ui/modules/Linphone/Styles/Contact/MessagesCounterStyle.qml</file>
<file>ui/modules/Linphone/Styles/NotificationStyle.qml</file>
<file>ui/modules/Linphone/Styles/Presence/PresenceStringStyle.qml</file>
<file>ui/modules/Linphone/Styles/qmldir</file>
......
......@@ -78,11 +78,11 @@ ChatModel::ChatModel (QObject *parent) : QAbstractListModel(parent) {
CoreManager::getInstance()->getSipAddressesModel()->connectToChatModel(this);
QObject::connect(
&(*m_core_handlers), &CoreHandlers::receivedMessage,
&(*m_core_handlers), &CoreHandlers::messageReceived,
this, [this](const shared_ptr<linphone::ChatMessage> &message) {
if (m_chat_room == message->getChatRoom()) {
insertMessageAtEnd(message);
m_chat_room->markAsRead();
resetMessagesCount();
emit messageReceived(message);
}
......@@ -165,10 +165,11 @@ void ChatModel::setSipAddress (const QString &sip_address) {
m_entries.clear();
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
string std_sip_address = ::Utils::qStringToLinphoneString(sip_address);
m_chat_room = core->getChatRoomFromUri(std_sip_address);
m_chat_room->markAsRead();
m_chat_room = core->getChatRoomFromUri(::Utils::qStringToLinphoneString(sip_address));
if (m_chat_room->getUnreadMessagesCount() > 0)
resetMessagesCount();
// Get messages.
for (auto &message : m_chat_room->getHistory(0)) {
......@@ -336,3 +337,8 @@ void ChatModel::insertMessageAtEnd (const shared_ptr<linphone::ChatMessage> &mes
endInsertRows();
}
void ChatModel::resetMessagesCount () {
m_chat_room->markAsRead();
emit messagesCountReset();
}
......@@ -75,6 +75,8 @@ signals:
void messageSent (const std::shared_ptr<linphone::ChatMessage> &message);
void messageReceived (const std::shared_ptr<linphone::ChatMessage> &message);
void messagesCountReset ();
private:
void fillMessageEntry (
QVariantMap &dest,
......@@ -95,6 +97,8 @@ private:
void insertMessageAtEnd (const std::shared_ptr<linphone::ChatMessage> &message);
void resetMessagesCount ();
QList<ChatEntryData> m_entries;
std::shared_ptr<linphone::ChatRoom> m_chat_room;
......
......@@ -2,33 +2,48 @@
// =============================================================================
ChatModelFilter::ChatModelFilter (QObject *parent) : QSortFilterProxyModel(parent) {
setSourceModel(&m_chat_model);
}
// Fetch the L last filtered chat entries.
class ChatProxyModel::ChatModelFilter : public QSortFilterProxyModel {
public:
ChatModelFilter (QObject *parent) : QSortFilterProxyModel(parent) {
setSourceModel(&m_chat_model);
}
bool ChatModelFilter::filterAcceptsRow (int source_row, const QModelIndex &) const {
if (m_entry_type_filter == ChatModel::EntryType::GenericEntry)
return true;
ChatModel::EntryType getEntryTypeFilter () {
return m_entry_type_filter;
}
QModelIndex index = sourceModel()->index(source_row, 0, QModelIndex());
const QVariantMap &data = index.data().toMap();
void setEntryTypeFilter (ChatModel::EntryType type) {
m_entry_type_filter = type;
invalidate();
}
return data["type"].toInt() == m_entry_type_filter;
}
protected:
bool filterAcceptsRow (int source_row, const QModelIndex &) const override {
if (m_entry_type_filter == ChatModel::EntryType::GenericEntry)
return true;
void ChatModelFilter::setEntryTypeFilter (ChatModel::EntryType type) {
m_entry_type_filter = type;
invalidate();
}
QModelIndex index = sourceModel()->index(source_row, 0, QModelIndex());
const QVariantMap &data = index.data().toMap();
return data["type"].toInt() == m_entry_type_filter;
}
private:
ChatModel m_chat_model;
ChatModel::EntryType m_entry_type_filter = ChatModel::EntryType::GenericEntry;
};
// =============================================================================
const unsigned int ChatProxyModel::ENTRIES_CHUNK_SIZE = 50;
ChatProxyModel::ChatProxyModel (QObject *parent) : QSortFilterProxyModel(parent) {
setSourceModel(&m_chat_model_filter);
m_chat_model_filter = new ChatModelFilter(this);
setSourceModel(m_chat_model_filter);
ChatModel *chat = static_cast<ChatModel *>(m_chat_model_filter.sourceModel());
ChatModel *chat = static_cast<ChatModel *>(m_chat_model_filter->sourceModel());
QObject::connect(
chat, &ChatModel::messageReceived, this, [this](const shared_ptr<linphone::ChatMessage> &) {
......@@ -45,7 +60,7 @@ ChatProxyModel::ChatProxyModel (QObject *parent) : QSortFilterProxyModel(parent)
void ChatProxyModel::loadMoreEntries () {
int count = rowCount();
int parent_count = m_chat_model_filter.rowCount();
int parent_count = m_chat_model_filter->rowCount();
if (count < parent_count) {
// Do not increase `m_n_max_displayed_entries` if it's not necessary...
......@@ -62,19 +77,37 @@ void ChatProxyModel::loadMoreEntries () {
}
void ChatProxyModel::setEntryTypeFilter (ChatModel::EntryType type) {
if (m_chat_model_filter.getEntryTypeFilter() != type) {
m_chat_model_filter.setEntryTypeFilter(type);
if (m_chat_model_filter->getEntryTypeFilter() != type) {
m_chat_model_filter->setEntryTypeFilter(type);
emit entryTypeFilterChanged(type);
}
}
void ChatProxyModel::removeEntry (int id) {
QModelIndex source_index = mapToSource(index(id, 0));
static_cast<ChatModel *>(m_chat_model_filter.sourceModel())->removeEntry(
m_chat_model_filter.mapToSource(source_index).row()
static_cast<ChatModel *>(m_chat_model_filter->sourceModel())->removeEntry(
m_chat_model_filter->mapToSource(source_index).row()
);
}
void ChatProxyModel::removeAllEntries () {
static_cast<ChatModel *>(m_chat_model_filter->sourceModel())->removeAllEntries();
}
void ChatProxyModel::sendMessage (const QString &message) {
static_cast<ChatModel *>(m_chat_model_filter->sourceModel())->sendMessage(message);
}
bool ChatProxyModel::filterAcceptsRow (int source_row, const QModelIndex &) const {
return m_chat_model_filter.rowCount() - source_row <= m_n_max_displayed_entries;
return m_chat_model_filter->rowCount() - source_row <= m_n_max_displayed_entries;
}
QString ChatProxyModel::getSipAddress () const {
return static_cast<ChatModel *>(m_chat_model_filter->sourceModel())->getSipAddress();
}
void ChatProxyModel::setSipAddress (const QString &sip_address) {
static_cast<ChatModel *>(m_chat_model_filter->sourceModel())->setSipAddress(
sip_address
);
}
......@@ -5,35 +5,11 @@
#include "ChatModel.hpp"
// =============================================================================
// Fetch the L last filtered chat entries.
// =============================================================================
// Cannot be used as a nested class by Qt and `Q_OBJECT` macro
// must be used in header c++ file.
class ChatModelFilter : public QSortFilterProxyModel {
Q_OBJECT;
public:
ChatModelFilter (QObject *parent = Q_NULLPTR);
ChatModel::EntryType getEntryTypeFilter () {
return m_entry_type_filter;
}
void setEntryTypeFilter (ChatModel::EntryType type);
protected:
bool filterAcceptsRow (int source_row, const QModelIndex &parent) const override;
private:
ChatModel m_chat_model;
ChatModel::EntryType m_entry_type_filter = ChatModel::EntryType::GenericEntry;
};
// =============================================================================
class ChatProxyModel : public QSortFilterProxyModel {
class ChatModelFilter;
Q_OBJECT;
Q_PROPERTY(
......@@ -50,16 +26,12 @@ public:
Q_INVOKABLE void setEntryTypeFilter (ChatModel::EntryType type);
Q_INVOKABLE void removeEntry (int id);
Q_INVOKABLE void removeAllEntries () {
static_cast<ChatModel *>(m_chat_model_filter.sourceModel())->removeAllEntries();
}
Q_INVOKABLE void removeAllEntries ();
Q_INVOKABLE void sendMessage (const QString &message) {
static_cast<ChatModel *>(m_chat_model_filter.sourceModel())->sendMessage(message);
}
Q_INVOKABLE void sendMessage (const QString &message);
signals:
void sipAddressChanged (const QString &sipAddress);
void sipAddressChanged (const QString &sip_address);
void moreEntriesLoaded (int n);
void entryTypeFilterChanged (ChatModel::EntryType type);
......@@ -68,17 +40,10 @@ protected:
bool filterAcceptsRow (int source_row, const QModelIndex &source_parent) const override;
private:
QString getSipAddress () const {
return static_cast<ChatModel *>(m_chat_model_filter.sourceModel())->getSipAddress();
}
void setSipAddress (const QString &sip_address) {
static_cast<ChatModel *>(m_chat_model_filter.sourceModel())->setSipAddress(
sip_address
);
}
QString getSipAddress () const;
void setSipAddress (const QString &sip_address);
ChatModelFilter m_chat_model_filter;
ChatModelFilter *m_chat_model_filter;
int m_n_max_displayed_entries = ENTRIES_CHUNK_SIZE;
static const unsigned int ENTRIES_CHUNK_SIZE;
......
......@@ -31,7 +31,7 @@ void CoreHandlers::onMessageReceived (
const shared_ptr<linphone::ChatRoom> &room,
const shared_ptr<linphone::ChatMessage> &message
) {
emit receivedMessage(message);
emit messageReceived(message);
const App *app = App::getInstance();
if (!app->hasFocus())
......
......@@ -12,7 +12,7 @@ class CoreHandlers :
Q_OBJECT;
signals:
void receivedMessage (const std::shared_ptr<linphone::ChatMessage> &message);
void messageReceived (const std::shared_ptr<linphone::ChatMessage> &message);
private:
void onAuthenticationRequested (
......
......@@ -21,7 +21,7 @@ SipAddressesModel::SipAddressesModel (QObject *parent) : QAbstractListModel(pare
m_handlers = CoreManager::getInstance()->getHandlers();
QObject::connect(
&(*m_handlers), &CoreHandlers::receivedMessage,
&(*m_handlers), &CoreHandlers::messageReceived,
this, [this](const std::shared_ptr<linphone::ChatMessage> &message) {
const QString &sip_address = ::Utils::linphoneStringToQString(message->getFromAddress()->asStringUriOnly());
addOrUpdateSipAddress(sip_address, nullptr, message);
......@@ -104,13 +104,33 @@ void SipAddressesModel::connectToChatModel (ChatModel *chat_model) {
}
);
for (auto &signal : { &ChatModel::messageSent, &ChatModel::messageReceived }) {
QObject::connect(
chat_model, signal,
this, [this](const std::shared_ptr<linphone::ChatMessage> &message) {
addOrUpdateSipAddress(
::Utils::linphoneStringToQString(message->getToAddress()->asStringUriOnly()), nullptr, message
);
}
);
}
QObject::connect(
chat_model, &ChatModel::messageSent,
this, [this](const std::shared_ptr<linphone::ChatMessage> &message) {
addOrUpdateSipAddress(
::Utils::linphoneStringToQString(message->getToAddress()->asStringUriOnly()), nullptr, message
);
});
chat_model, &ChatModel::messagesCountReset, this, [this, chat_model]() {
const QString &sip_address = chat_model->getSipAddress();
auto it = m_sip_addresses.find(sip_address);
if (it != m_sip_addresses.end()) {
(*it)["unreadMessagesCount"] = 0;
int row = m_refs.indexOf(&(*it));
Q_ASSERT(row != -1);
emit dataChanged(index(row, 0), index(row, 0));
return;
}
}
);
}
// -----------------------------------------------------------------------------
......
import QtQuick 2.7
import QtQuick.Layouts 1.3
import Common 1.0
import Linphone 1.0
import LinphoneUtils 1.0
import Linphone.Styles 1.0
import Utils 1.0
// =============================================================================
......@@ -14,10 +12,11 @@ Rectangle {
// ---------------------------------------------------------------------------
property alias actions: actionBar.data
property alias sipAddressColor: description.sipAddressColor
property alias usernameColor: description.usernameColor
property bool displayUnreadMessagesCount: false
property var entry
property var _contact: entry.contact
......@@ -33,7 +32,7 @@ Rectangle {
leftMargin: ContactStyle.leftMargin
rightMargin: ContactStyle.rightMargin
}
spacing: ContactStyle.spacing
spacing: 0
Avatar {
id: avatar
......@@ -50,14 +49,17 @@ Rectangle {
Layout.fillHeight: true
Layout.fillWidth: true
Layout.leftMargin: ContactStyle.spacing
sipAddress: entry.sipAddress
username: avatar.username
}
ActionBar {
id: actionBar
MessagesCounter {
Layout.alignment: Qt.AlignTop
Layout.preferredHeight: ContactStyle.contentHeight
count: entry.unreadMessagesCount || 0
visible: displayUnreadMessagesCount && entry.unreadMessagesCount > 0
}
}
}
import QtQuick 2.7
import QtQuick.Layouts 1.3
import Common 1.0
import Linphone.Styles 1.0
// =============================================================================
Item {
id: messagesCounter
property int count
implicitHeight: counterIcon.height + MessagesCounterStyle.verticalMargins * 2
implicitWidth: counterIcon.width + MessagesCounterStyle.horizontalMargins * 2
Icon {
id: counterIcon
anchors.centerIn: parent
icon: 'chat_count'
iconSize: MessagesCounterStyle.iconSize.message
Icon {
anchors.horizontalCenter: parent.right
anchors.verticalCenter: parent.bottom
icon: 'chat_amount'
iconSize: MessagesCounterStyle.iconSize.amount
Text {
anchors.centerIn: parent
color: MessagesCounterStyle.text.color
font.pointSize: MessagesCounterStyle.text.fontSize
text: messagesCounter.count
}
}
}
}
pragma Singleton
import QtQuick 2.7
import Common 1.0
// =============================================================================
QtObject {
property int horizontalMargins: 0
property int verticalMargins: 10
property QtObject iconSize: QtObject {
property int amount: 16
property int message: 18
}
property QtObject text: QtObject {
property color color: Colors.k
property int fontSize: 7
}
}
......@@ -11,6 +11,7 @@ singleton ChatStyle 1.0 ChatStyle.qml
singleton AvatarStyle 1.0 Contact/AvatarStyle.qml
singleton ContactDescriptionStyle 1.0 Contact/ContactDescriptionStyle.qml
singleton ContactStyle 1.0 Contact/ContactStyle.qml
singleton MessagesCounterStyle 1.0 Contact/MessagesCounterStyle.qml
singleton NotificationStyle 1.0 NotificationStyle.qml
......
......@@ -113,6 +113,7 @@ ColumnLayout {
? TimelineStyle.contact.backgroundColor.a
: TimelineStyle.contact.backgroundColor.b
)
displayUnreadMessagesCount: view.currentIndex !== index
entry: $timelineEntry
sipAddressColor: view.currentIndex === index
? TimelineStyle.contact.sipAddress.color.selected
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment