mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-22 22:28:08 +00:00
feat(app):
- remove useless smart-search-bar from cpp - create a new `SipAddressesModel` component - create a `UnregisteredSipAddressesModel` (it uses the previous model) - `TimelineModel` uses too this model - many fixes and refactoring - `mapToSipAddress` moved from `ContactsListModel` to `SipAddressesModel` - ...
This commit is contained in:
parent
758dd1c00a
commit
e5d9bf20f7
31 changed files with 223 additions and 227 deletions
|
|
@ -67,9 +67,7 @@ set(SOURCES
|
|||
src/components/settings/AccountSettingsModel.cpp
|
||||
src/components/settings/SettingsModel.cpp
|
||||
src/components/sip-addresses/SipAddressesModel.cpp
|
||||
src/components/sip-addresses/SipAddressModel.cpp
|
||||
src/components/smart-search-bar/SmartSearchBarModel.cpp
|
||||
src/components/smart-search-bar/SmartSearchBarProxyModel.cpp
|
||||
src/components/sip-addresses/UnregisteredSipAddressesModel.cpp
|
||||
src/components/timeline/TimelineModel.cpp
|
||||
src/main.cpp
|
||||
)
|
||||
|
|
@ -93,9 +91,7 @@ set(HEADERS
|
|||
src/components/settings/AccountSettingsModel.hpp
|
||||
src/components/settings/SettingsModel.hpp
|
||||
src/components/sip-addresses/SipAddressesModel.hpp
|
||||
src/components/sip-addresses/SipAddressModel.hpp
|
||||
src/components/smart-search-bar/SmartSearchBarModel.hpp
|
||||
src/components/smart-search-bar/SmartSearchBarProxyModel.hpp
|
||||
src/components/sip-addresses/UnregisteredSipAddressesModel.hpp
|
||||
src/components/timeline/TimelineModel.hpp
|
||||
src/utils.hpp
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "../components/core/CoreManager.hpp"
|
||||
#include "../components/notifier/Notifier.hpp"
|
||||
#include "../components/settings/AccountSettingsModel.hpp"
|
||||
#include "../components/sip-addresses/SipAddressesModel.hpp"
|
||||
#include "../components/timeline/TimelineModel.hpp"
|
||||
|
||||
#include "App.hpp"
|
||||
|
|
@ -66,6 +67,7 @@ void App::initContentApp () {
|
|||
// Init core & contacts.
|
||||
CoreManager::init();
|
||||
ContactsListModel::init();
|
||||
SipAddressesModel::init();
|
||||
|
||||
// Register types and load context properties.
|
||||
registerTypes();
|
||||
|
|
@ -120,6 +122,13 @@ void App::registerTypes () {
|
|||
}
|
||||
);
|
||||
|
||||
qmlRegisterSingletonType<SipAddressesModel>(
|
||||
"Linphone", 1, 0, "SipAddressesModel",
|
||||
[](QQmlEngine *, QJSEngine *) -> QObject *{
|
||||
return SipAddressesModel::getInstance();
|
||||
}
|
||||
);
|
||||
|
||||
qmlRegisterSingletonType<AccountSettingsModel>(
|
||||
"Linphone", 1, 0, "AccountSettingsModel",
|
||||
[](QQmlEngine *, QJSEngine *) -> QObject *{
|
||||
|
|
@ -144,6 +153,7 @@ void App::addContextProperties () {
|
|||
qInfo() << "Adding context properties...";
|
||||
QQmlContext *context = m_engine.rootContext();
|
||||
|
||||
// TODO: Avoid context properties. Use qmlRegister...
|
||||
QQmlComponent component(&m_engine, QUrl(QML_VIEW_CALL_WINDOW));
|
||||
if (component.isError()) {
|
||||
qWarning() << component.errors();
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ bool ChatModelFilter::filterAcceptsRow (int source_row, const QModelIndex &) con
|
|||
return true;
|
||||
|
||||
QModelIndex index = sourceModel()->index(source_row, 0, QModelIndex());
|
||||
const QVariantMap &data = qvariant_cast<QVariantMap>(index.data());
|
||||
const QVariantMap &data = index.data().toMap();
|
||||
|
||||
return data["type"].toInt() == m_entry_type_filter;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,8 @@ using namespace std;
|
|||
|
||||
// =============================================================================
|
||||
|
||||
const char *ContactModel::NAME = "contact-model";
|
||||
|
||||
ContactModel::ContactModel (shared_ptr<linphone::Friend> linphone_friend) {
|
||||
m_linphone_friend = linphone_friend;
|
||||
m_linphone_friend->setData(NAME, *this);
|
||||
m_vcard = make_shared<VcardModel>(linphone_friend->getVcard());
|
||||
|
||||
App::getInstance()->getEngine()->setObjectOwnership(m_vcard.get(), QQmlEngine::CppOwnership);
|
||||
|
|
@ -23,7 +20,6 @@ ContactModel::ContactModel (VcardModel *vcard) {
|
|||
throw std::invalid_argument("A contact is already linked to this vcard.");
|
||||
|
||||
m_linphone_friend = linphone::Friend::newFromVcard(vcard->m_vcard);
|
||||
m_linphone_friend->setData(NAME, *this);
|
||||
m_vcard.reset(vcard);
|
||||
|
||||
engine->setObjectOwnership(vcard, QQmlEngine::CppOwnership);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
#ifndef CONTACT_MODEL_H_
|
||||
#define CONTACT_MODEL_H_
|
||||
|
||||
#include <linphone++/linphone.hh>
|
||||
#include <QObject>
|
||||
|
||||
#include "../presence/Presence.hpp"
|
||||
|
||||
#include "VcardModel.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
|
@ -25,7 +21,9 @@ public:
|
|||
ContactModel (VcardModel *vcard);
|
||||
~ContactModel () = default;
|
||||
|
||||
static const char *NAME;
|
||||
std::shared_ptr<VcardModel> getVcardModel () const {
|
||||
return m_vcard;
|
||||
}
|
||||
|
||||
public slots:
|
||||
void startEdit () {
|
||||
|
|
@ -48,10 +46,6 @@ private:
|
|||
Presence::PresenceStatus getPresenceStatus () const;
|
||||
Presence::PresenceLevel getPresenceLevel () const;
|
||||
|
||||
std::shared_ptr<VcardModel> getVcardModel () const {
|
||||
return m_vcard;
|
||||
}
|
||||
|
||||
VcardModel *getVcardModelPtr () const {
|
||||
return m_vcard.get();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ public:
|
|||
~VcardModel ();
|
||||
|
||||
QString getUsername () const;
|
||||
QVariantList getSipAddresses () const;
|
||||
|
||||
public slots:
|
||||
bool addSipAddress (const QString &sip_address);
|
||||
|
|
@ -58,7 +59,6 @@ private:
|
|||
bool setAvatar (const QString &path);
|
||||
|
||||
QVariantMap getAddress () const;
|
||||
QVariantList getSipAddresses () const;
|
||||
QVariantList getCompanies () const;
|
||||
QVariantList getEmails () const;
|
||||
QVariantList getUrls () const;
|
||||
|
|
|
|||
|
|
@ -77,17 +77,6 @@ bool ContactsListModel::removeRows (int row, int count, const QModelIndex &paren
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
ContactModel *ContactsListModel::mapSipAddressToContact (const QString &sipAddress) const {
|
||||
shared_ptr<linphone::Friend> friend_ = m_linphone_friends->findFriendByUri(
|
||||
::Utils::qStringToLinphoneString(sipAddress)
|
||||
);
|
||||
|
||||
if (!friend_)
|
||||
return nullptr;
|
||||
|
||||
return &friend_->getData<ContactModel>(ContactModel::NAME);
|
||||
}
|
||||
|
||||
ContactModel *ContactsListModel::addContact (VcardModel *vcard) {
|
||||
ContactModel *contact = new ContactModel(vcard);
|
||||
App::getInstance()->getEngine()->setObjectOwnership(contact, QQmlEngine::CppOwnership);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ class ContactsListModel : public QAbstractListModel {
|
|||
Q_OBJECT;
|
||||
|
||||
friend class ContactsListProxyModel;
|
||||
friend class SipAddressesModel;
|
||||
|
||||
public:
|
||||
~ContactsListModel () = default;
|
||||
|
|
@ -25,7 +26,7 @@ public:
|
|||
bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override;
|
||||
|
||||
static void init () {
|
||||
if (!ContactsListModel::m_instance) {
|
||||
if (!m_instance) {
|
||||
m_instance = new ContactsListModel();
|
||||
}
|
||||
}
|
||||
|
|
@ -35,8 +36,6 @@ public:
|
|||
}
|
||||
|
||||
public slots:
|
||||
ContactModel *mapSipAddressToContact (const QString &sipAddress) const;
|
||||
|
||||
ContactModel *addContact (VcardModel *vcard);
|
||||
void removeContact (ContactModel *contact);
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ bool ContactsListProxyModel::filterAcceptsRow (
|
|||
const QModelIndex &source_parent
|
||||
) const {
|
||||
QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
|
||||
const ContactModel *contact = qvariant_cast<ContactModel *>(index.data());
|
||||
const ContactModel *contact = index.data().value<ContactModel *>();
|
||||
|
||||
m_weights[contact] = static_cast<unsigned int>(computeContactWeight(*contact));
|
||||
|
||||
|
|
@ -60,8 +60,8 @@ bool ContactsListProxyModel::filterAcceptsRow (
|
|||
}
|
||||
|
||||
bool ContactsListProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const {
|
||||
const ContactModel *contact_a = qvariant_cast<ContactModel *>(sourceModel()->data(left));
|
||||
const ContactModel *contact_b = qvariant_cast<ContactModel *>(sourceModel()->data(right));
|
||||
const ContactModel *contact_a = sourceModel()->data(left).value<ContactModel *>();
|
||||
const ContactModel *contact_b = sourceModel()->data(right).value<ContactModel *>();
|
||||
|
||||
unsigned int weight_a = m_weights[contact_a];
|
||||
unsigned int weight_b = m_weights[contact_b];
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
#ifndef CORE_MANAGER_H_
|
||||
#define CORE_MANAGER_H_
|
||||
|
||||
#include <QObject>
|
||||
#include <linphone++/linphone.hh>
|
||||
|
||||
#include "../contact/VcardModel.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
|
@ -30,7 +27,7 @@ public:
|
|||
|
||||
public slots:
|
||||
// Must be used in a qml scene.
|
||||
// The ownership of `VcardModel` is `QQmlEngine::JavaScriptOwnership` by default.
|
||||
// Warning: The ownership of `VcardModel` is `QQmlEngine::JavaScriptOwnership` by default.
|
||||
VcardModel *createDetachedVcardModel ();
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include <QQmlComponent>
|
||||
#include <QQuickWindow>
|
||||
#include <QtDebug>
|
||||
#include <QTimer>
|
||||
|
|
@ -22,7 +23,7 @@
|
|||
// =============================================================================
|
||||
|
||||
inline int getNotificationSize (const QObject &object, const char *property) {
|
||||
QVariant variant(object.property(property));
|
||||
QVariant variant = object.property(property);
|
||||
bool so_far_so_good;
|
||||
|
||||
int size = variant.toInt(&so_far_so_good);
|
||||
|
|
@ -56,7 +57,7 @@ Notifier::Notifier (QObject *parent) :
|
|||
m_components[Notifier::Call] = new QQmlComponent(engine, QUrl(QML_NOTIFICATION_PATH));
|
||||
|
||||
// Check errors.
|
||||
for (int i = 0; i < Notifier::MaxNbTypes; i++) {
|
||||
for (int i = 0; i < Notifier::MaxNbTypes; ++i) {
|
||||
QQmlComponent *component = m_components[i];
|
||||
if (component->isError()) {
|
||||
qWarning() << QStringLiteral("Errors found in `Notification` component %1:").arg(i) <<
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@
|
|||
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QQmlComponent>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class QQmlComponent;
|
||||
|
||||
class Notifier : public QObject {
|
||||
Q_OBJECT;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
#include "SipAddressModel.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#ifndef SIP_ADDRESS_MODEL_H_
|
||||
#define SIP_ADDRESS_MODEL_H_
|
||||
|
||||
#include <QObject>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class SipAddressModel : public QObject {
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
};
|
||||
|
||||
#endif // SIP_ADDRESS_H_
|
||||
|
|
@ -1,3 +1,104 @@
|
|||
#include <QDateTime>
|
||||
#include <QSet>
|
||||
|
||||
#include "../../utils.hpp"
|
||||
#include "../contacts/ContactsListModel.hpp"
|
||||
#include "../core/CoreManager.hpp"
|
||||
|
||||
#include "SipAddressesModel.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
SipAddressesModel *SipAddressesModel::m_instance = nullptr;
|
||||
|
||||
SipAddressesModel::SipAddressesModel (QObject *parent) : QAbstractListModel(parent) {
|
||||
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
|
||||
|
||||
// Get sip addresses from chatrooms.
|
||||
for (const auto &chat_room : core->getChatRooms()) {
|
||||
list<shared_ptr<linphone::ChatMessage> > history = chat_room->getHistory(0);
|
||||
|
||||
if (history.size() == 0)
|
||||
continue;
|
||||
|
||||
QString sip_address = ::Utils::linphoneStringToQString(chat_room->getPeerAddress()->asString());
|
||||
|
||||
QVariantMap map;
|
||||
map["sipAddress"] = sip_address;
|
||||
map["timestamp"] = QDateTime::fromMSecsSinceEpoch(static_cast<qint64>(history.back()->getTime()) * 1000);
|
||||
|
||||
m_sip_addresses[sip_address] = map;
|
||||
}
|
||||
|
||||
// Get sip addresses from calls.
|
||||
QSet<QString> address_done;
|
||||
for (const auto &call_log : core->getCallLogs()) {
|
||||
QString sip_address = ::Utils::linphoneStringToQString(call_log->getRemoteAddress()->asString());
|
||||
|
||||
if (address_done.contains(sip_address))
|
||||
continue; // Already used.
|
||||
address_done << sip_address;
|
||||
|
||||
QVariantMap map;
|
||||
map["sipAddress"] = sip_address;
|
||||
map["timestamp"] = QDateTime::fromMSecsSinceEpoch(
|
||||
static_cast<qint64>(call_log->getStartDate() + call_log->getDuration()) * 1000
|
||||
);
|
||||
|
||||
auto it = m_sip_addresses.find(sip_address);
|
||||
if (it == m_sip_addresses.end() || map["timestamp"] > (*it)["timestamp"])
|
||||
m_sip_addresses[sip_address] = map;
|
||||
}
|
||||
|
||||
// Get sip addresses from contacts.
|
||||
for (auto &contact : ContactsListModel::getInstance()->m_list) {
|
||||
for (const auto &sip_address : contact->getVcardModel()->getSipAddresses()) {
|
||||
auto it = m_sip_addresses.find(sip_address.toString());
|
||||
|
||||
if (it == m_sip_addresses.end()) {
|
||||
QVariantMap map;
|
||||
map["sipAddress"] = sip_address;
|
||||
map["contact"] = QVariant::fromValue(contact);
|
||||
m_sip_addresses[sip_address.toString()] = map;
|
||||
} else
|
||||
(*it)["contact"] = QVariant::fromValue(contact);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &map : m_sip_addresses)
|
||||
m_refs << ↦
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
int SipAddressesModel::rowCount (const QModelIndex &) const {
|
||||
return m_refs.count();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> SipAddressesModel::roleNames () const {
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[Qt::DisplayRole] = "$sipAddress";
|
||||
return roles;
|
||||
}
|
||||
|
||||
QVariant SipAddressesModel::data (const QModelIndex &index, int role) const {
|
||||
int row = index.row();
|
||||
|
||||
if (row < 0 || row >= m_refs.count())
|
||||
return QVariant();
|
||||
|
||||
if (role == Qt::DisplayRole)
|
||||
return QVariant::fromValue(*m_refs[row]);
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
ContactModel *SipAddressesModel::mapSipAddressToContact (const QString &sip_address) const {
|
||||
auto it = m_sip_addresses.find(sip_address);
|
||||
if (it == m_sip_addresses.end())
|
||||
return nullptr;
|
||||
|
||||
return it->value("contact").value<ContactModel *>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,40 @@
|
|||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
#include "../contact/ContactModel.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class SipAddresses : public QAbstractListModel {
|
||||
class SipAddressesModel : public QAbstractListModel {
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
~SipAddressesModel () = default;
|
||||
|
||||
int rowCount (const QModelIndex &index = QModelIndex()) const override;
|
||||
|
||||
QHash<int, QByteArray> roleNames () const override;
|
||||
QVariant data (const QModelIndex &index, int role) const override;
|
||||
|
||||
static void init () {
|
||||
if (!m_instance)
|
||||
m_instance = new SipAddressesModel();
|
||||
}
|
||||
|
||||
static SipAddressesModel *getInstance () {
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
public slots:
|
||||
ContactModel *mapSipAddressToContact (const QString &sip_address) const;
|
||||
|
||||
private:
|
||||
SipAddressesModel (QObject *parent = Q_NULLPTR);
|
||||
|
||||
QHash<QString, QVariantMap> m_sip_addresses;
|
||||
QList<const QVariantMap *> m_refs;
|
||||
|
||||
static SipAddressesModel *m_instance;
|
||||
};
|
||||
|
||||
#endif // SIP_ADDRESSES_MODEL_H_
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
#include "../sip-addresses/SipAddressesModel.hpp"
|
||||
|
||||
#include "UnregisteredSipAddressesModel.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
UnregisteredSipAddressesModel::UnregisteredSipAddressesModel (QObject *parent) : QSortFilterProxyModel(parent) {
|
||||
setSourceModel(SipAddressesModel::getInstance());
|
||||
}
|
||||
|
||||
bool UnregisteredSipAddressesModel::filterAcceptsRow (int source_row, const QModelIndex &source_parent) const {
|
||||
QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
|
||||
return index.data().toMap().contains("contact");
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef UNREGISTERED_SIP_ADDRESSES_MODEL_H_
|
||||
#define UNREGISTERED_SIP_ADDRESSES_MODEL_H_
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class UnregisteredSipAddressesModel : public QSortFilterProxyModel {
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
UnregisteredSipAddressesModel (QObject *parent = Q_NULLPTR);
|
||||
~UnregisteredSipAddressesModel () = default;
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow (int source_row, const QModelIndex &source_parent) const override;
|
||||
};
|
||||
|
||||
#endif // UNREGISTERED_SIP_ADDRESSES_MODEL_H_
|
||||
|
|
@ -1 +0,0 @@
|
|||
#include "SmartSearchBarModel.hpp"
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#ifndef SMART_SEARCH_BAR_MODEL_H_
|
||||
#define SMART_SEARCH_BAR_MODEL_H_
|
||||
|
||||
class SmartSearchBarModel {
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // SMART_SEARCH_BAR_MODEL_H_
|
||||
|
|
@ -1 +0,0 @@
|
|||
#include "SmartSearchBarProxyModel.hpp"
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#ifndef SMART_SEARCH_BAR_PROXY_MODEL_H_
|
||||
#define SMART_SEARCH_BAR_PROXY_MODEL_H_
|
||||
|
||||
class SmartSearchBarProxyModel {
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // SMART_SEARCH_BAR_PROXY_MODEL_H_
|
||||
|
|
@ -1,36 +1,12 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include <linphone++/linphone.hh>
|
||||
#include <QDateTime>
|
||||
#include <QSet>
|
||||
|
||||
#include "../../utils.hpp"
|
||||
#include "../contacts/ContactsListModel.hpp"
|
||||
#include "../core/CoreManager.hpp"
|
||||
#include "../sip-addresses/SipAddressesModel.hpp"
|
||||
|
||||
#include "TimelineModel.hpp"
|
||||
|
||||
using namespace std;
|
||||
// =============================================================================
|
||||
|
||||
// ===================================================================
|
||||
|
||||
TimelineModel::TimelineModel (QObject *parent) : QAbstractListModel(parent) {
|
||||
init_entries();
|
||||
|
||||
// Invalidate model if a contact is removed.
|
||||
// Better than compare each sip address.
|
||||
connect(
|
||||
ContactsListModel::getInstance(), &ContactsListModel::rowsRemoved, this,
|
||||
[this](const QModelIndex &, int, int) {
|
||||
beginResetModel();
|
||||
// Nothing.
|
||||
endResetModel();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
int TimelineModel::rowCount (const QModelIndex &) const {
|
||||
return m_entries.count();
|
||||
TimelineModel::TimelineModel (QObject *parent) : QSortFilterProxyModel(parent) {
|
||||
setSourceModel(SipAddressesModel::getInstance());
|
||||
sort(0);
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> TimelineModel::roleNames () const {
|
||||
|
|
@ -39,96 +15,16 @@ QHash<int, QByteArray> TimelineModel::roleNames () const {
|
|||
return roles;
|
||||
}
|
||||
|
||||
QVariant TimelineModel::data (const QModelIndex &index, int role) const {
|
||||
int row = index.row();
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
if (row < 0 || row >= m_entries.count())
|
||||
return QVariant();
|
||||
|
||||
if (role == Qt::DisplayRole)
|
||||
return m_entries[row];
|
||||
|
||||
return QVariant();
|
||||
bool TimelineModel::filterAcceptsRow (int source_row, const QModelIndex &source_parent) const {
|
||||
QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
|
||||
return index.data().toMap().contains("timestamp");
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
bool TimelineModel::lessThan (const QModelIndex &left, const QModelIndex &right) const {
|
||||
const QVariantMap &sip_address_a = sourceModel()->data(left).toMap();
|
||||
const QVariantMap &sip_address_b = sourceModel()->data(right).toMap();
|
||||
|
||||
void TimelineModel::init_entries () {
|
||||
// Returns an iterator entry position to insert a new entry.
|
||||
auto search_entry = [this](
|
||||
const QVariantMap &map,
|
||||
const QList<QMap<QString, QVariant> >::iterator *start = NULL
|
||||
) {
|
||||
return lower_bound(
|
||||
start ? *start : m_entries.begin(), m_entries.end(), map,
|
||||
[](const QVariantMap &a, const QVariantMap &b) {
|
||||
return a["timestamp"] > b["timestamp"];
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
shared_ptr<linphone::Core> core(CoreManager::getInstance()->getCore());
|
||||
|
||||
// Insert chat rooms events.
|
||||
for (const auto &chat_room : core->getChatRooms()) {
|
||||
list<shared_ptr<linphone::ChatMessage> > history = chat_room->getHistory(0);
|
||||
|
||||
if (history.size() == 0)
|
||||
continue;
|
||||
|
||||
// Last message must be at the end of history.
|
||||
shared_ptr<linphone::ChatMessage> message = history.back();
|
||||
|
||||
// Insert event message in timeline entries.
|
||||
QVariantMap map;
|
||||
map["timestamp"] = QDateTime::fromMSecsSinceEpoch(
|
||||
static_cast<qint64>(message->getTime()) * 1000
|
||||
);
|
||||
map["sipAddresses"] = ::Utils::linphoneStringToQString(
|
||||
chat_room->getPeerAddress()->asString()
|
||||
);
|
||||
|
||||
m_entries.insert(search_entry(map), map);
|
||||
}
|
||||
|
||||
// Insert calls events.
|
||||
QSet<QString> address_done;
|
||||
for (const auto &call_log : core->getCallLogs()) {
|
||||
// Get a sip uri to check.
|
||||
QString address = ::Utils::linphoneStringToQString(
|
||||
call_log->getRemoteAddress()->asString()
|
||||
);
|
||||
|
||||
if (address_done.contains(address))
|
||||
continue; // Already used.
|
||||
|
||||
address_done << address;
|
||||
|
||||
// Make a new map.
|
||||
QVariantMap map;
|
||||
map["timestamp"] = QDateTime::fromMSecsSinceEpoch(
|
||||
static_cast<qint64>(call_log->getStartDate() + call_log->getDuration()) * 1000
|
||||
);
|
||||
map["sipAddresses"] = address;
|
||||
|
||||
// Search existing entry.
|
||||
auto it = find_if(
|
||||
m_entries.begin(), m_entries.end(), [&address](const QVariantMap &map) {
|
||||
return address == map["sipAddresses"].toString();
|
||||
}
|
||||
);
|
||||
|
||||
// Is it a new entry?
|
||||
if (it == m_entries.cend())
|
||||
m_entries.insert(search_entry(map), map);
|
||||
else if (map["timestamp"] > (*it)["timestamp"]) {
|
||||
// Remove old entry and insert.
|
||||
it = m_entries.erase(it);
|
||||
|
||||
if (it != m_entries.cbegin())
|
||||
it--;
|
||||
|
||||
m_entries.insert(search_entry(map, &it), map);
|
||||
}
|
||||
}
|
||||
return sip_address_a["timestamp"] > sip_address_b["timestamp"];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,22 @@
|
|||
#ifndef TIMELINE_MODEL_H_
|
||||
#define TIMELINE_MODEL_H_
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
class ContactsListModel;
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class TimelineModel : public QAbstractListModel {
|
||||
class TimelineModel : public QSortFilterProxyModel {
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
TimelineModel (QObject *parent = Q_NULLPTR);
|
||||
~TimelineModel () = default;
|
||||
|
||||
int rowCount (const QModelIndex &) const override;
|
||||
QHash<int, QByteArray> roleNames () const override;
|
||||
QVariant data (const QModelIndex &index, int role) const override;
|
||||
|
||||
private:
|
||||
void init_entries ();
|
||||
|
||||
// A timeline enty is a object that contains:
|
||||
// - A QDateTime `timestamp`.
|
||||
// - A `sipAddresses` value, if it exists only one address, it's
|
||||
// a string, otherwise it's a string array.
|
||||
QList<QVariantMap> m_entries;
|
||||
protected:
|
||||
bool filterAcceptsRow (int source_row, const QModelIndex &source_parent) const override;
|
||||
bool lessThan (const QModelIndex &left, const QModelIndex &right) const override;
|
||||
};
|
||||
|
||||
#endif // TIMELINE_MODEL_H_
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import Linphone.Styles 1.0
|
|||
ColumnLayout {
|
||||
property alias proxyModel: chat.model
|
||||
|
||||
property var _contact: ContactsListModel.mapSipAddressToContact(
|
||||
property var _contact: SipAddressesModel.mapSipAddressToContact(
|
||||
proxyModel.sipAddress
|
||||
) || proxyModel.sipAddress
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ Rectangle {
|
|||
property alias sipAddressColor: description.sipAddressColor
|
||||
property alias usernameColor: description.usernameColor
|
||||
property string sipAddress
|
||||
property var _contact: ContactsListModel.mapSipAddressToContact(sipAddress)
|
||||
property var _contact: SipAddressesModel.mapSipAddressToContact(sipAddress)
|
||||
|
||||
color: 'transparent' // No color by default.
|
||||
height: ContactStyle.height
|
||||
|
|
|
|||
|
|
@ -66,17 +66,17 @@ ColumnLayout {
|
|||
delegate: Item {
|
||||
property var contact: {
|
||||
Utils.assert(
|
||||
!Utils.isArray($timelineEntry.sipAddresses),
|
||||
!Utils.isArray($timelineEntry.sipAddress),
|
||||
'Conferences are not supported at this moment.'
|
||||
)
|
||||
|
||||
return ContactsListModel.mapSipAddressToContact(
|
||||
$timelineEntry.sipAddresses
|
||||
) || $timelineEntry.sipAddresses
|
||||
return SipAddressesModel.mapSipAddressToContact(
|
||||
$timelineEntry.sipAddress
|
||||
) || $timelineEntry.sipAddress
|
||||
}
|
||||
|
||||
height: TimelineStyle.contact.height
|
||||
width: parent.width
|
||||
width: parent ? parent.width : 0
|
||||
|
||||
Contact {
|
||||
anchors.fill: parent
|
||||
|
|
@ -87,7 +87,7 @@ ColumnLayout {
|
|||
? TimelineStyle.contact.backgroundColor.a
|
||||
: TimelineStyle.contact.backgroundColor.b
|
||||
)
|
||||
sipAddress: $timelineEntry.sipAddresses
|
||||
sipAddress: $timelineEntry.sipAddress
|
||||
sipAddressColor: view.currentIndex === index
|
||||
? TimelineStyle.contact.sipAddress.color.selected
|
||||
: TimelineStyle.contact.sipAddress.color.normal
|
||||
|
|
@ -110,7 +110,7 @@ ColumnLayout {
|
|||
anchors.fill: parent
|
||||
onClicked: {
|
||||
view.currentIndex = index
|
||||
timeline.entrySelected($timelineEntry.sipAddresses)
|
||||
timeline.entrySelected($timelineEntry.sipAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ Rectangle {
|
|||
property bool isVideoCall: false
|
||||
property string sipAddress
|
||||
|
||||
property var _contact: ContactsListModel.mapSipAddressToContact(
|
||||
property var _contact: SipAddressesModel.mapSipAddressToContact(
|
||||
sipAddress
|
||||
) || sipAddress
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ Rectangle {
|
|||
property bool isVideoCall: false
|
||||
property string sipAddress
|
||||
|
||||
property var _contact: ContactsListModel.mapSipAddressToContact(
|
||||
property var _contact: SipAddressesModel.mapSipAddressToContact(
|
||||
sipAddress
|
||||
) || sipAddress
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ ColumnLayout {
|
|||
spacing: 0
|
||||
|
||||
Component.onCompleted: {
|
||||
_contact = ContactsListModel.mapSipAddressToContact(sipAddress)
|
||||
_contact = SipAddressesModel.mapSipAddressToContact(sipAddress)
|
||||
|
||||
if (!_contact) {
|
||||
_vcard = CoreManager.createDetachedVcardModel()
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ ColumnLayout {
|
|||
|
||||
property string sipAddress
|
||||
|
||||
property var _contact: ContactsListModel.mapSipAddressToContact(
|
||||
property var _contact: SipAddressesModel.mapSipAddressToContact(
|
||||
sipAddress
|
||||
) || sipAddress
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue