mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-04-25 23:58:37 +00:00
unstable, supports calls in chat
This commit is contained in:
parent
74be020133
commit
bcee4d9d19
11 changed files with 181 additions and 25 deletions
|
|
@ -34,6 +34,7 @@ set(SOURCES
|
||||||
src/app/Database.cpp
|
src/app/Database.cpp
|
||||||
src/app/Logger.cpp
|
src/app/Logger.cpp
|
||||||
src/components/chat/ChatModel.cpp
|
src/components/chat/ChatModel.cpp
|
||||||
|
src/components/chat/ChatProxyModel.cpp
|
||||||
src/components/contacts/ContactModel.cpp
|
src/components/contacts/ContactModel.cpp
|
||||||
src/components/contacts/ContactsListModel.cpp
|
src/components/contacts/ContactsListModel.cpp
|
||||||
src/components/contacts/ContactsListProxyModel.cpp
|
src/components/contacts/ContactsListProxyModel.cpp
|
||||||
|
|
@ -52,6 +53,7 @@ set(HEADERS
|
||||||
src/app/Database.hpp
|
src/app/Database.hpp
|
||||||
src/app/Logger.hpp
|
src/app/Logger.hpp
|
||||||
src/components/chat/ChatModel.hpp
|
src/components/chat/ChatModel.hpp
|
||||||
|
src/components/chat/ChatProxyModel.hpp
|
||||||
src/components/contacts/ContactModel.hpp
|
src/components/contacts/ContactModel.hpp
|
||||||
src/components/contacts/ContactsListModel.hpp
|
src/components/contacts/ContactsListModel.hpp
|
||||||
src/components/contacts/ContactsListProxyModel.hpp
|
src/components/contacts/ContactsListProxyModel.hpp
|
||||||
|
|
|
||||||
|
|
@ -194,6 +194,10 @@
|
||||||
<source>endCall</source>
|
<source>endCall</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>outgoingCall</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>Home</name>
|
<name>Home</name>
|
||||||
|
|
|
||||||
|
|
@ -194,6 +194,10 @@
|
||||||
<source>endCall</source>
|
<source>endCall</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>outgoingCall</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>Home</name>
|
<name>Home</name>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
#include <QQuickView>
|
#include <QQuickView>
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
|
|
||||||
#include "../components/chat/ChatModel.hpp"
|
#include "../components/chat/ChatProxyModel.hpp"
|
||||||
#include "../components/contacts/ContactModel.hpp"
|
#include "../components/contacts/ContactModel.hpp"
|
||||||
#include "../components/contacts/ContactsListModel.hpp"
|
#include "../components/contacts/ContactsListModel.hpp"
|
||||||
#include "../components/contacts/ContactsListProxyModel.hpp"
|
#include "../components/contacts/ContactsListProxyModel.hpp"
|
||||||
|
|
@ -99,6 +99,7 @@ void App::registerTypes () {
|
||||||
qmlRegisterType<ContactsListProxyModel>("Linphone", 1, 0, "ContactsListProxyModel");
|
qmlRegisterType<ContactsListProxyModel>("Linphone", 1, 0, "ContactsListProxyModel");
|
||||||
|
|
||||||
qmlRegisterType<ChatModel>("Linphone", 1, 0, "ChatModel");
|
qmlRegisterType<ChatModel>("Linphone", 1, 0, "ChatModel");
|
||||||
|
qmlRegisterType<ChatProxyModel>("Linphone", 1, 0, "ChatProxyModel");
|
||||||
|
|
||||||
// Register singletons.
|
// Register singletons.
|
||||||
qmlRegisterSingletonType<ContactsListModel>(
|
qmlRegisterSingletonType<ContactsListModel>(
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
|
|
||||||
|
|
@ -79,6 +81,38 @@ void ChatModel::removeAllEntries () {
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
void ChatModel::fillMessageEntry (
|
||||||
|
QVariantMap &dest,
|
||||||
|
const shared_ptr<linphone::ChatMessage> &message
|
||||||
|
) {
|
||||||
|
dest["type"] = EntryType::MessageEntry;
|
||||||
|
dest["timestamp"] = QDateTime::fromTime_t(message->getTime());
|
||||||
|
dest["content"] = Utils::linphoneStringToQString(
|
||||||
|
message->getText()
|
||||||
|
);
|
||||||
|
dest["isOutgoing"] = message->isOutgoing();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatModel::fillCallStartEntry (
|
||||||
|
QVariantMap &dest,
|
||||||
|
const std::shared_ptr<linphone::CallLog> &call_log
|
||||||
|
) {
|
||||||
|
QDateTime timestamp = QDateTime::fromTime_t(call_log->getStartDate());
|
||||||
|
|
||||||
|
dest["type"] = EntryType::CallEntry;
|
||||||
|
dest["timestamp"] = timestamp;
|
||||||
|
dest["isOutgoing"] = call_log->getDir() == linphone::CallDirOutgoing;
|
||||||
|
dest["status"] = call_log->getStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatModel::fillCallEndEntry (
|
||||||
|
QVariantMap &dest,
|
||||||
|
const std::shared_ptr<linphone::CallLog> &call_log
|
||||||
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void ChatModel::removeEntry (ChatEntryData &pair) {
|
void ChatModel::removeEntry (ChatEntryData &pair) {
|
||||||
int type = pair.first["type"].toInt();
|
int type = pair.first["type"].toInt();
|
||||||
|
|
||||||
|
|
@ -89,7 +123,9 @@ void ChatModel::removeEntry (ChatEntryData &pair) {
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case ChatModel::CallEntry:
|
case ChatModel::CallEntry:
|
||||||
|
CoreManager::getInstance()->getCore()->removeCallLog(
|
||||||
|
static_pointer_cast<linphone::CallLog>(pair.second)
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
qWarning() << "Unknown chat entry type:" << type;
|
qWarning() << "Unknown chat entry type:" << type;
|
||||||
|
|
@ -114,29 +150,42 @@ void ChatModel::setSipAddress (const QString &sip_address) {
|
||||||
// Invalid old sip address entries.
|
// Invalid old sip address entries.
|
||||||
m_entries.clear();
|
m_entries.clear();
|
||||||
|
|
||||||
m_chat_room =
|
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
|
||||||
CoreManager::getInstance()->getCore()->getChatRoomFromUri(
|
string std_sip_address = Utils::qStringToLinphoneString(sip_address);
|
||||||
Utils::qStringToLinphoneString(sip_address)
|
|
||||||
);
|
m_chat_room = core->getChatRoomFromUri(std_sip_address);
|
||||||
|
|
||||||
// Get messages.
|
// Get messages.
|
||||||
for (auto &message : m_chat_room->getHistory(0)) {
|
for (auto &message : m_chat_room->getHistory(0)) {
|
||||||
QVariantMap map;
|
QVariantMap map;
|
||||||
|
|
||||||
map["type"] = EntryType::MessageEntry;
|
fillMessageEntry(map, message);
|
||||||
map["timestamp"] = QDateTime::fromTime_t(message->getTime());
|
|
||||||
map["content"] = Utils::linphoneStringToQString(
|
|
||||||
message->getText()
|
|
||||||
);
|
|
||||||
map["isOutgoing"] = message->isOutgoing();
|
|
||||||
|
|
||||||
m_entries << qMakePair(map, static_pointer_cast<void>(message));
|
m_entries << qMakePair(map, static_pointer_cast<void>(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get calls.
|
// Get calls.
|
||||||
// TODO.
|
for (auto &call_log : core->getCallHistoryForAddress(m_chat_room->getPeerAddress())) {
|
||||||
|
QVariantMap start, end;
|
||||||
|
fillCallStartEntry(start, call_log);
|
||||||
|
|
||||||
|
ChatEntryData pair = qMakePair(start, static_pointer_cast<void>(call_log));
|
||||||
|
|
||||||
|
auto it = lower_bound(
|
||||||
|
m_entries.begin(), m_entries.end(), pair,
|
||||||
|
[](const ChatEntryData &a, const ChatEntryData &b) {
|
||||||
|
return a.first["timestamp"] < b.first["timestamp"];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
m_entries.insert(it, pair);
|
||||||
|
}
|
||||||
|
|
||||||
endResetModel();
|
endResetModel();
|
||||||
|
|
||||||
emit sipAddressChanged(sip_address);
|
emit sipAddressChanged(sip_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QT_TR_NOOP('endCall'),
|
||||||
|
// QT_TR_NOOP('incomingCall'),
|
||||||
|
// QT_TR_NOOP('lostIncomingCall'),
|
||||||
|
// QT_TR_NOOP('lostOutgoingCall')
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
class ChatModel : public QAbstractListModel {
|
class ChatModel : public QAbstractListModel {
|
||||||
|
friend class ChatProxyModel;
|
||||||
|
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
|
|
||||||
Q_PROPERTY(
|
Q_PROPERTY(
|
||||||
|
|
@ -16,6 +18,9 @@ class ChatModel : public QAbstractListModel {
|
||||||
NOTIFY sipAddressChanged
|
NOTIFY sipAddressChanged
|
||||||
);
|
);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void sipAddressChanged (const QString &sipAddress);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum Roles {
|
enum Roles {
|
||||||
ChatEntry = Qt::DisplayRole,
|
ChatEntry = Qt::DisplayRole,
|
||||||
|
|
@ -28,6 +33,13 @@ public:
|
||||||
};
|
};
|
||||||
Q_ENUM(EntryType);
|
Q_ENUM(EntryType);
|
||||||
|
|
||||||
|
enum CallStatus {
|
||||||
|
CallStatusDeclined = linphone::CallStatusDeclined,
|
||||||
|
CallStatusMissed = linphone::CallStatusMissed,
|
||||||
|
CallStatusSuccess = linphone::CallStatusSuccess
|
||||||
|
};
|
||||||
|
Q_ENUM(CallStatus);
|
||||||
|
|
||||||
ChatModel (QObject *parent = Q_NULLPTR) : QAbstractListModel(parent) {}
|
ChatModel (QObject *parent = Q_NULLPTR) : QAbstractListModel(parent) {}
|
||||||
|
|
||||||
int rowCount (const QModelIndex &index = QModelIndex()) const {
|
int rowCount (const QModelIndex &index = QModelIndex()) const {
|
||||||
|
|
@ -44,12 +56,24 @@ public slots:
|
||||||
void removeEntry (int id);
|
void removeEntry (int id);
|
||||||
void removeAllEntries ();
|
void removeAllEntries ();
|
||||||
|
|
||||||
signals:
|
|
||||||
void sipAddressChanged (const QString &sipAddress);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef QPair<QVariantMap, std::shared_ptr<void> > ChatEntryData;
|
typedef QPair<QVariantMap, std::shared_ptr<void> > ChatEntryData;
|
||||||
|
|
||||||
|
void fillMessageEntry (
|
||||||
|
QVariantMap &dest,
|
||||||
|
const std::shared_ptr<linphone::ChatMessage> &message
|
||||||
|
);
|
||||||
|
|
||||||
|
void fillCallStartEntry (
|
||||||
|
QVariantMap &dest,
|
||||||
|
const std::shared_ptr<linphone::CallLog> &call_log
|
||||||
|
);
|
||||||
|
|
||||||
|
void fillCallEndEntry (
|
||||||
|
QVariantMap &dest,
|
||||||
|
const std::shared_ptr<linphone::CallLog> &call_log
|
||||||
|
);
|
||||||
|
|
||||||
void removeEntry (ChatEntryData &pair);
|
void removeEntry (ChatEntryData &pair);
|
||||||
|
|
||||||
QString getSipAddress () const;
|
QString getSipAddress () const;
|
||||||
|
|
|
||||||
13
tests/src/components/chat/ChatProxyModel.cpp
Normal file
13
tests/src/components/chat/ChatProxyModel.cpp
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#include "ChatProxyModel.hpp"
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
ChatProxyModel::ChatProxyModel (QObject *parent) : QSortFilterProxyModel(parent) {
|
||||||
|
m_chat_model.setParent(this);
|
||||||
|
setSourceModel(&m_chat_model);
|
||||||
|
setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatProxyModel::filterAcceptsRow (int source_row, const QModelIndex &source_parent) const {
|
||||||
|
return true; // TODO.
|
||||||
|
}
|
||||||
44
tests/src/components/chat/ChatProxyModel.hpp
Normal file
44
tests/src/components/chat/ChatProxyModel.hpp
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
#ifndef CHAT_PROXY_MODEL_H_
|
||||||
|
#define CHAT_PROXY_MODEL_H_
|
||||||
|
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
|
#include "ChatModel.hpp"
|
||||||
|
|
||||||
|
class ChatProxyModel : public QSortFilterProxyModel {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
Q_PROPERTY(
|
||||||
|
QString sipAddress
|
||||||
|
READ getSipAddress
|
||||||
|
WRITE setSipAddress
|
||||||
|
NOTIFY sipAddressChanged
|
||||||
|
);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void sipAddressChanged (const QString &sipAddress);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ChatProxyModel (QObject *parent = Q_NULLPTR);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
ChatModel *getChatModel () {
|
||||||
|
return &m_chat_model;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool filterAcceptsRow (int source_row, const QModelIndex &source_parent) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString getSipAddress () const {
|
||||||
|
return m_chat_model.getSipAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSipAddress (const QString &sip_address) {
|
||||||
|
m_chat_model.setSipAddress(sip_address);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatModel m_chat_model;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CHAT_PROXY_MODEL_H_
|
||||||
|
|
@ -11,6 +11,7 @@ import Linphone.Styles 1.0
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
property var contact
|
property var contact
|
||||||
|
|
||||||
|
// Can be a model or a proxy chat model.
|
||||||
property alias model: chat.model
|
property alias model: chat.model
|
||||||
|
|
||||||
// -----------------------------------------------------------------
|
// -----------------------------------------------------------------
|
||||||
|
|
@ -80,12 +81,18 @@ ColumnLayout {
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
id: entry
|
id: entry
|
||||||
|
|
||||||
|
// Chat supports model and proxy model.
|
||||||
|
function getModel () {
|
||||||
|
var model = chat.model
|
||||||
|
return model.getChatModel ? model.getChatModel() : model
|
||||||
|
}
|
||||||
|
|
||||||
function isHoverEntry () {
|
function isHoverEntry () {
|
||||||
return mouseArea.containsMouse
|
return mouseArea.containsMouse
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeEntry () {
|
function removeEntry () {
|
||||||
chat.model.removeEntry(index)
|
getModel().removeEntry(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,27 @@
|
||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
|
|
||||||
import Common 1.0
|
import Common 1.0
|
||||||
|
import Linphone 1.0
|
||||||
import Linphone.Styles 1.0
|
import Linphone.Styles 1.0
|
||||||
import Utils 1.0
|
import Utils 1.0
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
|
property string _type: {
|
||||||
|
if ($chatEntry.status === ChatModel.CallStatusSuccess) {
|
||||||
|
return $chatEntry.isOutgoing ? 'outgoing_call' : 'incoming_call'
|
||||||
|
}
|
||||||
|
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
height: ChatStyle.entry.lineHeight
|
height: ChatStyle.entry.lineHeight
|
||||||
spacing: ChatStyle.entry.message.extraContent.spacing
|
spacing: ChatStyle.entry.message.extraContent.spacing
|
||||||
|
|
||||||
Icon {
|
Icon {
|
||||||
height: parent.height
|
height: parent.height
|
||||||
icon: $content
|
icon: _type
|
||||||
iconSize: ChatStyle.entry.event.iconSize
|
iconSize: ChatStyle.entry.event.iconSize
|
||||||
width: ChatStyle.entry.metaWidth
|
width: ChatStyle.entry.metaWidth
|
||||||
}
|
}
|
||||||
|
|
@ -25,8 +34,7 @@ Row {
|
||||||
property var i18n: [
|
property var i18n: [
|
||||||
QT_TR_NOOP('endCall'),
|
QT_TR_NOOP('endCall'),
|
||||||
QT_TR_NOOP('incomingCall'),
|
QT_TR_NOOP('incomingCall'),
|
||||||
QT_TR_NOOP('lostIncomingCall'),
|
QT_TR_NOOP('outgoingCall')
|
||||||
QT_TR_NOOP('lostOutgoingCall')
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -37,7 +45,7 @@ Row {
|
||||||
pointSize: ChatStyle.entry.event.text.fontSize
|
pointSize: ChatStyle.entry.event.text.fontSize
|
||||||
}
|
}
|
||||||
height: parent.height
|
height: parent.height
|
||||||
text: qsTr(Utils.snakeToCamel($content))
|
text: qsTr(Utils.snakeToCamel(_type))
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ ColumnLayout {
|
||||||
descriptionText: qsTr('removeAllEntriesDescription'),
|
descriptionText: qsTr('removeAllEntriesDescription'),
|
||||||
exitHandler: function (status) {
|
exitHandler: function (status) {
|
||||||
if (status) {
|
if (status) {
|
||||||
chatModel.removeAllEntries()
|
chatProxyModel.removeAllEntries()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
title: qsTr('removeAllEntriesTitle')
|
title: qsTr('removeAllEntriesTitle')
|
||||||
|
|
@ -145,8 +145,8 @@ ColumnLayout {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
contact: parent._contact
|
contact: parent._contact
|
||||||
model: ChatModel {
|
model: ChatProxyModel {
|
||||||
id: chatModel
|
id: chatProxyModel
|
||||||
|
|
||||||
sipAddress: conversation.sipAddress
|
sipAddress: conversation.sipAddress
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue