Skip to content
Snippets Groups Projects
Commit 9cb6b611 authored by Trevor Tabah's avatar Trevor Tabah Committed by Andreas Traczyk
Browse files

messagelistmodel: derive from QAbstractListModel

This patch allows the client to use a listview to display chat
messages.

Gitlab: #479

Change-Id: I45ccbdbebe212299c4d477b69218e273014a3bd2
parent 44202dea
No related branches found
No related tags found
No related merge requests found
......@@ -313,6 +313,7 @@ SET( libringclient_LIB_SRCS
src/newvideo.cpp
src/shmrenderer.cpp
src/directrenderer.cpp
src/messagelistmodel.cpp
#Communication
src/dbus/configurationmanager.cpp
......@@ -331,7 +332,6 @@ SET( libringclient_LIB_SRCS
src/avmodel.cpp
src/pluginmodel.cpp
src/namedirectory.cpp
src/messageslist.cpp
src/smartinfohub.cpp
src/chatview.cpp
)
......@@ -345,9 +345,9 @@ SET( libringclient_LIB_HDRS
src/smartinfohub.h
src/vcard.h
src/namedirectory.h
src/messageslist.h
src/shmrenderer.h
src/directrenderer.h
src/messagelistmodel.h
)
SET(libringclient_api_LIB_HDRS
......
......@@ -19,11 +19,12 @@
#pragma once
#include "interaction.h"
#include "messageslist.h"
#include "messagelistmodel.h"
#include "typedefs.h"
#include <vector>
#include <map>
#include <memory>
#include <vector>
namespace lrc {
......@@ -57,7 +58,9 @@ to_mode(const int intMode)
struct Info
{
Info() = default;
Info()
: interactions(std::make_unique<MessageListModel>(nullptr))
{}
Info(const Info& other) = delete;
Info(Info&& other) = default;
Info& operator=(const Info& other) = delete;
......@@ -69,7 +72,8 @@ struct Info
VectorString participants;
QString callId;
QString confId;
MessagesList interactions;
std::unique_ptr<MessageListModel> interactions;
// MessageListModel interactions;
QString lastMessageUid = 0;
QHash<QString, QString> parentsId; // pair messageid/parentid for messages without parent loaded
std::map<QString, QString> lastDisplayedMessageUid;
......
/****************************************************************************
/****************************************************************************
* Copyright (C) 2017-2021 Savoir-faire Linux Inc. *
* Author: Nicolas Jäger <nicolas.jager@savoirfairelinux.com> *
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com> *
......@@ -235,6 +235,9 @@ getContactInteractionString(const QString& authorUri, const ContactAction& actio
* @var type
* @var status
* @var isRead
* @var commit
* @var linkPreviewInfo
* @var linkified
*/
struct Info
{
......@@ -247,6 +250,8 @@ struct Info
Status status = Status::INVALID;
bool isRead = false;
MapStringString commit;
QVariantMap linkPreviewInfo = {};
bool linkified = false;
Info() {}
......
......@@ -436,7 +436,7 @@ getHistory(Database& db, api::conversation::Info& conversation)
type,
status,
(payloads[i + 6] == "1" ? true : false)});
conversation.interactions.emplace(payloads[i], std::move(msg));
conversation.interactions->emplace(payloads[i], std::move(msg));
conversation.lastMessageUid = payloads[i];
if (status != api::interaction::Status::DISPLAYED || !payloads[i + 1].isEmpty()) {
continue;
......@@ -448,9 +448,9 @@ getHistory(Database& db, api::conversation::Info& conversation)
payloads[i]);
continue;
}
auto lastReadInteraction = conversation.interactions.find(messageId->second);
auto lastReadInteraction = conversation.interactions->find(messageId->second);
auto timestamp = std::stoi(payloads[i + 3].toStdString());
if (lastReadInteraction == conversation.interactions.end()
if (lastReadInteraction == conversation.interactions->end()
|| lastReadInteraction->second.timestamp < timestamp) {
conversation.lastDisplayedMessageUid.at(conversation.participants.front())
= std::stoull(payloads[i].toStdString());
......@@ -818,7 +818,7 @@ writeJSONValue(QJsonObject& json, const QString& key, const QString& value)
// ├── history.db < --conversations and interactions database
// ├── profile.vcf < --account vcard
// ├── profiles < --account contact vcards
// │ │──{ contact_uri }.vcf
// │ │──{ contact_uri }.vcf
// │ └── ...
// ├── ring_device.crt
// └── ring_device.key
......
This diff is collapsed.
......@@ -2,6 +2,7 @@
* Copyright (C) 2020-2021 Savoir-faire Linux Inc.
*
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
* Author: Trevor Tabah <trevor.tabah@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
......@@ -18,18 +19,27 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <QCoreApplication>
#include <stdexcept>
#include "messagelistmodel.h"
#include "messageslist.h"
#include "api/conversationmodel.h"
#include "api/interaction.h"
#include <QAbstractListModel>
namespace lrc {
using namespace api;
using constIterator = MessageListModel::constIterator;
using iterator = MessageListModel::iterator;
using reverseIterator = MessageListModel::reverseIterator;
MessageListModel::MessageListModel(QObject* parent)
: QAbstractListModel(parent)
{}
QPair<iterator, bool>
MessagesList::emplace(QString msgId, interaction::Info message, bool beginning)
MessageListModel::emplace(const QString& msgId, interaction::Info message, bool beginning)
{
iterator it;
for (it = interactions_.begin(); it != interactions_.end(); ++it) {
......@@ -38,11 +48,12 @@ MessagesList::emplace(QString msgId, interaction::Info message, bool beginning)
}
}
auto iter = beginning ? interactions_.begin() : interactions_.end();
auto iterator = interactions_.insert(iter, qMakePair(msgId, message));
auto iterator = insertMessage(iter, qMakePair(msgId, message));
return qMakePair(iterator, true);
}
iterator
MessagesList::find(QString msgId)
MessageListModel::find(const QString& msgId)
{
iterator it;
for (it = interactions_.begin(); it != interactions_.end(); ++it) {
......@@ -54,7 +65,7 @@ MessagesList::find(QString msgId)
}
constIterator
MessagesList::find(QString msgId) const
MessageListModel::find(const QString& msgId) const
{
constIterator it;
for (it = interactions_.cbegin(); it != interactions_.cend(); ++it) {
......@@ -64,27 +75,30 @@ MessagesList::find(QString msgId) const
}
return interactions_.cend();
}
QPair<iterator, bool>
MessagesList::insert(std::pair<QString, interaction::Info> message, bool beginning)
MessageListModel::insert(std::pair<QString, interaction::Info> message, bool beginning)
{
return emplace(message.first, message.second, beginning);
}
int
MessagesList::erase(QString msgId)
MessageListModel::erase(const QString& msgId)
{
iterator it;
int index = 0;
for (it = interactions_.begin(); it != interactions_.end(); ++it) {
if (it->first == msgId) {
interactions_.erase(it);
removeMessage(index, it);
return 1;
}
index++;
}
return 0;
}
interaction::Info&
MessagesList::operator[](QString messageId)
MessageListModel::operator[](const QString& messageId)
{
for (auto it = interactions_.cbegin(); it != interactions_.cend(); ++it) {
if (it->first == messageId) {
......@@ -93,7 +107,7 @@ MessagesList::operator[](QString messageId)
}
// element not find, add it to the end
interaction::Info newMessage = {};
interactions_.insert(interactions_.end(), qMakePair(messageId, newMessage));
insertMessage(interactions_.end(), qMakePair(messageId, newMessage));
if (interactions_.last().first == messageId) {
return const_cast<interaction::Info&>(interactions_.last().second);
}
......@@ -101,61 +115,61 @@ MessagesList::operator[](QString messageId)
}
iterator
MessagesList::end()
MessageListModel::end()
{
return interactions_.end();
}
constIterator
MessagesList::end() const
MessageListModel::end() const
{
return interactions_.end();
}
constIterator
MessagesList::cend() const
MessageListModel::cend() const
{
return interactions_.cend();
}
iterator
MessagesList::begin()
MessageListModel::begin()
{
return interactions_.begin();
}
constIterator
MessagesList::begin() const
MessageListModel::begin() const
{
return interactions_.begin();
}
reverseIterator
MessagesList::rbegin()
MessageListModel::rbegin()
{
return interactions_.rbegin();
}
int
MessagesList::size() const
MessageListModel::size() const
{
return interactions_.size();
}
void
MessagesList::clear()
MessageListModel::clear()
{
interactions_.clear();
}
bool
MessagesList::empty() const
MessageListModel::empty() const
{
return interactions_.empty();
}
interaction::Info
MessagesList::at(QString msgId) const
MessageListModel::at(const QString& msgId) const
{
for (auto it = interactions_.cbegin(); it != interactions_.cend(); ++it) {
if (it->first == msgId) {
......@@ -166,25 +180,25 @@ MessagesList::at(QString msgId) const
}
QPair<QString, interaction::Info>
MessagesList::front() const
MessageListModel::front() const
{
return interactions_.front();
}
QPair<QString, interaction::Info>
MessagesList::last() const
MessageListModel::last() const
{
return interactions_.last();
}
QPair<QString, interaction::Info>
MessagesList::atIndex(int index)
MessageListModel::atIndex(int index) const
{
return interactions_.at(index);
}
QPair<iterator, bool>
MessagesList::insert(int it, QPair<QString, interaction::Info> message)
MessageListModel::insert(int index, QPair<QString, interaction::Info> message)
{
iterator itr;
for (itr = interactions_.begin(); itr != interactions_.end(); ++itr) {
......@@ -192,16 +206,16 @@ MessagesList::insert(int it, QPair<QString, interaction::Info> message)
return qMakePair(itr, false);
}
}
if (it >= size()) {
auto iterator = interactions_.insert(interactions_.end(), message);
if (index >= size()) {
auto iterator = insertMessage(interactions_.end(), message);
return qMakePair(iterator, true);
}
interactions_.insert(it, message);
insertMessage(index, message);
return qMakePair(interactions_.end(), true);
}
int
MessagesList::indexOfMessage(QString msgId, bool reverse) const
MessageListModel::indexOfMessage(const QString& msgId, bool reverse) const
{
auto getIndex = [reverse, &msgId](const auto& start, const auto& end) -> int {
auto it = std::find_if(start, end, [&msgId](const auto& it) { return it.first == msgId; });
......@@ -215,7 +229,7 @@ MessagesList::indexOfMessage(QString msgId, bool reverse) const
}
void
MessagesList::moveMessages(QList<QString> msgIds, QString parentId)
MessageListModel::moveMessages(QList<QString> msgIds, const QString& parentId)
{
for (auto msgId : msgIds) {
moveMessage(msgId, parentId);
......@@ -223,7 +237,7 @@ MessagesList::moveMessages(QList<QString> msgIds, QString parentId)
}
void
MessagesList::moveMessage(QString msgId, QString parentId)
MessageListModel::moveMessage(const QString& msgId, const QString& parentId)
{
int currentIndex = indexOfMessage(msgId);
......@@ -241,11 +255,148 @@ MessagesList::moveMessage(QString msgId, QString parentId)
if (newIndex >= interactions_.size()) {
newIndex = interactions_.size() - 1;
}
interactions_.move(currentIndex, newIndex);
if (currentIndex == newIndex)
return;
moveMessage(currentIndex, newIndex);
// move a child message
if (!childMessageIdToMove.isEmpty()) {
moveMessage(childMessageIdToMove, msgId);
}
}
void
MessageListModel::insertMessage(int index, item_t& message)
{
Q_EMIT beginInsertRows(QModelIndex(), index, index);
interactions_.insert(index, message);
Q_EMIT endInsertRows();
}
iterator
MessageListModel::insertMessage(iterator it, item_t& message)
{
auto index = std::distance(begin(), it);
Q_EMIT beginInsertRows(QModelIndex(), index, index);
auto insertion = interactions_.insert(it, message);
Q_EMIT endInsertRows();
return insertion;
}
void
MessageListModel::removeMessage(int index, iterator it)
{
Q_EMIT beginRemoveRows(QModelIndex(), index, index);
interactions_.erase(it);
Q_EMIT endRemoveRows();
}
void
MessageListModel::moveMessage(int from, int to)
{
Q_EMIT beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
interactions_.move(from, to);
Q_EMIT endMoveRows();
}
bool
MessageListModel::contains(const QString& msgId)
{
return find(msgId) != interactions_.end();
}
int
MessageListModel::rowCount(const QModelIndex& parent) const
{
return interactions_.size();
}
QHash<int, QByteArray>
MessageListModel::roleNames() const
{
using namespace MessageList;
QHash<int, QByteArray> roles;
#define X(role) roles[role] = #role;
MSG_ROLES
#undef X
return roles;
}
QVariant
MessageListModel::dataForItem(item_t item, int indexRow, int role) const
{
switch (role) {
case Role::Id:
return QVariant(item.first);
case Role::Author:
return QVariant(item.second.authorUri);
case Role::Body:
return QVariant(item.second.body);
case Role::Timestamp:
return QVariant::fromValue(item.second.timestamp);
case Role::Duration:
return QVariant::fromValue(item.second.duration);
case Role::Type:
return QVariant(static_cast<int>(item.second.type));
case Role::Status:
return QVariant(static_cast<int>(item.second.status));
case Role::IsRead:
return QVariant(item.second.isRead);
case Role::LinkPreviewInfo:
return QVariant(item.second.linkPreviewInfo);
case Role::Linkified:
return QVariant(item.second.linkified);
default:
return {};
}
}
QVariant
MessageListModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) {
return {};
}
return dataForItem(interactions_.at(index.row()), index.row(), role);
}
int
MessageListModel::getIndexOfMessage(const QString& messageId) const
{
for (int i = 0; i < interactions_.size(); i++) {
if (atIndex(i).first == messageId) {
return i;
}
}
return -1;
}
void
MessageListModel::addHyperlinkInfo(const QString& messageId, const QVariantMap& info)
{
int index = getIndexOfMessage(messageId);
if (index == -1) {
return;
}
QModelIndex modelIndex = QAbstractListModel::index(index, 0);
interactions_[index].second.linkPreviewInfo = info;
Q_EMIT dataChanged(modelIndex, modelIndex, {Role::LinkPreviewInfo});
}
void
MessageListModel::linkifyMessage(const QString& messageId, const QString& linkified)
{
int index = getIndexOfMessage(messageId);
if (index == -1) {
return;
}
QModelIndex modelIndex = QAbstractListModel::index(index, 0);
interactions_[index].second.body = linkified;
interactions_[index].second.linkified = true;
Q_EMIT dataChanged(modelIndex, modelIndex, {Role::Body, Role::Linkified});
}
} // namespace lrc
......@@ -2,7 +2,7 @@
* Copyright (C) 2020-2021 Savoir-faire Linux Inc.
*
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* Author: Trevor Tabah <trevor.tabah@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
......@@ -19,6 +19,10 @@
*/
#pragma once
#include "api/interaction.h"
#include <QAbstractListModel>
namespace lrc {
namespace api {
......@@ -26,20 +30,55 @@ namespace interaction {
struct Info;
}
typedef QList<QPair<QString, interaction::Info>>::ConstIterator constIterator;
typedef QList<QPair<QString, interaction::Info>>::Iterator iterator;
typedef QList<QPair<QString, interaction::Info>>::reverse_iterator reverseIterator;
class MessagesList
#define MSG_ROLES \
X(Id) \
X(Author) \
X(Body) \
X(ParentId) \
X(Timestamp) \
X(Duration) \
X(Type) \
X(Status) \
X(IsRead) \
X(Commit) \
X(LinkPreviewInfo) \
X(Linkified)
namespace MessageList {
Q_NAMESPACE
enum Role {
DummyRole = Qt::UserRole + 1,
#define X(role) role,
MSG_ROLES
#undef X
};
Q_ENUM_NS(Role)
} // namespace MessageList
class MessageListModel : public QAbstractListModel
{
Q_OBJECT
public:
using item_t = const QPair<QString, interaction::Info>;
typedef QList<QPair<QString, interaction::Info>>::ConstIterator constIterator;
typedef QList<QPair<QString, interaction::Info>>::Iterator iterator;
typedef QList<QPair<QString, interaction::Info>>::reverse_iterator reverseIterator;
explicit MessageListModel(QObject* parent = nullptr);
~MessageListModel() = default;
// map functions
QPair<iterator, bool> emplace(QString msgId, interaction::Info message, bool beginning = false);
iterator find(QString msgId);
constIterator find(QString msgId) const;
QPair<iterator, bool> emplace(const QString& msgId,
interaction::Info message,
bool beginning = false);
iterator find(const QString& msgId);
constIterator find(const QString& msgId) const;
QPair<iterator, bool> insert(std::pair<QString, interaction::Info> message,
bool beginning = false);
int erase(QString msgId);
interaction::Info& operator[](QString);
Q_INVOKABLE int erase(const QString& msgId);
interaction::Info& operator[](const QString& messageId);
iterator end();
constIterator end() const;
constIterator cend() const;
......@@ -49,18 +88,36 @@ public:
int size() const;
void clear();
bool empty() const;
interaction::Info at(QString intId) const;
interaction::Info at(const QString& intId) const;
QPair<QString, interaction::Info> front() const;
QPair<QString, interaction::Info> last() const;
QPair<QString, interaction::Info> atIndex(int index);
// jami functions
QPair<iterator, bool> insert(int it, QPair<QString, interaction::Info> message);
int indexOfMessage(QString msgId, bool reverse = true) const;
void moveMessages(QList<QString> msgIds, QString parentId);
QPair<QString, interaction::Info> atIndex(int index) const;
QPair<iterator, bool> insert(int index, QPair<QString, interaction::Info> message);
int indexOfMessage(const QString& msgId, bool reverse = true) const;
void moveMessages(QList<QString> msgIds, const QString& parentId);
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
QHash<int, QByteArray> roleNames() const override;
QVariant dataForItem(item_t item, int indexRow, int role = Qt::DisplayRole) const;
bool contains(const QString& msgId);
int getIndexOfMessage(const QString& messageId) const;
void addHyperlinkInfo(const QString& messageId, const QVariantMap& info);
void linkifyMessage(const QString& messageId, const QString& linkified);
protected:
using Role = MessageList::Role;
private:
QList<QPair<QString, interaction::Info>> interactions_;
void moveMessage(QString msgId, QString parentId);
void moveMessage(const QString& msgId, const QString& parentId);
void insertMessage(int index, item_t& message);
iterator insertMessage(iterator it, item_t& message);
void removeMessage(int index, iterator it);
void moveMessage(int from, int to);
};
} // namespace api
} // namespace lrc
Q_DECLARE_METATYPE(lrc::api::MessageListModel*)
......@@ -25,6 +25,7 @@ SOFTWARE.*/
* @param doc the DOM of the url that is being previewed
* @returns the title of the given webpage
*/
function getTitle(doc){
const og_title = doc.querySelector('meta[property="og:title"]')
if (og_title !== null && og_title.content.length > 0) {
......@@ -122,4 +123,4 @@ function getImage(doc) {
})
}
return null
}
\ No newline at end of file
}
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