open chat room from call/conf

navigate to chat from contact/history/magic search

select chat when joining from contact/history/magic search

message notification

filter chat rooms
This commit is contained in:
Gaelle Braud 2025-04-29 17:30:04 +02:00
parent 8516a3febf
commit b79b324027
43 changed files with 692 additions and 154 deletions

View file

@ -43,17 +43,17 @@ ChatCore::ChatCore(const std::shared_ptr<linphone::ChatRoom> &chatRoom) : QObjec
mLastUpdatedTime = QDateTime::fromSecsSinceEpoch(chatRoom->getLastUpdateTime()); mLastUpdatedTime = QDateTime::fromSecsSinceEpoch(chatRoom->getLastUpdateTime());
if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Basic)) { if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Basic)) {
mTitle = ToolModel::getDisplayName(chatRoom->getPeerAddress()->clone()); mTitle = ToolModel::getDisplayName(chatRoom->getPeerAddress()->clone());
mAvatarUri = Utils::coreStringToAppString(chatRoom->getPeerAddress()->asStringUriOnly()); mAvatarUri = ToolModel::getDisplayName(chatRoom->getPeerAddress()->clone());
auto peerAddress = chatRoom->getPeerAddress(); auto peerAddress = chatRoom->getPeerAddress();
mPeerAddress = Utils::coreStringToAppString(peerAddress->asStringUriOnly()); mPeerAddress = Utils::coreStringToAppString(peerAddress->asStringUriOnly());
} else { } else {
if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne)) { if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne)) {
auto peer = chatRoom->getParticipants().front();
if (peer) mTitle = ToolModel::getDisplayName(peer->getAddress()->clone());
mAvatarUri = Utils::coreStringToAppString(peer->getAddress()->asStringUriOnly());
auto participants = chatRoom->getParticipants(); auto participants = chatRoom->getParticipants();
auto peer = participants.front();
if (peer) mTitle = ToolModel::getDisplayName(peer->getAddress()->clone());
mAvatarUri = ToolModel::getDisplayName(peer->getAddress()->clone());
if (participants.size() == 1) { if (participants.size() == 1) {
auto peerAddress = participants.front()->getAddress(); auto peerAddress = peer->getAddress();
if (peerAddress) mPeerAddress = Utils::coreStringToAppString(peerAddress->asStringUriOnly()); if (peerAddress) mPeerAddress = Utils::coreStringToAppString(peerAddress->asStringUriOnly());
} }
} else if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Conference)) { } else if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Conference)) {
@ -75,6 +75,7 @@ ChatCore::ChatCore(const std::shared_ptr<linphone::ChatRoom> &chatRoom) : QObjec
auto chatMessage = ChatMessageCore::create(message); auto chatMessage = ChatMessageCore::create(message);
mChatMessageList.append(chatMessage); mChatMessageList.append(chatMessage);
} }
mIdentifier = Utils::coreStringToAppString(chatRoom->getIdentifier());
} }
ChatCore::~ChatCore() { ChatCore::~ChatCore() {
@ -121,6 +122,10 @@ void ChatCore::setTitle(QString title) {
} }
} }
QString ChatCore::getIdentifier() const {
return mIdentifier;
}
QString ChatCore::getPeerAddress() const { QString ChatCore::getPeerAddress() const {
return mPeerAddress; return mPeerAddress;
} }
@ -193,3 +198,7 @@ void ChatCore::removeMessagesFromMessageList(QList<QSharedPointer<ChatMessageCor
} }
if (nbRemoved > 0) emit messageListChanged(); if (nbRemoved > 0) emit messageListChanged();
} }
std::shared_ptr<ChatModel> ChatCore::getModel() const {
return mChatModel;
}

View file

@ -35,6 +35,7 @@ class ChatCore : public QObject, public AbstractObject {
public: public:
Q_PROPERTY(QString title READ getTitle WRITE setTitle NOTIFY titleChanged) Q_PROPERTY(QString title READ getTitle WRITE setTitle NOTIFY titleChanged)
Q_PROPERTY(QString identifier READ getIdentifier CONSTANT)
Q_PROPERTY(QString peerAddress READ getPeerAddress WRITE setPeerAddress NOTIFY peerAddressChanged) Q_PROPERTY(QString peerAddress READ getPeerAddress WRITE setPeerAddress NOTIFY peerAddressChanged)
Q_PROPERTY(QString avatarUri READ getAvatarUri WRITE setAvatarUri NOTIFY avatarUriChanged) Q_PROPERTY(QString avatarUri READ getAvatarUri WRITE setAvatarUri NOTIFY avatarUriChanged)
Q_PROPERTY(QDateTime lastUpdatedTime READ getLastUpdatedTime WRITE setLastUpdatedTime NOTIFY lastUpdatedTimeChanged) Q_PROPERTY(QDateTime lastUpdatedTime READ getLastUpdatedTime WRITE setLastUpdatedTime NOTIFY lastUpdatedTimeChanged)
@ -56,6 +57,8 @@ public:
QString getTitle() const; QString getTitle() const;
void setTitle(QString title); void setTitle(QString title);
QString getIdentifier() const;
QString getLastMessageInHistory() const; QString getLastMessageInHistory() const;
void setLastMessageInHistory(QString message); void setLastMessageInHistory(QString message);
@ -73,6 +76,8 @@ public:
QString getAvatarUri() const; QString getAvatarUri() const;
void setAvatarUri(QString avatarUri); void setAvatarUri(QString avatarUri);
std::shared_ptr<ChatModel> getModel() const;
signals: signals:
void lastUpdatedTimeChanged(QDateTime time); void lastUpdatedTimeChanged(QDateTime time);
void lastMessageInHistoryChanged(QString time); void lastMessageInHistoryChanged(QString time);
@ -88,6 +93,7 @@ private:
QString mLastMessageInHistory; QString mLastMessageInHistory;
QString mPeerAddress; QString mPeerAddress;
QString mTitle; QString mTitle;
QString mIdentifier;
QString mAvatarUri; QString mAvatarUri;
int mUnreadMessagesCount; int mUnreadMessagesCount;
std::shared_ptr<ChatModel> mChatModel; std::shared_ptr<ChatModel> mChatModel;

View file

@ -61,8 +61,7 @@ void ChatList::setSelf(QSharedPointer<ChatList> me) {
// Avoid copy to lambdas // Avoid copy to lambdas
QList<QSharedPointer<ChatCore>> *chats = new QList<QSharedPointer<ChatCore>>(); QList<QSharedPointer<ChatCore>> *chats = new QList<QSharedPointer<ChatCore>>();
auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount(); auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
// auto linphoneChatRooms = currentAccount->filterChatRooms(Utils::appStringToCoreString(mFilter)); auto linphoneChatRooms = currentAccount->filterChatRooms(Utils::appStringToCoreString(mFilter));
auto linphoneChatRooms = currentAccount->getChatRooms();
for (auto it : linphoneChatRooms) { for (auto it : linphoneChatRooms) {
auto model = createChatCore(it); auto model = createChatCore(it);
chats->push_back(model); chats->push_back(model);
@ -75,25 +74,29 @@ void ChatList::setSelf(QSharedPointer<ChatList> me) {
}); });
}); });
mModelConnection->makeConnectToModel(&CoreModel::chatRoomStateChanged, mModelConnection->makeConnectToModel(
[this](const std::shared_ptr<linphone::Core> &core, &CoreModel::chatRoomStateChanged,
const std::shared_ptr<linphone::ChatRoom> &chatRoom, [this](const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::ChatRoom> &chatRoom,
linphone::ChatRoom::State state) { linphone::ChatRoom::State state) {
// check account, filtre, puis ajout si c'est bon // check account, filtre, puis ajout si c'est bon
bool toCreate = false; if (chatRoom->getAccount() == core->getDefaultAccount()) {
if (toCreate) { if (state == linphone::ChatRoom::State::Created) {
auto model = createChatCore(chatRoom); auto list = getSharedList<ChatCore>();
mModelConnection->invokeToCore([this, model]() { auto found =
// We set the current here and not on firstChatStarted event std::find_if(list.begin(), list.end(), [chatRoom](const QSharedPointer<ChatCore> &item) {
// because we don't want to add unicity check while keeping the return (item && item->getModel()->getMonitor() == chatRoom);
// same model between list and current chat.
add(model);
}); });
if (found != list.end()) {
qDebug() << "chat room created, add it to the list";
auto model = createChatCore(chatRoom);
mModelConnection->invokeToCore([this, model]() { add(model); });
}
}
} }
}); });
mModelConnection->makeConnectToModel(&CoreModel::defaultAccountChanged, [this] (std::shared_ptr<linphone::Core> core, std::shared_ptr<linphone::Account> account) { mModelConnection->makeConnectToModel(
lUpdate(); &CoreModel::defaultAccountChanged,
}); [this](std::shared_ptr<linphone::Core> core, std::shared_ptr<linphone::Account> account) { lUpdate(); });
connect(this, &ChatList::filterChanged, [this](QString filter) { connect(this, &ChatList::filterChanged, [this](QString filter) {
mFilter = filter; mFilter = filter;
@ -102,6 +105,16 @@ void ChatList::setSelf(QSharedPointer<ChatList> me) {
lUpdate(); lUpdate();
} }
int ChatList::findChatIndex(ChatGui *chatGui) {
if (!chatGui) return -1;
auto core = chatGui->mCore;
auto chatList = getSharedList<ChatCore>();
auto it = std::find_if(chatList.begin(), chatList.end(), [core](const QSharedPointer<ChatCore> item) {
return item->getIdentifier() == core->getIdentifier();
});
return it == chatList.end() ? -1 : std::distance(chatList.begin(), it);
}
QVariant ChatList::data(const QModelIndex &index, int role) const { QVariant ChatList::data(const QModelIndex &index, int role) const {
int row = index.row(); int row = index.row();
if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant(); if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant();

View file

@ -38,9 +38,11 @@ public:
QSharedPointer<ChatCore> createChatCore(const std::shared_ptr<linphone::ChatRoom> &chatroom); QSharedPointer<ChatCore> createChatCore(const std::shared_ptr<linphone::ChatRoom> &chatroom);
ChatList(QObject *parent = Q_NULLPTR); ChatList(QObject *parent = Q_NULLPTR);
~ChatList(); ~ChatList();
void setSelf(QSharedPointer<ChatList> me); void setSelf(QSharedPointer<ChatList> me);
int findChatIndex(ChatGui *chat);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
signals: signals:
void lUpdate(); void lUpdate();
void filterChanged(QString filter); void filterChanged(QString filter);

View file

@ -40,12 +40,27 @@ void ChatProxy::setSourceModel(QAbstractItemModel *model) {
} }
auto newChatList = dynamic_cast<ChatList *>(model); auto newChatList = dynamic_cast<ChatList *>(model);
if (newChatList) { if (newChatList) {
// connect(this, &ChatProxy::filterTextChanged, newChatList, &ChatList::filterChanged); connect(this, &ChatProxy::filterTextChanged, newChatList,
[this, newChatList] { emit newChatList->filterChanged(getFilterText()); });
} }
setSourceModels(new SortFilterList(model)); setSourceModels(new SortFilterList(model));
sort(0); sort(0);
} }
int ChatProxy::findChatIndex(ChatGui *chatGui) {
auto chatList = getListModel<ChatList>();
if (chatList) {
auto listIndex = chatList->findChatIndex(chatGui);
if (listIndex != -1) {
listIndex =
dynamic_cast<SortFilterList *>(sourceModel())->mapFromSource(chatList->index(listIndex, 0)).row();
if (mMaxDisplayItems <= listIndex) setMaxDisplayItems(listIndex + mDisplayItemsStep);
return listIndex;
}
}
return -1;
}
bool ChatProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { bool ChatProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
// auto l = getItemAtSource<ChatList, ChatCore>(sourceRow); // auto l = getItemAtSource<ChatList, ChatCore>(sourceRow);
// return l != nullptr; // return l != nullptr;

View file

@ -39,6 +39,8 @@ public:
void setSourceModel(QAbstractItemModel *sourceModel) override; void setSourceModel(QAbstractItemModel *sourceModel) override;
Q_INVOKABLE int findChatIndex(ChatGui *chatGui);
protected: protected:
QSharedPointer<ChatList> mList; QSharedPointer<ChatList> mList;
DECLARE_ABSTRACT_OBJECT DECLARE_ABSTRACT_OBJECT

View file

@ -20,6 +20,7 @@
#include "ChatMessageCore.hpp" #include "ChatMessageCore.hpp"
#include "core/App.hpp" #include "core/App.hpp"
#include "model/tool/ToolModel.hpp"
DEFINE_ABSTRACT_OBJECT(ChatMessageCore) DEFINE_ABSTRACT_OBJECT(ChatMessageCore)
@ -31,7 +32,7 @@ QSharedPointer<ChatMessageCore> ChatMessageCore::create(const std::shared_ptr<li
} }
ChatMessageCore::ChatMessageCore(const std::shared_ptr<linphone::ChatMessage> &chatmessage) { ChatMessageCore::ChatMessageCore(const std::shared_ptr<linphone::ChatMessage> &chatmessage) {
lDebug() << "[ChatMessageCore] new" << this; // lDebug() << "[ChatMessageCore] new" << this;
mustBeInLinphoneThread(getClassName()); mustBeInLinphoneThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership); App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mChatMessageModel = Utils::makeQObject_ptr<ChatMessageModel>(chatmessage); mChatMessageModel = Utils::makeQObject_ptr<ChatMessageModel>(chatmessage);
@ -41,6 +42,8 @@ ChatMessageCore::ChatMessageCore(const std::shared_ptr<linphone::ChatMessage> &c
auto from = chatmessage->getFromAddress(); auto from = chatmessage->getFromAddress();
auto to = chatmessage->getLocalAddress(); auto to = chatmessage->getLocalAddress();
mIsRemoteMessage = !from->weakEqual(to); mIsRemoteMessage = !from->weakEqual(to);
mPeerAddress = Utils::coreStringToAppString(chatmessage->getPeerAddress()->asStringUriOnly());
mPeerName = ToolModel::getDisplayName(chatmessage->getPeerAddress()->clone());
} }
ChatMessageCore::~ChatMessageCore() { ChatMessageCore::~ChatMessageCore() {
@ -72,6 +75,14 @@ void ChatMessageCore::setText(QString text) {
} }
} }
QString ChatMessageCore::getPeerAddress() const {
return mPeerAddress;
}
QString ChatMessageCore::getPeerName() const {
return mPeerName;
}
bool ChatMessageCore::isRemoteMessage() const { bool ChatMessageCore::isRemoteMessage() const {
return mIsRemoteMessage; return mIsRemoteMessage;
} }

View file

@ -33,6 +33,8 @@ class ChatMessageCore : public QObject, public AbstractObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QDateTime timestamp READ getTimestamp WRITE setTimestamp NOTIFY timestampChanged) Q_PROPERTY(QDateTime timestamp READ getTimestamp WRITE setTimestamp NOTIFY timestampChanged)
Q_PROPERTY(QString text READ getText WRITE setText NOTIFY textChanged) Q_PROPERTY(QString text READ getText WRITE setText NOTIFY textChanged)
Q_PROPERTY(QString peerAddress READ getPeerAddress CONSTANT)
Q_PROPERTY(QString peerName READ getPeerName CONSTANT)
Q_PROPERTY(bool isRemoteMessage READ isRemoteMessage WRITE setIsRemoteMessage NOTIFY isRemoteMessageChanged) Q_PROPERTY(bool isRemoteMessage READ isRemoteMessage WRITE setIsRemoteMessage NOTIFY isRemoteMessageChanged)
public: public:
@ -47,6 +49,9 @@ public:
QString getText() const; QString getText() const;
void setText(QString text); void setText(QString text);
QString getPeerAddress() const;
QString getPeerName() const;
bool isRemoteMessage() const; bool isRemoteMessage() const;
void setIsRemoteMessage(bool isRemote); void setIsRemoteMessage(bool isRemote);
@ -58,6 +63,8 @@ signals:
private: private:
DECLARE_ABSTRACT_OBJECT DECLARE_ABSTRACT_OBJECT
QString mText; QString mText;
QString mPeerAddress;
QString mPeerName;
QDateTime mTimestamp; QDateTime mTimestamp;
bool mIsRemoteMessage = false; bool mIsRemoteMessage = false;
std::shared_ptr<ChatMessageModel> mChatMessageModel; std::shared_ptr<ChatMessageModel> mChatMessageModel;

View file

@ -33,6 +33,7 @@
#include "core/App.hpp" #include "core/App.hpp"
#include "core/call/CallGui.hpp" #include "core/call/CallGui.hpp"
#include "core/chat/ChatCore.hpp"
#include "model/tool/ToolModel.hpp" #include "model/tool/ToolModel.hpp"
#include "tool/LinphoneEnums.hpp" #include "tool/LinphoneEnums.hpp"
#include "tool/providers/AvatarProvider.hpp" #include "tool/providers/AvatarProvider.hpp"
@ -85,9 +86,9 @@ void setProperty(QObject &object, const char *property, const T &value) {
// ============================================================================= // =============================================================================
const QHash<int, Notifier::Notification> Notifier::Notifications = { const QHash<int, Notifier::Notification> Notifier::Notifications = {
//{Notifier::ReceivedMessage, {Notifier::ReceivedMessage, "NotificationReceivedMessage.qml", 10}}, {Notifier::ReceivedMessage, {Notifier::ReceivedMessage, "NotificationReceivedMessage.qml", 10}},
//{Notifier::ReceivedFileMessage, {Notifier::ReceivedFileMessage, "NotificationReceivedFileMessage.qml", 10}}, //{Notifier::ReceivedFileMessage, {Notifier::ReceivedFileMessage, "NotificationReceivedFileMessage.qml", 10}},
{Notifier::ReceivedCall, {Notifier::ReceivedCall, "NotificationReceivedCall.qml", 30}}, {Notifier::ReceivedCall, {Notifier::ReceivedCall, "NotificationReceivedCall.qml", 30}}
//{Notifier::NewVersionAvailable, {Notifier::NewVersionAvailable, "NotificationNewVersionAvailable.qml", 30}}, //{Notifier::NewVersionAvailable, {Notifier::NewVersionAvailable, "NotificationNewVersionAvailable.qml", 30}},
//{Notifier::SnapshotWasTaken, {Notifier::SnapshotWasTaken, "NotificationSnapshotWasTaken.qml", 10}}, //{Notifier::SnapshotWasTaken, {Notifier::SnapshotWasTaken, "NotificationSnapshotWasTaken.qml", 10}},
//{Notifier::RecordingCompleted, {Notifier::RecordingCompleted, "NotificationRecordingCompleted.qml", 10}} //{Notifier::RecordingCompleted, {Notifier::RecordingCompleted, "NotificationRecordingCompleted.qml", 10}}
@ -127,7 +128,7 @@ Notifier::~Notifier() {
bool Notifier::createNotification(Notifier::NotificationType type, QVariantMap data) { bool Notifier::createNotification(Notifier::NotificationType type, QVariantMap data) {
mMutex->lock(); mMutex->lock();
Q_ASSERT(mInstancesNumber <= MaxNotificationsNumber); // Q_ASSERT(mInstancesNumber <= MaxNotificationsNumber);
if (mInstancesNumber == MaxNotificationsNumber) { // Check existing instances. if (mInstancesNumber == MaxNotificationsNumber) { // Check existing instances.
qWarning() << QStringLiteral("Unable to create another notification."); qWarning() << QStringLiteral("Unable to create another notification.");
mMutex->unlock(); mMutex->unlock();
@ -305,14 +306,35 @@ void Notifier::notifyReceivedCall(const shared_ptr<linphone::Call> &call) {
}); });
} }
/* void Notifier::notifyReceivedMessages(const std::shared_ptr<linphone::ChatRoom> &room,
void Notifier::notifyReceivedMessages(const list<shared_ptr<linphone::ChatMessage>> &messages) { const list<shared_ptr<linphone::ChatMessage>> &messages) {
QVariantMap map; mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
QString txt; QString txt;
QString remoteAddress;
if (messages.size() > 0) { if (messages.size() > 0) {
shared_ptr<linphone::ChatMessage> message = messages.front(); shared_ptr<linphone::ChatMessage> message = messages.front();
if (messages.size() == 1) { auto receiverAccount = ToolModel::findAccount(message->getToAddress());
if (receiverAccount) {
auto senderAccount = ToolModel::findAccount(message->getFromAddress());
if (senderAccount) {
return;
}
auto accountModel = Utils::makeQObject_ptr<AccountModel>(receiverAccount);
accountModel->setSelf(accountModel);
if (!accountModel->getNotificationsAllowed()) {
qInfo() << "Notifications have been disabled for this account - not creating a notification for "
"incoming message";
return;
}
}
if (messages.size() == 1) { // Display only sender on mono message.
auto remoteAddr = message->getFromAddress()->clone();
remoteAddr->clean();
remoteAddress = Utils::coreStringToAppString(remoteAddr->asStringUriOnly());
auto fileContent = message->getFileTransferInformation(); auto fileContent = message->getFileTransferInformation();
if (!fileContent) { if (!fileContent) {
foreach (auto content, message->getContents()) { foreach (auto content, message->getContents()) {
@ -325,23 +347,27 @@ void Notifier::notifyReceivedMessages(const list<shared_ptr<linphone::ChatMessag
if (txt.isEmpty() && message->hasConferenceInvitationContent()) if (txt.isEmpty() && message->hasConferenceInvitationContent())
//: 'Conference invitation received!' : Notification about receiving an invitation to a conference. //: 'Conference invitation received!' : Notification about receiving an invitation to a conference.
txt = tr("new_conference_invitation"); txt = tr("new_conference_invitation");
} else } else {
//: 'New messages received!' Notification that warn the user of new messages. //: 'New messages received!' Notification that warn the user of new messages.
txt = tr("new_chat_room_messages"); txt = tr("new_chat_room_messages");
}
auto chatCore = ChatCore::create(room);
App::postCoreAsync([this, txt, chatCore, remoteAddress]() {
mustBeInMainThread(getClassName());
QVariantMap map;
map["message"] = txt; map["message"] = txt;
shared_ptr<linphone::ChatRoom> chatRoom(message->getChatRoom()); qDebug() << "create notif from address" << remoteAddress;
map["timelineModel"].setValue( map["remoteAddress"] = remoteAddress;
CoreManager::getInstance()->getTimelineListModel()->getTimeline(chatRoom, true).get()); map["chatRoomName"] = chatCore->getTitle();
if (messages.size() == 1) { // Display only sender on mono message. map["chatRoomAddress"] = chatCore->getPeerAddress();
map["remoteAddress"] = Utils::coreStringToAppString(message->getFromAddress()->asStringUriOnly()); map["avatarUri"] = chatCore->getAvatarUri();
map["fullremoteAddress"] = Utils::coreStringToAppString(message->getFromAddress()->asString());
}
map["localAddress"] = Utils::coreStringToAppString(message->getToAddress()->asStringUriOnly());
map["fullLocalAddress"] = Utils::coreStringToAppString(message->getToAddress()->asString());
map["window"].setValue(App::getInstance()->getMainWindow());
CREATE_NOTIFICATION(Notifier::ReceivedMessage, map) CREATE_NOTIFICATION(Notifier::ReceivedMessage, map)
});
} }
} }
/*
void Notifier::notifyReceivedReactions( void Notifier::notifyReceivedReactions(
const QList<QPair<std::shared_ptr<linphone::ChatMessage>, std::shared_ptr<const linphone::ChatMessageReaction>>> const QList<QPair<std::shared_ptr<linphone::ChatMessage>, std::shared_ptr<const linphone::ChatMessageReaction>>>

View file

@ -41,9 +41,9 @@ public:
~Notifier(); ~Notifier();
enum NotificationType { enum NotificationType {
// ReceivedMessage, ReceivedMessage,
// ReceivedFileMessage, // ReceivedFileMessage,
ReceivedCall, ReceivedCall
// NewVersionAvailable, // NewVersionAvailable,
// SnapshotWasTaken, // SnapshotWasTaken,
// RecordingCompleted // RecordingCompleted
@ -52,8 +52,9 @@ public:
// void notifyReceivedCall(Call *call); // void notifyReceivedCall(Call *call);
void notifyReceivedCall(const std::shared_ptr<linphone::Call> &call); // Call from Linphone void notifyReceivedCall(const std::shared_ptr<linphone::Call> &call); // Call from Linphone
void notifyReceivedMessages(const std::shared_ptr<linphone::ChatRoom> &room,
const std::list<std::shared_ptr<linphone::ChatMessage>> &messages);
/* /*
void notifyReceivedMessages(const std::list<std::shared_ptr<linphone::ChatMessage>> &messages);
void notifyReceivedReactions( void notifyReceivedReactions(
const QList<QPair<std::shared_ptr<linphone::ChatMessage>, std::shared_ptr<const const QList<QPair<std::shared_ptr<linphone::ChatMessage>, std::shared_ptr<const
linphone::ChatMessageReaction>>> &reactions); void notifyReceivedFileMessage(const linphone::ChatMessageReaction>>> &reactions); void notifyReceivedFileMessage(const

View file

@ -110,12 +110,14 @@ int MagicSearchProxy::loadUntil(const QString &address) {
auto magicSearchList = getListModel<MagicSearchList>(); auto magicSearchList = getListModel<MagicSearchList>();
if (magicSearchList) { if (magicSearchList) {
auto listIndex = magicSearchList->findFriendIndexByAddress(address); auto listIndex = magicSearchList->findFriendIndexByAddress(address);
if (listIndex == -1) return -1; if (listIndex != -1) {
listIndex = listIndex = dynamic_cast<SortFilterList *>(sourceModel())
dynamic_cast<SortFilterList *>(sourceModel())->mapFromSource(magicSearchList->index(listIndex, 0)).row(); ->mapFromSource(magicSearchList->index(listIndex, 0))
.row();
if (mMaxDisplayItems <= listIndex) setMaxDisplayItems(listIndex + mDisplayItemsStep); if (mMaxDisplayItems <= listIndex) setMaxDisplayItems(listIndex + mDisplayItemsStep);
return listIndex; return listIndex;
} }
}
return -1; return -1;
} }

View file

@ -233,6 +233,10 @@ bool CallModel::getZrtpCaseMismatch() const {
return mMonitor->getZrtpCacheMismatchFlag(); return mMonitor->getZrtpCacheMismatchFlag();
} }
std::shared_ptr<linphone::Conference> CallModel::getConference() const {
return mMonitor->getConference();
}
void CallModel::setConference(const std::shared_ptr<linphone::Conference> &conference) { void CallModel::setConference(const std::shared_ptr<linphone::Conference> &conference) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
if (mConference != conference) { if (mConference != conference) {

View file

@ -72,6 +72,8 @@ public:
QStringList getRemoteAtuhenticationTokens() const; QStringList getRemoteAtuhenticationTokens() const;
bool getZrtpCaseMismatch() const; bool getZrtpCaseMismatch() const;
std::shared_ptr<linphone::Conference> getConference() const;
LinphoneEnums::ConferenceLayout getConferenceVideoLayout() const; LinphoneEnums::ConferenceLayout getConferenceVideoLayout() const;
void changeConferenceVideoLayout(LinphoneEnums::ConferenceLayout layout); // Make a call request void changeConferenceVideoLayout(LinphoneEnums::ConferenceLayout layout); // Make a call request
void updateConferenceVideoLayout(); // Called from call state changed ater the new layout has been set. void updateConferenceVideoLayout(); // Called from call state changed ater the new layout has been set.

View file

@ -54,6 +54,10 @@ std::list<std::shared_ptr<linphone::ChatMessage>> ChatModel::getHistory() const
return res; return res;
} }
QString ChatModel::getIdentifier() const {
return Utils::coreStringToAppString(mMonitor->getIdentifier());
}
QString ChatModel::getTitle() { QString ChatModel::getTitle() {
if (mMonitor->hasCapability((int)linphone::ChatRoom::Capabilities::Basic)) { if (mMonitor->hasCapability((int)linphone::ChatRoom::Capabilities::Basic)) {
return ToolModel::getDisplayName(mMonitor->getPeerAddress()->clone()); return ToolModel::getDisplayName(mMonitor->getPeerAddress()->clone());
@ -65,6 +69,7 @@ QString ChatModel::getTitle() {
return Utils::coreStringToAppString(mMonitor->getSubject()); return Utils::coreStringToAppString(mMonitor->getSubject());
} }
} }
return QString();
} }
QString ChatModel::getPeerAddress() const { QString ChatModel::getPeerAddress() const {
@ -101,15 +106,14 @@ void ChatModel::onIsComposingReceived(const std::shared_ptr<linphone::ChatRoom>
emit isComposingReceived(chatRoom, remoteAddress, isComposing); emit isComposingReceived(chatRoom, remoteAddress, isComposing);
} }
// Do not use this api, only manipulate EventLogs
void ChatModel::onMessageReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom, void ChatModel::onMessageReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message) { const std::shared_ptr<linphone::ChatMessage> &message) {
// emit messageReceived(chatRoom, message); emit messageReceived(chatRoom, message);
} }
void ChatModel::onMessagesReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom, void ChatModel::onMessagesReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::list<std::shared_ptr<linphone::ChatMessage>> &chatMessages) { const std::list<std::shared_ptr<linphone::ChatMessage>> &chatMessages) {
// emit messagesReceived(chatRoom, chatMessages); emit messagesReceived(chatRoom, chatMessages);
} }
void ChatModel::onNewEvent(const std::shared_ptr<linphone::ChatRoom> &chatRoom, void ChatModel::onNewEvent(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
@ -258,5 +262,5 @@ void ChatModel::onChatRoomRead(const std::shared_ptr<linphone::ChatRoom> &chatRo
void ChatModel::onNewMessageReaction(const std::shared_ptr<linphone::ChatRoom> &chatRoom, void ChatModel::onNewMessageReaction(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message, const std::shared_ptr<linphone::ChatMessage> &message,
const std::shared_ptr<const linphone::ChatMessageReaction> &reaction) { const std::shared_ptr<const linphone::ChatMessageReaction> &reaction) {
emit onNewMessageReaction(chatRoom, message, reaction); // emit onNewMessageReaction(chatRoom, message, reaction);
} }

View file

@ -43,6 +43,7 @@ public:
QString getLastMessageInHistory(std::list<std::shared_ptr<linphone::Content>> startList = {}) const; QString getLastMessageInHistory(std::list<std::shared_ptr<linphone::Content>> startList = {}) const;
int getUnreadMessagesCount() const; int getUnreadMessagesCount() const;
std::list<std::shared_ptr<linphone::ChatMessage>> getHistory() const; std::list<std::shared_ptr<linphone::ChatMessage>> getHistory() const;
QString getIdentifier() const;
private: private:
DECLARE_ABSTRACT_OBJECT DECLARE_ABSTRACT_OBJECT

View file

@ -32,7 +32,7 @@ DEFINE_ABSTRACT_OBJECT(ChatMessageModel)
ChatMessageModel::ChatMessageModel(const std::shared_ptr<linphone::ChatMessage> &chatMessage, QObject *parent) ChatMessageModel::ChatMessageModel(const std::shared_ptr<linphone::ChatMessage> &chatMessage, QObject *parent)
: ::Listener<linphone::ChatMessage, linphone::ChatMessageListener>(chatMessage, parent) { : ::Listener<linphone::ChatMessage, linphone::ChatMessageListener>(chatMessage, parent) {
lDebug() << "[ChatMessageModel] new" << this << " / SDKModel=" << chatMessage.get(); // lDebug() << "[ChatMessageModel] new" << this << " / SDKModel=" << chatMessage.get();
mustBeInLinphoneThread(getClassName()); mustBeInLinphoneThread(getClassName());
} }
@ -44,6 +44,10 @@ QString ChatMessageModel::getText() const {
return ToolModel::getMessageFromContent(mMonitor->getContents()); return ToolModel::getMessageFromContent(mMonitor->getContents());
} }
QString ChatMessageModel::getPeerAddress() const {
return Utils::coreStringToAppString(mMonitor->getPeerAddress()->asStringUriOnly());
}
QDateTime ChatMessageModel::getTimestamp() const { QDateTime ChatMessageModel::getTimestamp() const {
return QDateTime::fromSecsSinceEpoch(mMonitor->getTime()); return QDateTime::fromSecsSinceEpoch(mMonitor->getTime());
} }

View file

@ -40,6 +40,8 @@ public:
QString getText() const; QString getText() const;
QDateTime getTimestamp() const; QDateTime getTimestamp() const;
QString getPeerAddress() const;
private: private:
DECLARE_ABSTRACT_OBJECT DECLARE_ABSTRACT_OBJECT
virtual std::shared_ptr<linphone::Buffer> onFileTransferSend(const std::shared_ptr<linphone::ChatMessage> &message, virtual std::shared_ptr<linphone::Buffer> onFileTransferSend(const std::shared_ptr<linphone::ChatMessage> &message,

View file

@ -76,6 +76,10 @@ int ConferenceModel::getParticipantDeviceCount() const {
return mMonitor->getParticipantDeviceList().size(); return mMonitor->getParticipantDeviceList().size();
} }
std::shared_ptr<linphone::ChatRoom> ConferenceModel::getChatRoom() const {
return mMonitor->getChatRoom();
}
void ConferenceModel::setMicrophoneMuted(bool isMuted) { void ConferenceModel::setMicrophoneMuted(bool isMuted) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mMonitor->setMicrophoneMuted(isMuted); mMonitor->setMicrophoneMuted(isMuted);
@ -171,7 +175,8 @@ bool ConferenceModel::isScreenSharingEnabled() const {
void ConferenceModel::onActiveSpeakerParticipantDevice( void ConferenceModel::onActiveSpeakerParticipantDevice(
const std::shared_ptr<linphone::Conference> &conference, const std::shared_ptr<linphone::Conference> &conference,
const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice) { const std::shared_ptr<const linphone::ParticipantDevice> &participantDevice) {
lDebug() << "onActiveSpeakerParticipantDevice: " << (participantDevice ? participantDevice->getAddress()->asString().c_str() : "NULL"); lDebug() << "onActiveSpeakerParticipantDevice: "
<< (participantDevice ? participantDevice->getAddress()->asString().c_str() : "NULL");
emit activeSpeakerParticipantDevice(conference, conference->getActiveSpeakerParticipantDevice()); emit activeSpeakerParticipantDevice(conference, conference->getActiveSpeakerParticipantDevice());
} }

View file

@ -57,6 +57,8 @@ public:
void removeParticipant(const std::shared_ptr<linphone::Address> &address); void removeParticipant(const std::shared_ptr<linphone::Address> &address);
void addParticipant(const std::shared_ptr<linphone::Address> &address); void addParticipant(const std::shared_ptr<linphone::Address> &address);
std::shared_ptr<linphone::ChatRoom> getChatRoom() const;
int getParticipantDeviceCount() const; int getParticipantDeviceCount() const;
void onIsScreenSharingEnabledChanged(); void onIsScreenSharingEnabledChanged();

View file

@ -122,9 +122,8 @@ void CoreModel::start() {
linphoneSearch->setLimitedSearch(true); linphoneSearch->setLimitedSearch(true);
mMagicSearch = Utils::makeQObject_ptr<MagicSearchModel>(linphoneSearch); mMagicSearch = Utils::makeQObject_ptr<MagicSearchModel>(linphoneSearch);
mMagicSearch->setSelf(mMagicSearch); mMagicSearch->setSelf(mMagicSearch);
connect(mMagicSearch.get(), &MagicSearchModel::searchResultsReceived, this, [this] { connect(mMagicSearch.get(), &MagicSearchModel::searchResultsReceived, this,
emit magicSearchResultReceived(mMagicSearch->mLastSearch); [this] { emit magicSearchResultReceived(mMagicSearch->mLastSearch); });
});
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -352,7 +351,8 @@ void CoreModel::migrate() {
config->setInt(SettingsModel::UiSection, Constants::RcVersionName, Constants::RcVersionCurrent); config->setInt(SettingsModel::UiSection, Constants::RcVersionName, Constants::RcVersionCurrent);
} }
void CoreModel::searchInMagicSearch(QString filter, int sourceFlags, void CoreModel::searchInMagicSearch(QString filter,
int sourceFlags,
LinphoneEnums::MagicSearchAggregation aggregation, LinphoneEnums::MagicSearchAggregation aggregation,
int maxResults) { int maxResults) {
mMagicSearch->search(filter, sourceFlags, aggregation, maxResults); mMagicSearch->search(filter, sourceFlags, aggregation, maxResults);
@ -503,12 +503,16 @@ void CoreModel::onMessageReceived(const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::ChatRoom> &room, const std::shared_ptr<linphone::ChatRoom> &room,
const std::shared_ptr<linphone::ChatMessage> &message) { const std::shared_ptr<linphone::ChatMessage> &message) {
emit unreadNotificationsChanged(); emit unreadNotificationsChanged();
std::list<std::shared_ptr<linphone::ChatMessage>> messages;
messages.push_back(message);
App::getInstance()->getNotifier()->notifyReceivedMessages(room, messages);
emit messageReceived(core, room, message); emit messageReceived(core, room, message);
} }
void CoreModel::onMessagesReceived(const std::shared_ptr<linphone::Core> &core, void CoreModel::onMessagesReceived(const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::ChatRoom> &room, const std::shared_ptr<linphone::ChatRoom> &room,
const std::list<std::shared_ptr<linphone::ChatMessage>> &messages) { const std::list<std::shared_ptr<linphone::ChatMessage>> &messages) {
emit unreadNotificationsChanged(); emit unreadNotificationsChanged();
App::getInstance()->getNotifier()->notifyReceivedMessages(room, messages);
emit messagesReceived(core, room, messages); emit messagesReceived(core, room, messages);
} }

View file

@ -34,8 +34,8 @@
#include "model/cli/CliModel.hpp" #include "model/cli/CliModel.hpp"
#include "model/listener/Listener.hpp" #include "model/listener/Listener.hpp"
#include "model/logger/LoggerModel.hpp" #include "model/logger/LoggerModel.hpp"
#include "tool/AbstractObject.hpp"
#include "model/search/MagicSearchModel.hpp" #include "model/search/MagicSearchModel.hpp"
#include "tool/AbstractObject.hpp"
// ============================================================================= // =============================================================================

View file

@ -120,10 +120,8 @@ std::shared_ptr<linphone::Friend> ToolModel::findFriendByAddress(const QString &
auto defaultFriendList = CoreModel::getInstance()->getCore()->getDefaultFriendList(); auto defaultFriendList = CoreModel::getInstance()->getCore()->getDefaultFriendList();
if (!defaultFriendList) return nullptr; if (!defaultFriendList) return nullptr;
auto linphoneAddr = ToolModel::interpretUrl(address); auto linphoneAddr = ToolModel::interpretUrl(address);
if (linphoneAddr) if (linphoneAddr) return ToolModel::findFriendByAddress(linphoneAddr);
return ToolModel::findFriendByAddress(linphoneAddr); else return nullptr;
else
return nullptr;
} }
std::shared_ptr<linphone::Friend> ToolModel::findFriendByAddress(std::shared_ptr<linphone::Address> linphoneAddr) { std::shared_ptr<linphone::Friend> ToolModel::findFriendByAddress(std::shared_ptr<linphone::Address> linphoneAddr) {
@ -146,15 +144,16 @@ std::shared_ptr<linphone::Friend> ToolModel::findFriendByAddress(std::shared_ptr
} }
if (!f) { if (!f) {
if (friendsManager->isInOtherAddresses(key)) { if (friendsManager->isInOtherAddresses(key)) {
// qDebug() << "A magic search has already be done for address" << key << "and nothing was found, return"; // qDebug() << "A magic search has already be done for address" << key << "and nothing was found,
// return";
return nullptr; return nullptr;
} }
friendsManager->appendOtherAddress(key); friendsManager->appendOtherAddress(key);
// qDebug() << "Couldn't find friend" << linphoneAddr->asStringUriOnly() << "in core, use magic search"; // qDebug() << "Couldn't find friend" << linphoneAddr->asStringUriOnly() << "in core, use magic search";
CoreModel::getInstance()->searchInMagicSearch(Utils::coreStringToAppString(linphoneAddr->asStringUriOnly()), CoreModel::getInstance()->searchInMagicSearch(Utils::coreStringToAppString(linphoneAddr->asStringUriOnly()),
(int)linphone::MagicSearch::Source::LdapServers (int)linphone::MagicSearch::Source::LdapServers |
| (int)linphone::MagicSearch::Source::RemoteCardDAV (int)linphone::MagicSearch::Source::RemoteCardDAV,
, LinphoneEnums::MagicSearchAggregation::Friend, 50); LinphoneEnums::MagicSearchAggregation::Friend, 50);
} }
return f; return f;
} }
@ -384,7 +383,6 @@ bool ToolModel::friendIsInFriendList(const std::shared_ptr<linphone::FriendList>
return (it != friends.end()); return (it != friends.end());
} }
QString ToolModel::getMessageFromContent(std::list<std::shared_ptr<linphone::Content>> contents) { QString ToolModel::getMessageFromContent(std::list<std::shared_ptr<linphone::Content>> contents) {
for (auto &content : contents) { for (auto &content : contents) {
if (content->isText()) { if (content->isText()) {
@ -467,3 +465,118 @@ QString ToolModel::computeUserAgent(const std::shared_ptr<linphone::Config> &con
.arg(qVersion()) .arg(qVersion())
.remove("'"); .remove("'");
} }
std::shared_ptr<linphone::ConferenceParams>
ToolModel::getChatRoomParams(std::shared_ptr<linphone::Call> call, std::shared_ptr<linphone::Address> remoteAddress) {
auto core = call ? call->getCore() : CoreModel::getInstance()->getCore();
auto localAddress = call ? call->getCallLog()->getLocalAddress() : nullptr;
if (!remoteAddress && call) remoteAddress = call->getRemoteAddress()->clone();
auto account = findAccount(localAddress);
if (!account) account = core->getDefaultAccount();
if (!account) qWarning() << "failed to get account, return";
if (!account) return nullptr;
auto params = core->createConferenceParams(call ? call->getConference() : nullptr);
params->enableChat(true);
params->enableGroup(false);
//: Dummy subject
params->setSubject(Utils::appStringToCoreString(QObject::tr("chat_dummy_subject")));
params->setAccount(account);
auto chatParams = params->getChatParams();
if (!chatParams) {
qWarning() << "failed to get chat params from conference params, return";
return nullptr;
}
chatParams->setEphemeralLifetime(0);
auto accountParams = account->getParams();
auto sameDomain = remoteAddress && remoteAddress->getDomain() == SettingsModel::getInstance()->getDefaultDomain() &&
remoteAddress->getDomain() == accountParams->getDomain();
if (accountParams->getInstantMessagingEncryptionMandatory() && sameDomain) {
qDebug() << "Account is in secure mode & domain matches, requesting E2E encryption";
chatParams->setBackend(linphone::ChatRoom::Backend::FlexisipChat);
params->setSecurityLevel(linphone::Conference::SecurityLevel::EndToEnd);
} else if (!accountParams->getInstantMessagingEncryptionMandatory()) {
if (SettingsModel::getInstance()->getCreateEndToEndEncryptedMeetingsAndGroupCalls()) {
qDebug() << "Account is in interop mode but LIME is available, requesting E2E encryption";
chatParams->setBackend(linphone::ChatRoom::Backend::FlexisipChat);
params->setSecurityLevel(linphone::Conference::SecurityLevel::EndToEnd);
} else {
qDebug() << "Account is in interop mode and LIME is available, disabling E2E encryption";
chatParams->setBackend(linphone::ChatRoom::Backend::Basic);
params->setSecurityLevel(linphone::Conference::SecurityLevel::None);
}
} else {
qDebug() << "Account is in secure mode, can't chat with SIP address of different domain";
return nullptr;
}
return params;
}
std::shared_ptr<linphone::ChatRoom> ToolModel::lookupCurrentCallChat(std::shared_ptr<CallModel> callModel) {
auto call = callModel->getMonitor();
auto conference = callModel->getConference();
if (conference) {
return conference->getChatRoom();
} else {
auto core = CoreModel::getInstance()->getCore();
auto params = getChatRoomParams(call);
auto remoteaddress = call->getRemoteAddress();
auto localAddress = call->getCallLog()->getLocalAddress();
std::list<std::shared_ptr<linphone::Address>> participants;
participants.push_back(remoteaddress->clone());
qDebug() << "Looking for chat with local address" << localAddress->asStringUriOnly() << "and participant"
<< remoteaddress->asStringUriOnly();
auto existingChat = core->searchChatRoom(params, localAddress, nullptr, participants);
if (existingChat) qDebug() << "Found existing chat";
else qDebug() << "Did not find existing chat";
return existingChat;
}
}
std::shared_ptr<linphone::ChatRoom> ToolModel::createCurrentCallChat(std::shared_ptr<CallModel> callModel) {
auto call = callModel->getMonitor();
auto remoteAddress = call->getRemoteAddress();
std::list<std::shared_ptr<linphone::Address>> participants;
participants.push_back(remoteAddress->clone());
auto core = CoreModel::getInstance()->getCore();
auto params = getChatRoomParams(call);
if (!params) {
qWarning() << "failed to create chatroom params for call with" << remoteAddress->asStringUriOnly();
return nullptr;
}
auto chatRoom = core->createChatRoom(params, participants);
return chatRoom;
}
std::shared_ptr<linphone::ChatRoom> ToolModel::lookupChatForAddress(std::shared_ptr<linphone::Address> remoteAddress) {
auto core = CoreModel::getInstance()->getCore();
auto account = core->getDefaultAccount();
if (!account) return nullptr;
auto localAddress = account->getParams()->getIdentityAddress();
if (!localAddress || !remoteAddress) return nullptr;
auto params = getChatRoomParams(nullptr, remoteAddress);
std::list<std::shared_ptr<linphone::Address>> participants;
participants.push_back(remoteAddress->clone());
qDebug() << "Looking for chat with local address" << localAddress->asStringUriOnly() << "and participant"
<< remoteAddress->asStringUriOnly();
auto existingChat = core->searchChatRoom(params, localAddress, nullptr, participants);
if (existingChat) qDebug() << "Found existing chat";
else qDebug() << "Did not find existing chat";
return existingChat;
}
std::shared_ptr<linphone::ChatRoom> ToolModel::createChatForAddress(std::shared_ptr<linphone::Address> remoteAddress) {
std::list<std::shared_ptr<linphone::Address>> participants;
participants.push_back(remoteAddress->clone());
auto core = CoreModel::getInstance()->getCore();
auto params = getChatRoomParams(nullptr, remoteAddress);
if (!params) {
qWarning() << "failed to create chatroom params for address" << remoteAddress->asStringUriOnly();
return nullptr;
}
auto chatRoom = core->createChatRoom(params, participants);
return chatRoom;
}

View file

@ -81,6 +81,13 @@ public:
static QString getOsProduct(); static QString getOsProduct();
static QString computeUserAgent(const std::shared_ptr<linphone::Config> &config); static QString computeUserAgent(const std::shared_ptr<linphone::Config> &config);
static std::shared_ptr<linphone::ConferenceParams>
getChatRoomParams(std::shared_ptr<linphone::Call> call, std::shared_ptr<linphone::Address> remoteAddress = nullptr);
static std::shared_ptr<linphone::ChatRoom> lookupCurrentCallChat(std::shared_ptr<CallModel> callModel);
static std::shared_ptr<linphone::ChatRoom> createCurrentCallChat(std::shared_ptr<CallModel> callModel);
static std::shared_ptr<linphone::ChatRoom> lookupChatForAddress(std::shared_ptr<linphone::Address> remoteAddress);
static std::shared_ptr<linphone::ChatRoom> createChatForAddress(std::shared_ptr<linphone::Address> remoteAddress);
private: private:
DECLARE_ABSTRACT_OBJECT DECLARE_ABSTRACT_OBJECT
}; };

View file

@ -22,6 +22,8 @@
#include "core/App.hpp" #include "core/App.hpp"
#include "core/call/CallGui.hpp" #include "core/call/CallGui.hpp"
#include "core/chat/ChatCore.hpp"
#include "core/chat/ChatGui.hpp"
#include "core/conference/ConferenceCore.hpp" #include "core/conference/ConferenceCore.hpp"
#include "core/conference/ConferenceInfoCore.hpp" #include "core/conference/ConferenceInfoCore.hpp"
#include "core/conference/ConferenceInfoGui.hpp" #include "core/conference/ConferenceInfoGui.hpp"
@ -1508,6 +1510,76 @@ Utils::createFriendDeviceVariant(const QString &name, const QString &address, Li
return map; return map;
} }
VariantObject *Utils::getCurrentCallChat(CallGui *call) {
VariantObject *data = new VariantObject("lookupCurrentCallChat");
if (!data) return nullptr;
if (!call || !call->mCore) return nullptr;
data->makeRequest([callModel = call->mCore->getModel(), data]() {
if (!callModel) return QVariant();
auto linphoneChatRoom = ToolModel::lookupCurrentCallChat(callModel);
if (linphoneChatRoom) {
auto chatCore = ChatCore::create(linphoneChatRoom);
return QVariant::fromValue(new ChatGui(chatCore));
} else {
qDebug() << "Did not find existing chat room, create one";
linphoneChatRoom = ToolModel::createCurrentCallChat(callModel);
if (linphoneChatRoom != nullptr) {
qDebug() << "Chatroom created with" << callModel->getRemoteAddress()->asStringUriOnly();
auto id = linphoneChatRoom->getIdentifier();
auto params = linphoneChatRoom->getCurrentParams();
auto chatCore = ChatCore::create(linphoneChatRoom);
return QVariant::fromValue(new ChatGui(chatCore));
} else {
qWarning() << "Failed to create 1-1 conversation with"
<< callModel->getRemoteAddress()->asStringUriOnly() << "!";
//: Failed to create 1-1 conversation with %1 !
data->mConnection->invokeToCore([] {
showInformationPopup(tr("information_popup_error_title"),
tr("information_popup_chatroom_creation_error_message"), false,
getCallsWindow());
});
return QVariant();
}
}
});
data->requestValue();
return data;
}
VariantObject *Utils::getChatForAddress(QString address) {
VariantObject *data = new VariantObject("lookupCurrentCallChat");
if (!data) return nullptr;
data->makeRequest([address, data]() {
auto linAddr = ToolModel::interpretUrl(address);
if (!linAddr) return QVariant();
auto linphoneChatRoom = ToolModel::lookupChatForAddress(linAddr);
if (linphoneChatRoom) {
auto chatCore = ChatCore::create(linphoneChatRoom);
return QVariant::fromValue(new ChatGui(chatCore));
} else {
qDebug() << "Did not find existing chat room, create one";
linphoneChatRoom = ToolModel::createChatForAddress(linAddr);
if (linphoneChatRoom != nullptr) {
qDebug() << "Chatroom created with" << linAddr->asStringUriOnly();
auto params = linphoneChatRoom->getCurrentParams();
auto chatCore = ChatCore::create(linphoneChatRoom);
return QVariant::fromValue(new ChatGui(chatCore));
} else {
qWarning() << "Failed to create 1-1 conversation with" << linAddr->asStringUriOnly() << "!";
//: Failed to create 1-1 conversation with %1 !
data->mConnection->invokeToCore([] {
showInformationPopup(tr("information_popup_error_title"),
tr("information_popup_chatroom_creation_error_message"), false,
getCallsWindow());
});
return QVariant();
}
}
});
data->requestValue();
return data;
}
// CLI // CLI
void Utils::runCommandLine(const QString command) { void Utils::runCommandLine(const QString command) {

View file

@ -51,6 +51,7 @@ class ConferenceInfoGui;
class ConferenceCore; class ConferenceCore;
class ParticipantDeviceCore; class ParticipantDeviceCore;
class DownloadablePayloadTypeCore; class DownloadablePayloadTypeCore;
class ChatGui;
class Utils : public QObject, public AbstractObject { class Utils : public QObject, public AbstractObject {
Q_OBJECT Q_OBJECT
@ -88,8 +89,10 @@ public:
Q_INVOKABLE static QString createAvatar(const QUrl &fileUrl); // Return the avatar path Q_INVOKABLE static QString createAvatar(const QUrl &fileUrl); // Return the avatar path
Q_INVOKABLE static QString formatElapsedTime(int seconds, Q_INVOKABLE static QString formatElapsedTime(int seconds,
bool dotsSeparator = true); // Return the elapsed time formated bool dotsSeparator = true); // Return the elapsed time formated
Q_INVOKABLE static QString Q_INVOKABLE static QString formatDate(const QDateTime &date,
formatDate(const QDateTime &date, bool includeTime = true, bool includeDateIfToday = true, QString format = ""); // Return the date formated bool includeTime = true,
bool includeDateIfToday = true,
QString format = ""); // Return the date formated
Q_INVOKABLE static QString formatDateElapsedTime(const QDateTime &date); Q_INVOKABLE static QString formatDateElapsedTime(const QDateTime &date);
Q_INVOKABLE static QString formatTime(const QDateTime &date); // Return the time formated Q_INVOKABLE static QString formatTime(const QDateTime &date); // Return the time formated
Q_INVOKABLE static QStringList generateSecurityLettersArray(int arraySize, int correctIndex, QString correctCode); Q_INVOKABLE static QStringList generateSecurityLettersArray(int arraySize, int correctIndex, QString correctCode);
@ -140,6 +143,8 @@ public:
Q_INVOKABLE QList<QVariant> append(const QList<QVariant> a, const QList<QVariant> b); Q_INVOKABLE QList<QVariant> append(const QList<QVariant> a, const QList<QVariant> b);
Q_INVOKABLE QString getAddressToDisplay(QVariantList addressList, QString filter, QString defaultAddress); Q_INVOKABLE QString getAddressToDisplay(QVariantList addressList, QString filter, QString defaultAddress);
Q_INVOKABLE static VariantObject *getCurrentCallChat(CallGui *call);
Q_INVOKABLE static VariantObject *getChatForAddress(QString address);
// QDir findDirectoryByName(QString startPath, QString name); // QDir findDirectoryByName(QString startPath, QString name);
static QString getApplicationProduct(); static QString getApplicationProduct();

View file

@ -88,6 +88,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Control/Popup/Loading/LoadingPopup.qml view/Control/Popup/Loading/LoadingPopup.qml
view/Control/Popup/Notification/Notification.qml view/Control/Popup/Notification/Notification.qml
view/Control/Popup/Notification/NotificationReceivedCall.qml view/Control/Popup/Notification/NotificationReceivedCall.qml
view/Control/Popup/Notification/NotificationReceivedMessage.qml
view/Control/Tool/MovableMouseArea.qml view/Control/Tool/MovableMouseArea.qml
view/Control/Tool/Helper/utils.js view/Control/Tool/Helper/utils.js

View file

@ -32,6 +32,7 @@ Control.Button {
property bool shadowEnabled: false property bool shadowEnabled: false
property var contentImageColor: style?.image?.normal || DefaultStyle.main2_600 property var contentImageColor: style?.image?.normal || DefaultStyle.main2_600
property var hoveredImageColor: style?.image?.pressed || Qt.darker(contentImageColor, 1.05) property var hoveredImageColor: style?.image?.pressed || Qt.darker(contentImageColor, 1.05)
property var checkedImageColor: style?.image?.checked || Qt.darker(contentImageColor, 1.1)
property var pressedImageColor: style?.image?.pressed || Qt.darker(contentImageColor, 1.1) property var pressedImageColor: style?.image?.pressed || Qt.darker(contentImageColor, 1.1)
property bool asynchronous: false property bool asynchronous: false
spacing: Math.round(5 * DefaultStyle.dp) spacing: Math.round(5 * DefaultStyle.dp)
@ -126,7 +127,7 @@ Control.Button {
imageWidth: mainItem.icon.width imageWidth: mainItem.icon.width
imageHeight: mainItem.icon.height imageHeight: mainItem.icon.height
colorizationColor: mainItem.checkable && mainItem.checked colorizationColor: mainItem.checkable && mainItem.checked
? mainItem.checkedColor || mainItem.pressedColor ? mainItem.checkedImageColor || mainItem.checkedColor || mainItem.pressedColor
: mainItem.pressed : mainItem.pressed
? mainItem.pressedImageColor ? mainItem.pressedImageColor
: mainItem.hovered : mainItem.hovered

View file

@ -193,7 +193,12 @@ ColumnLayout {
button.icon.source: AppIcons.chatTeardropText button.icon.source: AppIcons.chatTeardropText
//: "Message" //: "Message"
label: qsTr("contact_message_action") label: qsTr("contact_message_action")
button.onClicked: console.debug("[ContactLayout.qml] TODO : open conversation") button.onClicked: {
console.debug("[CallHistoryLayout.qml] Open conversation")
if (mainItem.specificAddress === "") {
mainWindow.displayChatPage(mainItem.contact.core.defaultAddress)
} else mainWindow.displayChatPage(mainItem.specificAddress)
}
} }
LabelButton { LabelButton {
visible: !mainItem.isConference && SettingsCpp.videoEnabled visible: !mainItem.isConference && SettingsCpp.videoEnabled

View file

@ -10,6 +10,7 @@ ColumnLayout {
property color panelColor: DefaultStyle.grey_100 property color panelColor: DefaultStyle.grey_100
property alias headerContent: rightPanelHeader.children property alias headerContent: rightPanelHeader.children
property alias content: rightPanelContent.children property alias content: rightPanelContent.children
property alias header: rightPanelHeader
spacing: 0 spacing: 0
Rectangle { Rectangle {

View file

@ -42,6 +42,11 @@ ListView {
// flickDeceleration: 10000 // flickDeceleration: 10000
spacing: Math.round(10 * DefaultStyle.dp) spacing: Math.round(10 * DefaultStyle.dp)
function selectChat(chatGui) {
var index = chatProxy.findChatIndex(chatGui)
mainItem.currentIndex = index
}
Component.onCompleted: cacheBuffer = Math.max(contentHeight, 0) //contentHeight>0 ? contentHeight : 0// cache all items Component.onCompleted: cacheBuffer = Math.max(contentHeight, 0) //contentHeight>0 ? contentHeight : 0// cache all items
// remove binding loop // remove binding loop
onContentHeightChanged: Qt.callLater(function () { onContentHeightChanged: Qt.callLater(function () {
@ -64,6 +69,7 @@ ListView {
chatProxy.displayMore() chatProxy.displayMore()
} }
} }
//---------------------------------------------------------------- //----------------------------------------------------------------
function moveToCurrentItem() { function moveToCurrentItem() {
if (mainItem.currentIndex >= 0) if (mainItem.currentIndex >= 0)
@ -109,8 +115,8 @@ ListView {
component UnreadNotification: Item { component UnreadNotification: Item {
id: unreadNotif id: unreadNotif
property int unread: 0 property int unread: 0
width: Math.round(22 * DefaultStyle.dp) width: Math.round(14 * DefaultStyle.dp)
height: Math.round(22 * DefaultStyle.dp) height: Math.round(14 * DefaultStyle.dp)
visible: unread > 0 visible: unread > 0
Rectangle { Rectangle {
id: background id: background
@ -123,7 +129,7 @@ ListView {
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
color: DefaultStyle.grey_0 color: DefaultStyle.grey_0
fontSizeMode: Text.Fit fontSizeMode: Text.Fit
font.pixelSize: Typography.p3.pixelSize font.pixelSize: Math.round(10 * DefaultStyle.dp)
text: parent.unreadNotif > 100 ? '99+' : unreadNotif.unread text: parent.unreadNotif > 100 ? '99+' : unreadNotif.unread
} }
} }
@ -155,11 +161,7 @@ ListView {
id: historyAvatar id: historyAvatar
property var contactObj: UtilsCpp.findFriendByAddress(modelData.core.peerAddress) property var contactObj: UtilsCpp.findFriendByAddress(modelData.core.peerAddress)
contact: contactObj?.value || null contact: contactObj?.value || null
onContactChanged: { displayNameVal: contact ? "" : modelData.core.avatarUri
if (contact) console.log("found contact", contact.core.defaultAddress)
else console.log("no contact for peer address", modelData.core.peerAddress, modelData.core.avatarUri)
}
displayNameVal: contact ? undefined : modelData.core.avatarUri
// secured: securityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified // secured: securityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified
Layout.preferredWidth: Math.round(45 * DefaultStyle.dp) Layout.preferredWidth: Math.round(45 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) Layout.preferredHeight: Math.round(45 * DefaultStyle.dp)
@ -208,6 +210,7 @@ ListView {
} }
RowLayout { RowLayout {
Item {Layout.fillWidth: true}
UnreadNotification { UnreadNotification {
id: unreadCount id: unreadCount
unread: modelData.core.unreadMessagesCount unread: modelData.core.unreadMessagesCount
@ -230,10 +233,8 @@ ListView {
anchors.fill: parent anchors.fill: parent
opacity: 0.7 opacity: 0.7
radius: Math.round(8 * DefaultStyle.dp) radius: Math.round(8 * DefaultStyle.dp)
color: mainItem.currentIndex color: mainItem.currentIndex === index ? DefaultStyle.main2_200 : DefaultStyle.main2_100
=== index ? DefaultStyle.main2_200 : DefaultStyle.main2_100 visible: mainItem.lastMouseContainsIndex === index || mainItem.currentIndex === index
visible: mainItem.lastMouseContainsIndex === index
|| mainItem.currentIndex === index
} }
onPressed: { onPressed: {
mainItem.currentIndex = model.index mainItem.currentIndex = model.index

View file

@ -19,6 +19,10 @@ ListView {
chatGui: mainItem.chat chatGui: mainItem.chat
} }
header: Item {
height: Math.round(18 * DefaultStyle.dp)
}
delegate: ChatMessage { delegate: ChatMessage {
id: chatMessage id: chatMessage
width: Math.min(implicitWidth, Math.round(mainItem.width * (3/4))) width: Math.min(implicitWidth, Math.round(mainItem.width * (3/4)))

View file

@ -170,6 +170,10 @@ FocusScope {
style: ButtonStyle.grey style: ButtonStyle.grey
KeyNavigation.left: videoCallButton KeyNavigation.left: videoCallButton
KeyNavigation.right: callButton KeyNavigation.right: callButton
onClicked: {
console.debug("[ContactListItem.qml] Open conversation")
mainWindow.displayChatPage(mainItem.addressFromFilter)
}
} }
} }
PopupButton { PopupButton {

View file

@ -0,0 +1,118 @@
import QtQuick
import QtQuick.Layouts
import Linphone
import UtilsCpp
import QtQuick.Controls as Control
import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle
// =============================================================================
Notification {
id: mainItem
radius: Math.round(20 * DefaultStyle.dp)
backgroundColor: DefaultStyle.grey_600
backgroundOpacity: 0.8
overriddenWidth: Math.round(400 * DefaultStyle.dp)
overriddenHeight: content.height
property string avatarUri: notificationData && notificationData.avatarUri
property string chatRoomName: notificationData && notificationData.chatRoomName
property string remoteAddress: notificationData && notificationData.remoteAddress
property string message: notificationData && notificationData.message
Popup {
id: content
visible: mainItem.visible
width: parent.width
leftPadding: Math.round(18 * DefaultStyle.dp)
rightPadding: Math.round(18 * DefaultStyle.dp)
topPadding: Math.round(9 * DefaultStyle.dp)
bottomPadding: Math.round(18 * DefaultStyle.dp)
background: Item{}
contentItem: ColumnLayout {
spacing: Math.round(9 * DefaultStyle.dp)
RowLayout {
spacing: Math.round(4 * DefaultStyle.dp)
Layout.alignment: Qt.AlignHCenter
Image {
Layout.preferredWidth: Math.round(12 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(12 * DefaultStyle.dp)
source: AppIcons.logo
}
Text {
text: "Linphone"
color: DefaultStyle.grey_0
font {
pixelSize: Math.round(12 * DefaultStyle.dp)
weight: Typography.b3.weight
capitalization: Font.Capitalize
}
}
}
ColumnLayout {
spacing: Math.round(14 * DefaultStyle.dp)
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
RowLayout {
spacing: Math.round(10 * DefaultStyle.dp)
Avatar {
Layout.preferredWidth: Math.round(45 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(45 * DefaultStyle.dp)
Layout.alignment: Qt.AlignHCenter
property var contactObj: UtilsCpp.findFriendByAddress(mainItem.remoteAddress)
contact: contactObj?.value || null
displayNameVal: contact ? "" : mainItem.avatarUri
}
ColumnLayout {
spacing: 0
Text {
text: mainItem.chatRoomName
color: DefaultStyle.main2_200
Layout.fillWidth: true
maximumLineCount: 1
font {
pixelSize: Typography.h3.pixelSize
weight: Typography.h3.weight
capitalization: Font.Capitalize
}
}
Text {
text: mainItem.remoteAddress
color: DefaultStyle.main2_100
Layout.fillWidth: true
maximumLineCount: 1
font {
pixelSize: Typography.p1.pixelSize
weight: Typography.p1.weight
}
}
}
Item{Layout.fillWidth: true}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: Math.round(60 * DefaultStyle.dp)
color: DefaultStyle.main2_400
radius: Math.round(5 * DefaultStyle.dp)
Text {
anchors.fill: parent
anchors.leftMargin: 8 * DefaultStyle.dp
anchors.rightMargin: 8 * DefaultStyle.dp
anchors.topMargin: 8 * DefaultStyle.dp
anchors.bottomMargin: 8 * DefaultStyle.dp
verticalAlignment: Text.AlignVCenter
text: mainItem.message
maximumLineCount: 2
color: DefaultStyle.grey_1000
font {
pixelSize: Typography.p1s.pixelSize
weight: Typography.p1s.weight
italic: true
}
}
}
}
}
}
}

View file

@ -12,21 +12,26 @@ import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle
RowLayout { RowLayout {
id: mainItem id: mainItem
property ChatGui chat property ChatGui chat
property CallGui call
property alias callHeaderContent: splitPanel.headerContent
spacing: 0 spacing: 0
MainRightPanel { MainRightPanel {
id: splitPanel
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
panelColor: DefaultStyle.grey_0 panelColor: DefaultStyle.grey_0
header.visible: !mainItem.call
clip: true clip: true
headerContent: [ headerContent: [
RowLayout { RowLayout {
anchors.left: parent.left anchors.left: parent?.left
anchors.leftMargin: Math.round(31 * DefaultStyle.dp) anchors.leftMargin: mainItem.call ? 0 : Math.round(31 * DefaultStyle.dp)
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent?.verticalCenter
spacing: Math.round(12 * DefaultStyle.dp) spacing: Math.round(12 * DefaultStyle.dp)
Avatar { Avatar {
property var contactObj: mainItem.chat ? UtilsCpp.findFriendByAddress(mainItem.chat.core.peerAddress) : null property var contactObj: mainItem.chat ? UtilsCpp.findFriendByAddress(mainItem.chat?.core.peerAddress) : null
contact: contactObj?.value || null contact: contactObj?.value || null
displayNameVal: contact ? "" : mainItem.chat.core.avatarUri
Layout.preferredWidth: Math.round(45 * DefaultStyle.dp) Layout.preferredWidth: Math.round(45 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) Layout.preferredHeight: Math.round(45 * DefaultStyle.dp)
} }
@ -38,6 +43,7 @@ RowLayout {
font { font {
pixelSize: Typography.h4.pixelSize pixelSize: Typography.h4.pixelSize
weight: Math.round(400 * DefaultStyle.dp) weight: Math.round(400 * DefaultStyle.dp)
capitalization: Font.Capitalize
} }
} }
}, },
@ -51,7 +57,7 @@ RowLayout {
} }
BigButton { BigButton {
style: ButtonStyle.noBackground style: ButtonStyle.noBackground
icon.source: AppIcons.camera icon.source: AppIcons.videoCamera
} }
BigButton { BigButton {
style: ButtonStyle.noBackground style: ButtonStyle.noBackground

View file

@ -26,6 +26,7 @@ Item {
signal openCallHistory signal openCallHistory
signal openNumPadRequest signal openNumPadRequest
signal displayContactRequested(string contactAddress) signal displayContactRequested(string contactAddress)
signal displayChatRequested(string contactAddress)
signal createContactRequested(string name, string address) signal createContactRequested(string name, string address)
signal accountRemoved signal accountRemoved
@ -41,6 +42,10 @@ Item {
tabbar.currentIndex = 1 tabbar.currentIndex = 1
mainItem.displayContactRequested(contactAddress) mainItem.displayContactRequested(contactAddress)
} }
function displayChatPage(contactAddress) {
tabbar.currentIndex = 2
mainItem.displayChatRequested(contactAddress)
}
function createContact(name, address) { function createContact(name, address) {
tabbar.currentIndex = 1 tabbar.currentIndex = 1
@ -616,7 +621,17 @@ Item {
} }
} }
} }
ChatPage{} ChatPage {
id: chatPage
Connections {
target: mainItem
function onDisplayChatRequested(contactAddress) {
console.log("display chat requested, open with address", contactAddress)
chatPage.remoteAddress = ""
chatPage.remoteAddress = contactAddress
}
}
}
MeetingPage {} MeetingPage {}
} }
} }

View file

@ -532,12 +532,9 @@ AbstractMainPage {
onClicked: { onClicked: {
detailOptions.close() detailOptions.close()
if (contactDetail.isLocalFriend) if (contactDetail.isLocalFriend)
mainWindow.displayContactPage( mainWindow.displayContactPage(contactDetail.contactAddress)
contactDetail.contactAddress)
else else
mainItem.createContactRequested( mainItem.createContactRequested(contactDetail.contactName, contactDetail.contactAddress)
contactDetail.contactName,
contactDetail.contactAddress)
} }
} }
IconLabelButton { IconLabelButton {

View file

@ -32,7 +32,7 @@ Control.Page {
header: Control.Control { header: Control.Control {
id: pageHeader id: pageHeader
width: mainItem.width width: mainItem.width
height: Math.round(56 * DefaultStyle.dp) height: Math.round(67 * DefaultStyle.dp)
leftPadding: Math.round(10 * DefaultStyle.dp) leftPadding: Math.round(10 * DefaultStyle.dp)
rightPadding: Math.round(10 * DefaultStyle.dp) rightPadding: Math.round(10 * DefaultStyle.dp)
background: Rectangle { background: Rectangle {

View file

@ -18,6 +18,12 @@ AbstractMainPage {
property var selectedChatGui property var selectedChatGui
property string remoteAddress
onRemoteAddressChanged: console.log("ChatPage : remote address changed :", remoteAddress)
property var remoteChatObj: UtilsCpp.getChatForAddress(remoteAddress)
property ChatGui remoteChat: remoteChatObj ? remoteChatObj.value : null
onRemoteChatChanged: if (remoteChat) selectedChatGui = remoteChat
onSelectedChatGuiChanged: { onSelectedChatGuiChanged: {
if (selectedChatGui) if (selectedChatGui)
rightPanelStackView.replace(currentChatComp, rightPanelStackView.replace(currentChatComp,
@ -151,6 +157,11 @@ AbstractMainPage {
onCountChanged: { onCountChanged: {
mainItem.selectedChatGui = model.getAt(currentIndex) mainItem.selectedChatGui = model.getAt(currentIndex)
} }
Connections {
target: mainItem
onSelectedChatGuiChanged: chatListView.selectChat(mainItem.selectedChatGui)
}
} }
} }
ScrollBar { ScrollBar {

View file

@ -411,9 +411,7 @@ FriendGui{
height: Math.round(56 * DefaultStyle.dp) height: Math.round(56 * DefaultStyle.dp)
button.icon.width: Math.round(24 * DefaultStyle.dp) button.icon.width: Math.round(24 * DefaultStyle.dp)
button.icon.height: Math.round(24 * DefaultStyle.dp) button.icon.height: Math.round(24 * DefaultStyle.dp)
button.onClicked: mainWindow.startCallWithContact( button.onClicked: mainWindow.startCallWithContact(contactDetail.contact, false, mainItem)
contactDetail.contact,
false, mainItem)
} }
LabelButton { LabelButton {
button.icon.source: AppIcons.chatTeardropText button.icon.source: AppIcons.chatTeardropText
@ -424,8 +422,10 @@ FriendGui{
height: Math.round(56 * DefaultStyle.dp) height: Math.round(56 * DefaultStyle.dp)
button.icon.width: Math.round(24 * DefaultStyle.dp) button.icon.width: Math.round(24 * DefaultStyle.dp)
button.icon.height: Math.round(24 * DefaultStyle.dp) button.icon.height: Math.round(24 * DefaultStyle.dp)
button.onClicked: console.debug( button.onClicked: {
"[ContactLayout.qml] TODO : open conversation") console.debug("[ContactLayout.qml] Open conversation")
mainWindow.displayChatPage(contactDetail.contact.core.defaultAddress)
}
} }
LabelButton { LabelButton {
visible: SettingsCpp.videoEnabled visible: SettingsCpp.videoEnabled
@ -436,9 +436,7 @@ FriendGui{
height: Math.round(56 * DefaultStyle.dp) height: Math.round(56 * DefaultStyle.dp)
button.icon.width: Math.round(24 * DefaultStyle.dp) button.icon.width: Math.round(24 * DefaultStyle.dp)
button.icon.height: Math.round(24 * DefaultStyle.dp) button.icon.height: Math.round(24 * DefaultStyle.dp)
button.onClicked: mainWindow.startCallWithContact( button.onClicked: mainWindow.startCallWithContact(contactDetail.contact, true, mainItem)
contactDetail.contact,
true, mainItem)
} }
} }
bannerContent: [ bannerContent: [
@ -636,7 +634,7 @@ FriendGui{
EffectImage { EffectImage {
Layout.preferredWidth: Math.round(24 * DefaultStyle.dp) Layout.preferredWidth: Math.round(24 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(24 * DefaultStyle.dp) Layout.preferredHeight: Math.round(24 * DefaultStyle.dp)
source: AppIcons.shareNetwork imageSource: AppIcons.shareNetwork
colorizationColor: DefaultStyle.main2_600 colorizationColor: DefaultStyle.main2_600
} }
Text { Text {

View file

@ -620,6 +620,19 @@ AbstractWindow {
contentStackView.initialItem: callListPanel contentStackView.initialItem: callListPanel
headerValidateButtonText: qsTr("add") headerValidateButtonText: qsTr("add")
Binding on topPadding {
when: rightPanel.contentStackView.currentItem.objectName === "chatPanel"
value: 0
}
Binding on leftPadding {
when: rightPanel.contentStackView.currentItem.objectName === "chatPanel"
value: 0
}
Binding on rightPadding {
when: rightPanel.contentStackView.currentItem.objectName == "chatPanel"
value: 0
}
Item { Item {
id: numericPadContainer id: numericPadContainer
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
@ -836,6 +849,28 @@ AbstractWindow {
} }
} }
} }
Component {
id: chatPanel
Item {
id: chatPanelContent
objectName: "chatPanel"
Control.StackView.onActivated: {
rightPanel.customHeaderButtons = chatView.callHeaderContent
rightPanel.headerTitleText = ""
}
Keys.onEscapePressed: event => {
rightPanel.visible = false
event.accepted = true
}
SelectedChatView {
id: chatView
anchors.fill: parent
call: mainWindow.call
property var chatObj: UtilsCpp.getCurrentCallChat(mainWindow.call)
chat: chatObj ? chatObj.value : null
}
}
}
Component { Component {
id: settingsPanel id: settingsPanel
Item { Item {
@ -1313,6 +1348,23 @@ AbstractWindow {
} }
} }
} }
CheckableButton {
iconUrl: AppIcons.chatTeardropText
//: Open chat
ToolTip.text: qsTr("call_open_chat_hint")
Layout.preferredWidth: Math.round(55 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(55 * DefaultStyle.dp)
icon.width: Math.round(32 * DefaultStyle.dp)
icon.height: Math.round(32 * DefaultStyle.dp)
onCheckedChanged: {
if (checked) {
rightPanel.visible = true
rightPanel.replace(chatPanel)
} else {
rightPanel.visible = false
}
}
}
CheckableButton { CheckableButton {
visible: false visible: false
checkable: false checkable: false

View file

@ -56,6 +56,10 @@ AbstractWindow {
openMainPage() openMainPage()
mainWindowStackView.currentItem.displayContactPage(contactAddress) mainWindowStackView.currentItem.displayContactPage(contactAddress)
} }
function displayChatPage(contactAddress) {
openMainPage()
mainWindowStackView.currentItem.displayChatPage(contactAddress)
}
function transferCallSucceed() { function transferCallSucceed() {
openMainPage() openMainPage()
//: "Appel transféré" //: "Appel transféré"

View file

@ -121,7 +121,8 @@
}, },
image: { image: {
normal: Linphone.DefaultStyle.grey_0, normal: Linphone.DefaultStyle.grey_0,
pressed: Linphone.DefaultStyle.grey_0 pressed: Linphone.DefaultStyle.grey_0,
checked: Linphone.DefaultStyle.grey_0
} }
} }