mentions
This commit is contained in:
parent
a7e39ab276
commit
de6d62021a
33 changed files with 566 additions and 98 deletions
|
|
@ -73,6 +73,7 @@
|
||||||
#include "core/notifier/Notifier.hpp"
|
#include "core/notifier/Notifier.hpp"
|
||||||
#include "core/participant/ParticipantDeviceProxy.hpp"
|
#include "core/participant/ParticipantDeviceProxy.hpp"
|
||||||
#include "core/participant/ParticipantGui.hpp"
|
#include "core/participant/ParticipantGui.hpp"
|
||||||
|
#include "core/participant/ParticipantInfoProxy.hpp"
|
||||||
#include "core/participant/ParticipantProxy.hpp"
|
#include "core/participant/ParticipantProxy.hpp"
|
||||||
#include "core/payload-type/PayloadTypeCore.hpp"
|
#include "core/payload-type/PayloadTypeCore.hpp"
|
||||||
#include "core/payload-type/PayloadTypeGui.hpp"
|
#include "core/payload-type/PayloadTypeGui.hpp"
|
||||||
|
|
@ -652,6 +653,7 @@ void App::initCppInterfaces() {
|
||||||
qmlRegisterType<VariantObject>(Constants::MainQmlUri, 1, 0, "VariantObject");
|
qmlRegisterType<VariantObject>(Constants::MainQmlUri, 1, 0, "VariantObject");
|
||||||
qmlRegisterType<VariantList>(Constants::MainQmlUri, 1, 0, "VariantList");
|
qmlRegisterType<VariantList>(Constants::MainQmlUri, 1, 0, "VariantList");
|
||||||
|
|
||||||
|
qmlRegisterType<ParticipantInfoProxy>(Constants::MainQmlUri, 1, 0, "ParticipantInfoProxy");
|
||||||
qmlRegisterType<ParticipantProxy>(Constants::MainQmlUri, 1, 0, "ParticipantProxy");
|
qmlRegisterType<ParticipantProxy>(Constants::MainQmlUri, 1, 0, "ParticipantProxy");
|
||||||
qmlRegisterType<ParticipantGui>(Constants::MainQmlUri, 1, 0, "ParticipantGui");
|
qmlRegisterType<ParticipantGui>(Constants::MainQmlUri, 1, 0, "ParticipantGui");
|
||||||
qmlRegisterType<ConferenceInfoProxy>(Constants::MainQmlUri, 1, 0, "ConferenceInfoProxy");
|
qmlRegisterType<ConferenceInfoProxy>(Constants::MainQmlUri, 1, 0, "ConferenceInfoProxy");
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,8 @@ list(APPEND _LINPHONEAPP_SOURCES
|
||||||
core/participant/ParticipantDeviceProxy.cpp
|
core/participant/ParticipantDeviceProxy.cpp
|
||||||
core/participant/ParticipantList.cpp
|
core/participant/ParticipantList.cpp
|
||||||
core/participant/ParticipantProxy.cpp
|
core/participant/ParticipantProxy.cpp
|
||||||
|
core/participant/ParticipantInfoList.cpp
|
||||||
|
core/participant/ParticipantInfoProxy.cpp
|
||||||
|
|
||||||
core/screen/ScreenList.cpp
|
core/screen/ScreenList.cpp
|
||||||
core/screen/ScreenProxy.cpp
|
core/screen/ScreenProxy.cpp
|
||||||
|
|
|
||||||
|
|
@ -593,3 +593,7 @@ ChatCore::buildParticipants(const std::shared_ptr<linphone::ChatRoom> &chatRoom)
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<QSharedPointer<ParticipantCore>> ChatCore::getParticipants() const {
|
||||||
|
return mParticipants;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,7 @@ public:
|
||||||
std::shared_ptr<ChatModel> getModel() const;
|
std::shared_ptr<ChatModel> getModel() const;
|
||||||
|
|
||||||
QList<QSharedPointer<ParticipantCore>> buildParticipants(const std::shared_ptr<linphone::ChatRoom> &chatRoom) const;
|
QList<QSharedPointer<ParticipantCore>> buildParticipants(const std::shared_ptr<linphone::ChatRoom> &chatRoom) const;
|
||||||
|
QList<QSharedPointer<ParticipantCore>> getParticipants() const;
|
||||||
QVariantList getParticipantsGui() const;
|
QVariantList getParticipantsGui() const;
|
||||||
QStringList getParticipantsAddresses() const;
|
QStringList getParticipantsAddresses() const;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ ChatMessageCore::ChatMessageCore(const std::shared_ptr<linphone::ChatMessage> &c
|
||||||
auto replymessage = chatmessage->getReplyMessage();
|
auto replymessage = chatmessage->getReplyMessage();
|
||||||
if (replymessage) {
|
if (replymessage) {
|
||||||
mReplyText = ToolModel::getMessageFromContent(replymessage->getContents());
|
mReplyText = ToolModel::getMessageFromContent(replymessage->getContents());
|
||||||
if (mIsFromChatGroup) mRepliedToName = ToolModel::getDisplayName(replymessage->getToAddress()->clone());
|
if (mIsFromChatGroup) mRepliedToName = ToolModel::getDisplayName(replymessage->getFromAddress()->clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mImdnStatusList = computeDeliveryStatus(chatmessage);
|
mImdnStatusList = computeDeliveryStatus(chatmessage);
|
||||||
|
|
@ -305,7 +305,10 @@ void ChatMessageCore::setSelf(QSharedPointer<ChatMessageCore> me) {
|
||||||
mChatMessageModelConnection->makeConnectToModel(
|
mChatMessageModelConnection->makeConnectToModel(
|
||||||
&ChatMessageModel::participantImdnStateChanged,
|
&ChatMessageModel::participantImdnStateChanged,
|
||||||
[this](const std::shared_ptr<linphone::ChatMessage> &message,
|
[this](const std::shared_ptr<linphone::ChatMessage> &message,
|
||||||
const std::shared_ptr<const linphone::ParticipantImdnState> &state) {});
|
const std::shared_ptr<const linphone::ParticipantImdnState> &state) {
|
||||||
|
auto imdnStatusList = computeDeliveryStatus(message);
|
||||||
|
mChatMessageModelConnection->invokeToCore([this, imdnStatusList] { setImdnStatusList(imdnStatusList); });
|
||||||
|
});
|
||||||
mChatMessageModelConnection->makeConnectToModel(&ChatMessageModel::ephemeralMessageTimerStarted,
|
mChatMessageModelConnection->makeConnectToModel(&ChatMessageModel::ephemeralMessageTimerStarted,
|
||||||
[this](const std::shared_ptr<linphone::ChatMessage> &message) {});
|
[this](const std::shared_ptr<linphone::ChatMessage> &message) {});
|
||||||
mChatMessageModelConnection->makeConnectToModel(&ChatMessageModel::ephemeralMessageDeleted,
|
mChatMessageModelConnection->makeConnectToModel(&ChatMessageModel::ephemeralMessageDeleted,
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ ImdnStatusList::ImdnStatusList(QObject *parent) : AbstractListProxy<ImdnStatus>(
|
||||||
|
|
||||||
ImdnStatusList::~ImdnStatusList() {
|
ImdnStatusList::~ImdnStatusList() {
|
||||||
mustBeInMainThread("~" + getClassName());
|
mustBeInMainThread("~" + getClassName());
|
||||||
|
mList.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<ImdnStatus> ImdnStatusList::getImdnStatusList() {
|
QList<ImdnStatus> ImdnStatusList::getImdnStatusList() {
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ signals:
|
||||||
void imdnStatusListChanged();
|
void imdnStatusListChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<ImdnStatus> mImdnStatuss;
|
|
||||||
DECLARE_ABSTRACT_OBJECT
|
DECLARE_ABSTRACT_OBJECT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -551,6 +551,10 @@ void ConferenceInfoCore::writeIntoModel(std::shared_ptr<ConferenceInfoModel> mod
|
||||||
model->setParticipantInfos(participantInfos);
|
model->setParticipantInfos(participantInfos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<ConferenceInfoModel> ConferenceInfoCore::getModel() const {
|
||||||
|
return mConferenceInfoModel;
|
||||||
|
}
|
||||||
|
|
||||||
void ConferenceInfoCore::save() {
|
void ConferenceInfoCore::save() {
|
||||||
mustBeInMainThread(getClassName() + "::save()");
|
mustBeInMainThread(getClassName() + "::save()");
|
||||||
ConferenceInfoCore *thisCopy = new ConferenceInfoCore(*this); // Pointer to avoid multiple copies in lambdas
|
ConferenceInfoCore *thisCopy = new ConferenceInfoCore(*this); // Pointer to avoid multiple copies in lambdas
|
||||||
|
|
@ -571,8 +575,8 @@ void ConferenceInfoCore::save() {
|
||||||
linphone::RegistrationState::Ok) {
|
linphone::RegistrationState::Ok) {
|
||||||
//: "Erreur"
|
//: "Erreur"
|
||||||
Utils::showInformationPopup(tr("information_popup_error_title"),
|
Utils::showInformationPopup(tr("information_popup_error_title"),
|
||||||
//: "Votre compte est déconnecté"
|
//: "Votre compte est déconnecté"
|
||||||
tr("information_popup_disconnected_account_message"), false);
|
tr("information_popup_disconnected_account_message"), false);
|
||||||
emit saveFailed();
|
emit saveFailed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,8 @@ public:
|
||||||
void writeFromModel(const std::shared_ptr<ConferenceInfoModel> &model);
|
void writeFromModel(const std::shared_ptr<ConferenceInfoModel> &model);
|
||||||
void writeIntoModel(std::shared_ptr<ConferenceInfoModel> model);
|
void writeIntoModel(std::shared_ptr<ConferenceInfoModel> model);
|
||||||
|
|
||||||
|
std::shared_ptr<ConferenceInfoModel> getModel() const;
|
||||||
|
|
||||||
Q_INVOKABLE void save();
|
Q_INVOKABLE void save();
|
||||||
Q_INVOKABLE void undo();
|
Q_INVOKABLE void undo();
|
||||||
Q_INVOKABLE void cancelCreation();
|
Q_INVOKABLE void cancelCreation();
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,13 @@ ParticipantCore::ParticipantCore(const std::shared_ptr<linphone::Participant> &p
|
||||||
mParticipantModel->moveToThread(CoreModel::getInstance()->thread());
|
mParticipantModel->moveToThread(CoreModel::getInstance()->thread());
|
||||||
if (participant) {
|
if (participant) {
|
||||||
mAdminStatus = participant->isAdmin();
|
mAdminStatus = participant->isAdmin();
|
||||||
mSipAddress = Utils::coreStringToAppString(participant->getAddress()->asStringUriOnly());
|
auto participantAddress = participant->getAddress();
|
||||||
|
mUsername = Utils::coreStringToAppString(participantAddress->getUsername());
|
||||||
|
mSipAddress = Utils::coreStringToAppString(participantAddress->asStringUriOnly());
|
||||||
mIsMe = ToolModel::isMe(mSipAddress);
|
mIsMe = ToolModel::isMe(mSipAddress);
|
||||||
mCreationTime = QDateTime::fromSecsSinceEpoch(participant->getCreationTime());
|
mCreationTime = QDateTime::fromSecsSinceEpoch(participant->getCreationTime());
|
||||||
mDisplayName = Utils::coreStringToAppString(participant->getAddress()->getDisplayName());
|
mDisplayName = Utils::coreStringToAppString(participantAddress->getDisplayName());
|
||||||
if (mDisplayName.isEmpty()) mDisplayName = ToolModel::getDisplayName(participant->getAddress()->clone());
|
if (mDisplayName.isEmpty()) mDisplayName = ToolModel::getDisplayName(participantAddress->clone());
|
||||||
for (auto &device : participant->getDevices()) {
|
for (auto &device : participant->getDevices()) {
|
||||||
auto name = Utils::coreStringToAppString(device->getName());
|
auto name = Utils::coreStringToAppString(device->getName());
|
||||||
auto address = Utils::coreStringToAppString(device->getAddress()->asStringUriOnly());
|
auto address = Utils::coreStringToAppString(device->getAddress()->asStringUriOnly());
|
||||||
|
|
@ -120,6 +122,17 @@ QString ParticipantCore::getDisplayName() const {
|
||||||
return mDisplayName;
|
return mDisplayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ParticipantCore::setUsername(const QString &name) {
|
||||||
|
if (mUsername != name) {
|
||||||
|
mUsername = name;
|
||||||
|
emit usernameChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ParticipantCore::getUsername() const {
|
||||||
|
return mUsername;
|
||||||
|
}
|
||||||
|
|
||||||
QDateTime ParticipantCore::getCreationTime() const {
|
QDateTime ParticipantCore::getCreationTime() const {
|
||||||
return mCreationTime;
|
return mCreationTime;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ class ParticipantCore : public QObject, public AbstractObject {
|
||||||
|
|
||||||
Q_PROPERTY(QString sipAddress READ getSipAddress WRITE setSipAddress NOTIFY sipAddressChanged)
|
Q_PROPERTY(QString sipAddress READ getSipAddress WRITE setSipAddress NOTIFY sipAddressChanged)
|
||||||
Q_PROPERTY(QString displayName READ getDisplayName WRITE setDisplayName NOTIFY displayNameChanged)
|
Q_PROPERTY(QString displayName READ getDisplayName WRITE setDisplayName NOTIFY displayNameChanged)
|
||||||
|
Q_PROPERTY(QString username READ getUsername WRITE setUsername NOTIFY usernameChanged)
|
||||||
Q_PROPERTY(bool isAdmin READ isAdmin WRITE setIsAdmin NOTIFY isAdminChanged)
|
Q_PROPERTY(bool isAdmin READ isAdmin WRITE setIsAdmin NOTIFY isAdminChanged)
|
||||||
Q_PROPERTY(bool isMe READ isMe NOTIFY isMeChanged)
|
Q_PROPERTY(bool isMe READ isMe NOTIFY isMeChanged)
|
||||||
Q_PROPERTY(QDateTime creationTime READ getCreationTime CONSTANT)
|
Q_PROPERTY(QDateTime creationTime READ getCreationTime CONSTANT)
|
||||||
|
|
@ -56,6 +57,7 @@ public:
|
||||||
void setSelf(QSharedPointer<ParticipantCore> me);
|
void setSelf(QSharedPointer<ParticipantCore> me);
|
||||||
|
|
||||||
QString getDisplayName() const;
|
QString getDisplayName() const;
|
||||||
|
QString getUsername() const;
|
||||||
QString getSipAddress() const;
|
QString getSipAddress() const;
|
||||||
QDateTime getCreationTime() const;
|
QDateTime getCreationTime() const;
|
||||||
bool isAdmin() const;
|
bool isAdmin() const;
|
||||||
|
|
@ -69,6 +71,7 @@ public:
|
||||||
|
|
||||||
void setSipAddress(const QString &address);
|
void setSipAddress(const QString &address);
|
||||||
void setDisplayName(const QString &name);
|
void setDisplayName(const QString &name);
|
||||||
|
void setUsername(const QString &name);
|
||||||
void setCreationTime(const QDateTime &date);
|
void setCreationTime(const QDateTime &date);
|
||||||
void setIsAdmin(const bool &status);
|
void setIsAdmin(const bool &status);
|
||||||
void setIsFocus(const bool &focus);
|
void setIsFocus(const bool &focus);
|
||||||
|
|
@ -92,6 +95,7 @@ signals:
|
||||||
void invitingChanged();
|
void invitingChanged();
|
||||||
void creationTimeChanged();
|
void creationTimeChanged();
|
||||||
void displayNameChanged();
|
void displayNameChanged();
|
||||||
|
void usernameChanged();
|
||||||
|
|
||||||
void lStartInvitation(const int &secs = 30);
|
void lStartInvitation(const int &secs = 30);
|
||||||
void lSetIsAdmin(bool status);
|
void lSetIsAdmin(bool status);
|
||||||
|
|
@ -107,6 +111,7 @@ private:
|
||||||
QList<QVariant> mParticipantDevices;
|
QList<QVariant> mParticipantDevices;
|
||||||
|
|
||||||
QString mDisplayName;
|
QString mDisplayName;
|
||||||
|
QString mUsername;
|
||||||
QString mSipAddress;
|
QString mSipAddress;
|
||||||
QDateTime mCreationTime;
|
QDateTime mCreationTime;
|
||||||
bool mAdminStatus;
|
bool mAdminStatus;
|
||||||
|
|
|
||||||
74
Linphone/core/participant/ParticipantInfoList.cpp
Normal file
74
Linphone/core/participant/ParticipantInfoList.cpp
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ParticipantInfoList.hpp"
|
||||||
|
#include "core/App.hpp"
|
||||||
|
#include "core/chat/ChatCore.hpp"
|
||||||
|
#include "core/participant/ParticipantGui.hpp"
|
||||||
|
#include "tool/Utils.hpp"
|
||||||
|
|
||||||
|
DEFINE_ABSTRACT_OBJECT(ParticipantInfoList)
|
||||||
|
|
||||||
|
QSharedPointer<ParticipantInfoList> ParticipantInfoList::create() {
|
||||||
|
auto model = QSharedPointer<ParticipantInfoList>(new ParticipantInfoList(), &QObject::deleteLater);
|
||||||
|
model->moveToThread(App::getInstance()->thread());
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<ParticipantInfoList> ParticipantInfoList::create(const QSharedPointer<ChatCore> &chatCore) {
|
||||||
|
auto model = create();
|
||||||
|
model->setChatCore(chatCore);
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticipantInfoList::ParticipantInfoList(QObject *parent) : ListProxy(parent) {
|
||||||
|
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticipantInfoList::~ParticipantInfoList() {
|
||||||
|
mList.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticipantInfoList::setChatCore(const QSharedPointer<ChatCore> &chatCore) {
|
||||||
|
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||||
|
if (mChatCore) disconnect(mChatCore.get());
|
||||||
|
mChatCore = chatCore;
|
||||||
|
lDebug() << "[ParticipantInfoList] : set Chat " << mChatCore.get();
|
||||||
|
clearData();
|
||||||
|
if (mChatCore) {
|
||||||
|
auto buildList = [this] {
|
||||||
|
QStringList participantAddresses;
|
||||||
|
QList<QSharedPointer<ParticipantGui>> participantList;
|
||||||
|
auto participants = mChatCore->getParticipants();
|
||||||
|
resetData<ParticipantCore>(participants);
|
||||||
|
};
|
||||||
|
connect(mChatCore.get(), &ChatCore::participantsChanged, this, buildList);
|
||||||
|
buildList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ParticipantInfoList::data(const QModelIndex &index, int role) const {
|
||||||
|
int row = index.row();
|
||||||
|
if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant();
|
||||||
|
if (role == Qt::DisplayRole) {
|
||||||
|
return QVariant::fromValue(new ParticipantGui(mList[row].objectCast<ParticipantCore>()));
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
51
Linphone/core/participant/ParticipantInfoList.hpp
Normal file
51
Linphone/core/participant/ParticipantInfoList.hpp
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PARTICIPANT_INFO_LIST_H_
|
||||||
|
#define PARTICIPANT_INFO_LIST_H_
|
||||||
|
|
||||||
|
#include "../proxy/ListProxy.hpp"
|
||||||
|
#include "model/chat/ChatModel.hpp"
|
||||||
|
#include "tool/thread/SafeConnection.hpp"
|
||||||
|
|
||||||
|
class ChatCore;
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class ParticipantInfoList : public ListProxy, public AbstractObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
static QSharedPointer<ParticipantInfoList> create();
|
||||||
|
static QSharedPointer<ParticipantInfoList> create(const QSharedPointer<ChatCore> &chatCore);
|
||||||
|
|
||||||
|
ParticipantInfoList(QObject *parent = Q_NULLPTR);
|
||||||
|
virtual ~ParticipantInfoList();
|
||||||
|
|
||||||
|
void setChatCore(const QSharedPointer<ChatCore> &chatCore);
|
||||||
|
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void lUpdateParticipants();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<ChatCore> mChatCore;
|
||||||
|
DECLARE_ABSTRACT_OBJECT
|
||||||
|
};
|
||||||
|
#endif // PARTICIPANT_INFO_LIST_H_
|
||||||
63
Linphone/core/participant/ParticipantInfoProxy.cpp
Normal file
63
Linphone/core/participant/ParticipantInfoProxy.cpp
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ParticipantInfoProxy.hpp"
|
||||||
|
#include "ParticipantInfoList.hpp"
|
||||||
|
|
||||||
|
#include "core/chat/ChatCore.hpp"
|
||||||
|
#include "model/core/CoreModel.hpp"
|
||||||
|
#include "tool/Utils.hpp"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
DEFINE_ABSTRACT_OBJECT(ParticipantInfoProxy)
|
||||||
|
|
||||||
|
ParticipantInfoProxy::ParticipantInfoProxy(QObject *parent) : LimitProxy(parent) {
|
||||||
|
mParticipants = ParticipantInfoList::create();
|
||||||
|
setSourceModels(new SortFilterList(mParticipants.get(), Qt::AscendingOrder));
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticipantInfoProxy::~ParticipantInfoProxy() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatGui *ParticipantInfoProxy::getChat() const {
|
||||||
|
return mChat;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticipantInfoProxy::setChat(ChatGui *chat) {
|
||||||
|
lDebug() << "[ParticipantInfoProxy] set current chat " << this << " => " << chat;
|
||||||
|
if (mChat != chat) {
|
||||||
|
mChat = chat;
|
||||||
|
mParticipants->setChatCore(chat ? chat->mCore : nullptr);
|
||||||
|
emit chatChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool ParticipantInfoProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParticipantInfoProxy::SortFilterList::lessThan(const QModelIndex &left, const QModelIndex &right) const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
59
Linphone/core/participant/ParticipantInfoProxy.hpp
Normal file
59
Linphone/core/participant/ParticipantInfoProxy.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PARTICIPANT_INFO_PROXY_H_
|
||||||
|
#define PARTICIPANT_INFO_PROXY_H_
|
||||||
|
|
||||||
|
#include "../proxy/LimitProxy.hpp"
|
||||||
|
#include "core/chat/ChatGui.hpp"
|
||||||
|
#include "tool/AbstractObject.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class ParticipantInfoList;
|
||||||
|
class ChatModel;
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
class QWindow;
|
||||||
|
|
||||||
|
class ParticipantInfoProxy : public LimitProxy, public AbstractObject {
|
||||||
|
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(ChatGui *chat READ getChat WRITE setChat NOTIFY chatChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
DECLARE_SORTFILTER_CLASS(bool mShowMe;)
|
||||||
|
|
||||||
|
ParticipantInfoProxy(QObject *parent = Q_NULLPTR);
|
||||||
|
~ParticipantInfoProxy();
|
||||||
|
|
||||||
|
ChatGui *getChat() const;
|
||||||
|
void setChat(ChatGui *chatGui);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void chatChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ChatGui *mChat = nullptr;
|
||||||
|
QSharedPointer<ParticipantInfoList> mParticipants;
|
||||||
|
DECLARE_ABSTRACT_OBJECT
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PARTICIPANT_INFO_PROXY_H_
|
||||||
|
|
@ -25,7 +25,6 @@
|
||||||
#include "model/core/CoreModel.hpp"
|
#include "model/core/CoreModel.hpp"
|
||||||
#include "tool/Utils.hpp"
|
#include "tool/Utils.hpp"
|
||||||
|
|
||||||
#include "ParticipantList.hpp"
|
|
||||||
#include "core/participant/ParticipantCore.hpp"
|
#include "core/participant/ParticipantCore.hpp"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
|
||||||
|
|
@ -115,24 +115,8 @@ linphone::ChatMessage::State ChatMessageModel::getState() const {
|
||||||
return mMonitor->getState();
|
return mMonitor->getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatMessageModel::computeDeliveryStatus() {
|
|
||||||
// Read
|
|
||||||
for (auto &participant : mMonitor->getParticipantsByImdnState(linphone::ChatMessage::State::Displayed)) {
|
|
||||||
}
|
|
||||||
// Received
|
|
||||||
for (auto &participant : mMonitor->getParticipantsByImdnState(linphone::ChatMessage::State::DeliveredToUser)) {
|
|
||||||
}
|
|
||||||
// Sent
|
|
||||||
for (auto &participant : mMonitor->getParticipantsByImdnState(linphone::ChatMessage::State::Delivered)) {
|
|
||||||
}
|
|
||||||
// Error
|
|
||||||
for (auto &participant : mMonitor->getParticipantsByImdnState(linphone::ChatMessage::State::NotDelivered)) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatMessageModel::onMsgStateChanged(const std::shared_ptr<linphone::ChatMessage> &message,
|
void ChatMessageModel::onMsgStateChanged(const std::shared_ptr<linphone::ChatMessage> &message,
|
||||||
linphone::ChatMessage::State state) {
|
linphone::ChatMessage::State state) {
|
||||||
computeDeliveryStatus();
|
|
||||||
emit msgStateChanged(message, state);
|
emit msgStateChanged(message, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,7 +168,6 @@ void ChatMessageModel::onFileTransferProgressIndication(const std::shared_ptr<li
|
||||||
void ChatMessageModel::onParticipantImdnStateChanged(
|
void ChatMessageModel::onParticipantImdnStateChanged(
|
||||||
const std::shared_ptr<linphone::ChatMessage> &message,
|
const std::shared_ptr<linphone::ChatMessage> &message,
|
||||||
const std::shared_ptr<const linphone::ParticipantImdnState> &state) {
|
const std::shared_ptr<const linphone::ParticipantImdnState> &state) {
|
||||||
computeDeliveryStatus();
|
|
||||||
emit participantImdnStateChanged(message, state);
|
emit participantImdnStateChanged(message, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,6 @@ public:
|
||||||
|
|
||||||
void deleteMessageFromChatRoom();
|
void deleteMessageFromChatRoom();
|
||||||
|
|
||||||
void computeDeliveryStatus();
|
|
||||||
|
|
||||||
void sendReaction(const QString &reaction);
|
void sendReaction(const QString &reaction);
|
||||||
|
|
||||||
void removeReaction();
|
void removeReaction();
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,10 @@ QVector<QPair<bool, QString>> UriTools::parseUri(const QString &text) {
|
||||||
return parse(text, gUriTools.mUriRegularExpression);
|
return parse(text, gUriTools.mUriRegularExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<QPair<bool, QString>> UriTools::parseMention(const QString &text) {
|
||||||
|
return parse(text, gUriTools.mMentionRegularExpression);
|
||||||
|
}
|
||||||
|
|
||||||
// Parse a text and return all lines where regex is matched or not
|
// Parse a text and return all lines where regex is matched or not
|
||||||
QVector<QPair<bool, QString>> UriTools::parse(const QString &text, const QRegularExpression regex) {
|
QVector<QPair<bool, QString>> UriTools::parse(const QString &text, const QRegularExpression regex) {
|
||||||
QVector<QPair<bool, QString>> results;
|
QVector<QPair<bool, QString>> results;
|
||||||
|
|
@ -200,4 +204,6 @@ void UriTools::initRegularExpressions() {
|
||||||
QRegularExpression::UseUnicodePropertiesOption);
|
QRegularExpression::UseUnicodePropertiesOption);
|
||||||
mUriRegularExpression = QRegularExpression(URI, QRegularExpression::CaseInsensitiveOption |
|
mUriRegularExpression = QRegularExpression(URI, QRegularExpression::CaseInsensitiveOption |
|
||||||
QRegularExpression::UseUnicodePropertiesOption);
|
QRegularExpression::UseUnicodePropertiesOption);
|
||||||
|
mMentionRegularExpression = QRegularExpression(
|
||||||
|
"@[A-Za-z0-9.-_]+", QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption);
|
||||||
}
|
}
|
||||||
|
|
@ -41,14 +41,16 @@ public:
|
||||||
|
|
||||||
static QVector<QPair<bool, QString>> parseIri(const QString &text);
|
static QVector<QPair<bool, QString>> parseIri(const QString &text);
|
||||||
static QVector<QPair<bool, QString>> parseUri(const QString &text);
|
static QVector<QPair<bool, QString>> parseUri(const QString &text);
|
||||||
|
static QVector<QPair<bool, QString>> parseMention(const QString &text);
|
||||||
static QRegularExpression getRegularExpression();
|
static QRegularExpression getRegularExpression();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initRegularExpressions();
|
void initRegularExpressions();
|
||||||
static QVector<QPair<bool, QString>> parse(const QString &text, const QRegularExpression regex);
|
static QVector<QPair<bool, QString>> parse(const QString &text, const QRegularExpression regex);
|
||||||
|
|
||||||
QRegularExpression mIriRegularExpression; // https://tools.ietf.org/html/rfc3987
|
QRegularExpression mIriRegularExpression; // https://tools.ietf.org/html/rfc3987
|
||||||
QRegularExpression mUriRegularExpression; // https://tools.ietf.org/html/rfc3986
|
QRegularExpression mUriRegularExpression; // https://tools.ietf.org/html/rfc3986
|
||||||
|
QRegularExpression mMentionRegularExpression; // https://tools.ietf.org/html/rfc3986
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1798,64 +1798,111 @@ QString Utils::getPresenceStatus(LinphoneEnums::Presence presence) {
|
||||||
return presenceStatus;
|
return presenceStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Utils::encodeTextToQmlRichFormat(const QString &text, const QVariantMap &options) {
|
VariantObject *Utils::encodeTextToQmlRichFormat(const QString &text, const QVariantMap &options, ChatGui *chat) {
|
||||||
/*QString images;
|
/*QString images;
|
||||||
QStringList imageFormat;
|
QStringList imageFormat;
|
||||||
for(auto format : QImageReader::supportedImageFormats())
|
for(auto format : QImageReader::supportedImageFormats())
|
||||||
imageFormat.append(QString::fromLatin1(format).toUpper());
|
imageFormat.append(QString::fromLatin1(format).toUpper());
|
||||||
*/
|
*/
|
||||||
QStringList formattedText;
|
VariantObject *data = new VariantObject("encodeTextToQmlRichFormat");
|
||||||
bool lastWasUrl = false;
|
if (!data) return nullptr;
|
||||||
|
auto primaryColor = getDefaultStyleColor("info_500_main");
|
||||||
|
data->makeRequest([text, options, chat, primaryColor] {
|
||||||
|
QStringList formattedText;
|
||||||
|
bool lastWasUrl = false;
|
||||||
|
|
||||||
if (options.contains("noLink") && options["noLink"].toBool()) {
|
if (options.contains("noLink") && options["noLink"].toBool()) {
|
||||||
formattedText.append(encodeEmojiToQmlRichFormat(text));
|
formattedText.append(encodeEmojiToQmlRichFormat(text));
|
||||||
} else {
|
} else {
|
||||||
auto primaryColor = getDefaultStyleColor("info_500_main");
|
|
||||||
auto iriParsed = UriTools::parseIri(text);
|
|
||||||
|
|
||||||
for (int i = 0; i < iriParsed.size(); ++i) {
|
auto iriParsed = UriTools::parseIri(text);
|
||||||
QString iri = iriParsed[i]
|
|
||||||
.second.replace('&', "&")
|
for (int i = 0; i < iriParsed.size(); ++i) {
|
||||||
.replace('<', "\u2063<")
|
QString iri = iriParsed[i]
|
||||||
.replace('>', "\u2063>")
|
.second.replace('&', "&")
|
||||||
.replace('"', """)
|
.replace('<', "\u2063<")
|
||||||
.replace('\'', "'");
|
.replace('>', "\u2063>")
|
||||||
if (!iriParsed[i].first) {
|
.replace('"', """)
|
||||||
if (lastWasUrl) {
|
.replace('\'', "'");
|
||||||
lastWasUrl = false;
|
if (!iriParsed[i].first) {
|
||||||
if (iri.front() != ' ') iri.push_front(' ');
|
if (lastWasUrl) {
|
||||||
|
lastWasUrl = false;
|
||||||
|
if (iri.front() != ' ') iri.push_front(' ');
|
||||||
|
}
|
||||||
|
formattedText.append(encodeEmojiToQmlRichFormat(iri));
|
||||||
|
} else {
|
||||||
|
QString uri =
|
||||||
|
iriParsed[i].second.left(3) == "www" ? "http://" + iriParsed[i].second : iriParsed[i].second;
|
||||||
|
/* TODO : preview from link
|
||||||
|
int extIndex = iriParsed[i].second.lastIndexOf('.');
|
||||||
|
QString ext;
|
||||||
|
if( extIndex >= 0)
|
||||||
|
ext = iriParsed[i].second.mid(extIndex+1).toUpper();
|
||||||
|
if(imageFormat.contains(ext.toLatin1())){// imagesHeight is not used because of bugs on display
|
||||||
|
(blank image if set without width) images += "<a href=\"" + uri + "\"><img" + (
|
||||||
|
options.contains("imagesWidth") ? QString(" width='") + options["imagesWidth"].toString() + "'" : ""
|
||||||
|
) + (
|
||||||
|
options.contains("imagesWidth")
|
||||||
|
? QString(" height='auto'")
|
||||||
|
: ""
|
||||||
|
) + " src=\"" + iriParsed[i].second + "\" />"+uri+"</a>";
|
||||||
|
}else{
|
||||||
|
*/
|
||||||
|
formattedText.append("<a style=\"color:" + primaryColor.name() + ";\" href=\"" + uri + "\">" + iri +
|
||||||
|
"</a>");
|
||||||
|
lastWasUrl = true;
|
||||||
|
/*}*/
|
||||||
}
|
}
|
||||||
formattedText.append(encodeEmojiToQmlRichFormat(iri));
|
|
||||||
} else {
|
|
||||||
QString uri =
|
|
||||||
iriParsed[i].second.left(3) == "www" ? "http://" + iriParsed[i].second : iriParsed[i].second;
|
|
||||||
/* TODO : preview from link
|
|
||||||
int extIndex = iriParsed[i].second.lastIndexOf('.');
|
|
||||||
QString ext;
|
|
||||||
if( extIndex >= 0)
|
|
||||||
ext = iriParsed[i].second.mid(extIndex+1).toUpper();
|
|
||||||
if(imageFormat.contains(ext.toLatin1())){// imagesHeight is not used because of bugs on display (blank
|
|
||||||
image if set without width) images += "<a href=\"" + uri + "\"><img" + ( options.contains("imagesWidth")
|
|
||||||
? QString(" width='") + options["imagesWidth"].toString() + "'"
|
|
||||||
: ""
|
|
||||||
) + (
|
|
||||||
options.contains("imagesWidth")
|
|
||||||
? QString(" height='auto'")
|
|
||||||
: ""
|
|
||||||
) + " src=\"" + iriParsed[i].second + "\" />"+uri+"</a>";
|
|
||||||
}else{
|
|
||||||
*/
|
|
||||||
formattedText.append("<a style=\"color:" + primaryColor.name() + ";\" href=\"" + uri + "\">" + iri +
|
|
||||||
"</a>");
|
|
||||||
lastWasUrl = true;
|
|
||||||
/*}*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (lastWasUrl && formattedText.last().back() != ' ') {
|
||||||
if (lastWasUrl && formattedText.last().back() != ' ') {
|
formattedText.push_back(" ");
|
||||||
formattedText.push_back(" ");
|
}
|
||||||
}
|
if (chat && chat->mCore) {
|
||||||
return "<p style=\"white-space:pre-wrap;\">" + formattedText.join("");
|
auto participants = chat->mCore->getParticipants();
|
||||||
|
auto mentionsParsed = UriTools::parseMention(formattedText.join(""));
|
||||||
|
formattedText.clear();
|
||||||
|
|
||||||
|
for (int i = 0; i < mentionsParsed.size(); ++i) {
|
||||||
|
QString mention = mentionsParsed[i].second;
|
||||||
|
|
||||||
|
if (mentionsParsed[i].first) {
|
||||||
|
QString mentions = mentionsParsed[i].second;
|
||||||
|
QStringList finalMentions;
|
||||||
|
QStringList parts = mentions.split(" ");
|
||||||
|
for (auto part : parts) {
|
||||||
|
if (part.startsWith("@")) { // mention
|
||||||
|
QString username = part;
|
||||||
|
username.removeFirst();
|
||||||
|
auto it = std::find_if(
|
||||||
|
participants.begin(), participants.end(),
|
||||||
|
[username](QSharedPointer<ParticipantCore> p) { return username == p->getUsername(); });
|
||||||
|
if (it != participants.end()) {
|
||||||
|
auto foundParticipant = participants.at(std::distance(participants.begin(), it));
|
||||||
|
auto address = foundParticipant->getSipAddress();
|
||||||
|
auto isFriend = ToolModel::findFriendByAddress(address);
|
||||||
|
if (isFriend)
|
||||||
|
part = "@" + Utils::coreStringToAppString(isFriend->getAddress()->getDisplayName());
|
||||||
|
QString participantLink = "<a style=\"color:" + primaryColor.name() +
|
||||||
|
";\" href=\"mention:" + address + "\">" + part + "</a>";
|
||||||
|
finalMentions.append(participantLink);
|
||||||
|
} else {
|
||||||
|
finalMentions.append(part);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
finalMentions.append(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
formattedText.push_back(finalMentions.join(" "));
|
||||||
|
} else {
|
||||||
|
formattedText.push_back(mentionsParsed[i].second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "<p style=\"white-space:pre-wrap;\">" + formattedText.join("");
|
||||||
|
});
|
||||||
|
data->requestValue();
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Utils::encodeEmojiToQmlRichFormat(const QString &body) {
|
QString Utils::encodeEmojiToQmlRichFormat(const QString &body) {
|
||||||
|
|
@ -1896,6 +1943,24 @@ bool Utils::isOnlyEmojis(const QString &text) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Utils::openContactAtAddress(const QString &address) {
|
||||||
|
App::postModelAsync([address] {
|
||||||
|
auto isFriend = ToolModel::findFriendByAddress(address);
|
||||||
|
if (isFriend) {
|
||||||
|
App::postCoreAsync([address] {
|
||||||
|
auto window = getMainWindow();
|
||||||
|
QMetaObject::invokeMethod(window, "displayContactPage", Q_ARG(QVariant, address));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
App::postCoreAsync([address] {
|
||||||
|
auto window = getMainWindow();
|
||||||
|
QMetaObject::invokeMethod(window, "displayCreateContactPage", Q_ARG(QVariant, ""),
|
||||||
|
Q_ARG(QVariant, address));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QString Utils::getFilename(QUrl url) {
|
QString Utils::getFilename(QUrl url) {
|
||||||
return url.fileName();
|
return url.fileName();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -154,10 +154,11 @@ public:
|
||||||
Q_INVOKABLE static VariantObject *createGroupChat(QString subject, QStringList participantAddresses);
|
Q_INVOKABLE static VariantObject *createGroupChat(QString subject, QStringList participantAddresses);
|
||||||
Q_INVOKABLE static void openChat(ChatGui *chat);
|
Q_INVOKABLE static void openChat(ChatGui *chat);
|
||||||
Q_INVOKABLE static bool isEmptyMessage(QString message);
|
Q_INVOKABLE static bool isEmptyMessage(QString message);
|
||||||
Q_INVOKABLE static QString encodeTextToQmlRichFormat(const QString &text,
|
Q_INVOKABLE static VariantObject *
|
||||||
const QVariantMap &options = QVariantMap());
|
encodeTextToQmlRichFormat(const QString &text, const QVariantMap &options = QVariantMap(), ChatGui *chat = nullptr);
|
||||||
Q_INVOKABLE static QString encodeEmojiToQmlRichFormat(const QString &body);
|
Q_INVOKABLE static QString encodeEmojiToQmlRichFormat(const QString &body);
|
||||||
Q_INVOKABLE static bool isOnlyEmojis(const QString &text);
|
Q_INVOKABLE static bool isOnlyEmojis(const QString &text);
|
||||||
|
Q_INVOKABLE static void openContactAtAddress(const QString &address);
|
||||||
|
|
||||||
Q_INVOKABLE static QString getFilename(QUrl url);
|
Q_INVOKABLE static QString getFilename(QUrl url);
|
||||||
static bool codepointIsEmoji(uint code);
|
static bool codepointIsEmoji(uint code);
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
|
||||||
view/Control/Display/Contact/Voicemail.qml
|
view/Control/Display/Contact/Voicemail.qml
|
||||||
view/Control/Display/Meeting/MeetingListView.qml
|
view/Control/Display/Meeting/MeetingListView.qml
|
||||||
view/Control/Display/Participant/ParticipantDeviceListView.qml
|
view/Control/Display/Participant/ParticipantDeviceListView.qml
|
||||||
|
view/Control/Display/Participant/ParticipantInfoListView.qml
|
||||||
view/Control/Display/Participant/ParticipantListView.qml
|
view/Control/Display/Participant/ParticipantListView.qml
|
||||||
view/Control/Display/Settings/SettingsMenuItem.qml
|
view/Control/Display/Settings/SettingsMenuItem.qml
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ Control.Control {
|
||||||
property bool isFirstMessage
|
property bool isFirstMessage
|
||||||
|
|
||||||
property ChatMessageGui chatMessage
|
property ChatMessageGui chatMessage
|
||||||
|
property ChatGui chat
|
||||||
property string ownReaction: chatMessage? chatMessage.core.ownReaction : ""
|
property string ownReaction: chatMessage? chatMessage.core.ownReaction : ""
|
||||||
property string fromAddress: chatMessage? chatMessage.core.fromAddress : ""
|
property string fromAddress: chatMessage? chatMessage.core.fromAddress : ""
|
||||||
property bool isRemoteMessage: chatMessage? chatMessage.core.isRemoteMessage : false
|
property bool isRemoteMessage: chatMessage? chatMessage.core.isRemoteMessage : false
|
||||||
|
|
@ -140,7 +141,6 @@ Control.Control {
|
||||||
Layout.preferredWidth: mainItem.isRemoteMessage ? 26 * DefaultStyle.dp : 0
|
Layout.preferredWidth: mainItem.isRemoteMessage ? 26 * DefaultStyle.dp : 0
|
||||||
Layout.preferredHeight: 26 * DefaultStyle.dp
|
Layout.preferredHeight: 26 * DefaultStyle.dp
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
Layout.topMargin: isFirstMessage ? 16 * DefaultStyle.dp : 0
|
|
||||||
_address: chatMessage ? chatMessage.core.fromAddress : ""
|
_address: chatMessage ? chatMessage.core.fromAddress : ""
|
||||||
}
|
}
|
||||||
Item {
|
Item {
|
||||||
|
|
@ -197,6 +197,7 @@ Control.Control {
|
||||||
id: chatBubbleContent
|
id: chatBubbleContent
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
chatGui: mainItem.chat
|
||||||
chatMessageGui: mainItem.chatMessage
|
chatMessageGui: mainItem.chatMessage
|
||||||
onMouseEvent: (event) => {
|
onMouseEvent: (event) => {
|
||||||
mainItem.handleDefaultMouseEvent(event)
|
mainItem.handleDefaultMouseEvent(event)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import Linphone
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: mainItem
|
id: mainItem
|
||||||
property ChatMessageGui chatMessageGui: null
|
property ChatMessageGui chatMessageGui: null
|
||||||
|
property ChatGui chatGui: null
|
||||||
|
|
||||||
signal isFileHoveringChanged(bool isFileHovering)
|
signal isFileHoveringChanged(bool isFileHovering)
|
||||||
signal lastSelectedTextChanged(string selectedText)
|
signal lastSelectedTextChanged(string selectedText)
|
||||||
|
|
@ -88,6 +89,7 @@ ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
// height: implicitHeight
|
// height: implicitHeight
|
||||||
contentGui: modelData
|
contentGui: modelData
|
||||||
|
chatGui: mainItem.chatGui
|
||||||
onLastTextSelectedChanged: mainItem.selectedText = selectedText
|
onLastTextSelectedChanged: mainItem.selectedText = selectedText
|
||||||
// onRightClicked: mainItem.rightClicked()
|
// onRightClicked: mainItem.rightClicked()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -202,13 +202,14 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: UtilsCpp.encodeTextToQmlRichFormat(conferenceInfo.description)
|
property var encodeTextObj: UtilsCpp.encodeTextToQmlRichFormat(conferenceInfo.description)
|
||||||
|
text: encodeTextObj? encodeTextObj.value : ""
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
font: Typography.p4
|
font: Typography.p4
|
||||||
color: DefaultStyle.main2_500main
|
color: DefaultStyle.main2_500main
|
||||||
visible: conferenceInfo.description.length > 0
|
visible: conferenceInfo.description.length > 0
|
||||||
onLinkActivated: {
|
onLinkActivated: (link) => {
|
||||||
if (link.startsWith('sip'))
|
if (link.startsWith('sip'))
|
||||||
UtilsCpp.createCall(link)
|
UtilsCpp.createCall(link)
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,7 @@ ListView {
|
||||||
delegate:
|
delegate:
|
||||||
ChatMessage {
|
ChatMessage {
|
||||||
chatMessage: modelData
|
chatMessage: modelData
|
||||||
|
chat: mainItem.chat
|
||||||
maxWidth: Math.round(mainItem.width * (3/4))
|
maxWidth: Math.round(mainItem.width * (3/4))
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (visible && !modelData.core.isRead) modelData.core.lMarkAsRead()
|
if (visible && !modelData.core.isRead) modelData.core.lMarkAsRead()
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,9 @@ import UtilsCpp
|
||||||
// TODO : into Loader
|
// TODO : into Loader
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
TextEdit {
|
TextEdit {
|
||||||
id: message
|
id: mainItem
|
||||||
property ChatMessageContentGui contentGui
|
property ChatMessageContentGui contentGui
|
||||||
|
property ChatGui chatGui: null
|
||||||
property string lastTextSelected : ''
|
property string lastTextSelected : ''
|
||||||
color: DefaultStyle.main2_700
|
color: DefaultStyle.main2_700
|
||||||
font {
|
font {
|
||||||
|
|
@ -24,15 +25,19 @@ TextEdit {
|
||||||
readOnly: true
|
readOnly: true
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
|
|
||||||
text: visible ? UtilsCpp.encodeTextToQmlRichFormat(contentGui.core.utf8Text)
|
property var encodeTextObj: visible ? UtilsCpp.encodeTextToQmlRichFormat(contentGui.core.utf8Text, {}, mainItem.chatGui)
|
||||||
: ''
|
: ''
|
||||||
|
text: encodeTextObj ? encodeTextObj.value : ""
|
||||||
textFormat: Text.RichText // To supports links and imgs.
|
textFormat: Text.RichText // To supports links and imgs.
|
||||||
wrapMode: TextEdit.Wrap
|
wrapMode: TextEdit.Wrap
|
||||||
|
|
||||||
onLinkActivated: (link) => {
|
onLinkActivated: (link) => {
|
||||||
if (link.startsWith('sip'))
|
if (link.startsWith('sip'))
|
||||||
UtilsCpp.createCall(link)
|
UtilsCpp.createCall(link)
|
||||||
|
else if (link.startsWith('mention:')) {
|
||||||
|
var mentionAddress = link.substring(8) // remove "mention:"
|
||||||
|
UtilsCpp.openContactAtAddress(mentionAddress);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
Qt.openUrlExternally(link)
|
Qt.openUrlExternally(link)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Linphone
|
||||||
|
import UtilsCpp
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: mainItem
|
||||||
|
clip: true
|
||||||
|
spacing: Math.round(5 * DefaultStyle.dp)
|
||||||
|
|
||||||
|
property bool hoverEnabled: true
|
||||||
|
property bool displayNameCapitalization: true
|
||||||
|
|
||||||
|
property ChatGui chatGui
|
||||||
|
height: contentHeight
|
||||||
|
|
||||||
|
signal participantClicked(string username)
|
||||||
|
|
||||||
|
currentIndex: -1
|
||||||
|
|
||||||
|
model: ParticipantInfoProxy {
|
||||||
|
id: participantModel
|
||||||
|
chat: mainItem.chatGui
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
id: participantDelegate
|
||||||
|
height: Math.round(56 * DefaultStyle.dp)
|
||||||
|
width: mainItem.width//mainItem.width
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Math.round(18 * DefaultStyle.dp)
|
||||||
|
anchors.rightMargin: Math.round(18 * DefaultStyle.dp)
|
||||||
|
spacing: Math.round(10 * DefaultStyle.dp)
|
||||||
|
Avatar {
|
||||||
|
Layout.preferredWidth: Math.round(45 * DefaultStyle.dp)
|
||||||
|
Layout.preferredHeight: Math.round(45 * DefaultStyle.dp)
|
||||||
|
_address: modelData.core.sipAddress
|
||||||
|
shadowEnabled: false
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
text: modelData.core.displayName
|
||||||
|
font.pixelSize: Math.round(14 * DefaultStyle.dp)
|
||||||
|
font.capitalization: mainItem.displayNameCapitalization ? Font.Capitalize : Font.MixedCase
|
||||||
|
maximumLineCount: 1
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
Item{Layout.fillWidth: true}
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
id: mousearea
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: mainItem.participantClicked(modelData.core.username)
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: mousearea.containsMouse
|
||||||
|
color: DefaultStyle.main2_200
|
||||||
|
opacity: 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -138,8 +138,13 @@ Control.Control {
|
||||||
width: sendingAreaFlickable.width
|
width: sendingAreaFlickable.width
|
||||||
height: sendingAreaFlickable.height
|
height: sendingAreaFlickable.height
|
||||||
textFormat: TextEdit.AutoText
|
textFormat: TextEdit.AutoText
|
||||||
onTextChanged: mainItem.text = text
|
onTextChanged: {
|
||||||
Component.onCompleted: mainItem.textArea = sendingTextArea
|
mainItem.text = text
|
||||||
|
}
|
||||||
|
Component.onCompleted: {
|
||||||
|
mainItem.textArea = sendingTextArea
|
||||||
|
sendingTextArea.text = mainItem.text
|
||||||
|
}
|
||||||
//: Say something… : placeholder text for sending message text area
|
//: Say something… : placeholder text for sending message text area
|
||||||
placeholderText: qsTr("chat_view_send_area_placeholder_text")
|
placeholderText: qsTr("chat_view_send_area_placeholder_text")
|
||||||
placeholderTextColor: DefaultStyle.main2_400
|
placeholderTextColor: DefaultStyle.main2_400
|
||||||
|
|
@ -160,7 +165,7 @@ Control.Control {
|
||||||
Connections {
|
Connections {
|
||||||
target: mainItem
|
target: mainItem
|
||||||
function onTextChanged() {
|
function onTextChanged() {
|
||||||
if (mainItem.text !== text) text = mainItem.text
|
sendingTextArea.text = mainItem.text
|
||||||
}
|
}
|
||||||
function onSendMessage() {
|
function onSendMessage() {
|
||||||
sendingTextArea.clear()
|
sendingTextArea.clear()
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@ TextEdit {
|
||||||
activeFocusOnTab: true
|
activeFocusOnTab: true
|
||||||
|
|
||||||
property bool displayAsRichText: false
|
property bool displayAsRichText: false
|
||||||
property string richFormatText: UtilsCpp.encodeTextToQmlRichFormat(text)
|
property var encodeTextObj: UtilsCpp.encodeTextToQmlRichFormat(text)
|
||||||
|
property string richFormatText: encodeTextObj && encodeTextObj.value || ""
|
||||||
property color textAreaColor
|
property color textAreaColor
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -30,7 +31,7 @@ TextEdit {
|
||||||
}
|
}
|
||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
richFormatText = UtilsCpp.encodeTextToQmlRichFormat(text)
|
encodeTextObj = UtilsCpp.encodeTextToQmlRichFormat(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,54 @@ RowLayout {
|
||||||
anchors.rightMargin: Math.round(5 * DefaultStyle.dp)
|
anchors.rightMargin: Math.round(5 * DefaultStyle.dp)
|
||||||
policy: Control.ScrollBar.AsNeeded
|
policy: Control.ScrollBar.AsNeeded
|
||||||
}
|
}
|
||||||
|
Control.Control {
|
||||||
|
id: participantListPopup
|
||||||
|
width: parent.width
|
||||||
|
height: Math.min(contentItem.height, Math.round(200 * DefaultStyle.dp))
|
||||||
|
visible: false
|
||||||
|
anchors.bottom: chatMessagesListView.bottom
|
||||||
|
anchors.left: chatMessagesListView.left
|
||||||
|
anchors.right: chatMessagesListView.right
|
||||||
|
|
||||||
|
background: Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
Rectangle {
|
||||||
|
id: participantBg
|
||||||
|
color: DefaultStyle.grey_0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
radius: Math.round(20 * DefaultStyle.dp)
|
||||||
|
height: parent.height
|
||||||
|
}
|
||||||
|
MultiEffect {
|
||||||
|
anchors.fill: participantBg
|
||||||
|
source: participantBg
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowBlur: 0.5
|
||||||
|
shadowColor: DefaultStyle.grey_1000
|
||||||
|
shadowOpacity: 0.3
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: bg
|
||||||
|
color: DefaultStyle.grey_0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
height: parent.height/2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contentItem: ParticipantInfoListView {
|
||||||
|
id: participantInfoList
|
||||||
|
height: contentHeight
|
||||||
|
width: participantListPopup.width
|
||||||
|
chatGui: mainItem.chat
|
||||||
|
onParticipantClicked: (username) => {
|
||||||
|
messageSender.text = messageSender.text + username + " "
|
||||||
|
messageSender.textArea.cursorPosition = messageSender.text.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Control.Control {
|
Control.Control {
|
||||||
id: selectedFilesArea
|
id: selectedFilesArea
|
||||||
|
|
@ -333,14 +381,16 @@ RowLayout {
|
||||||
Control.SplitView.preferredHeight: mainItem.chat.core.isReadOnly ? 0 : Math.round(79 * DefaultStyle.dp)
|
Control.SplitView.preferredHeight: mainItem.chat.core.isReadOnly ? 0 : Math.round(79 * DefaultStyle.dp)
|
||||||
Control.SplitView.minimumHeight: mainItem.chat.core.isReadOnly ? 0 : Math.round(79 * DefaultStyle.dp)
|
Control.SplitView.minimumHeight: mainItem.chat.core.isReadOnly ? 0 : Math.round(79 * DefaultStyle.dp)
|
||||||
chat: mainItem.chat
|
chat: mainItem.chat
|
||||||
Component.onCompleted: {
|
onChatChanged: {
|
||||||
|
if (chat) messageSender.text = mainItem.chat.core.sendingText
|
||||||
if (mainItem.chat) text = mainItem.chat.core.sendingText
|
|
||||||
}
|
}
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
if (text !== "" && mainItem.chat.core.composingName !== "") {
|
if (text !== "" && mainItem.chat.core.composingName !== "") {
|
||||||
mainItem.chat.core.lCompose()
|
mainItem.chat.core.lCompose()
|
||||||
}
|
}
|
||||||
|
var lastChar = text.slice(-1)
|
||||||
|
if (lastChar == "@") participantListPopup.visible = true
|
||||||
|
else participantListPopup.visible = false
|
||||||
mainItem.chat.core.sendingText = text
|
mainItem.chat.core.sendingText = text
|
||||||
}
|
}
|
||||||
onSendMessage: {
|
onSendMessage: {
|
||||||
|
|
@ -359,7 +409,6 @@ RowLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: detailsPanel.visible
|
visible: detailsPanel.visible
|
||||||
|
|
|
||||||
|
|
@ -311,7 +311,7 @@ AbstractMainPage {
|
||||||
FocusScope {
|
FocusScope {
|
||||||
SelectedChatView {
|
SelectedChatView {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
chat: mainItem.selectedChatGui
|
chat: mainItem.selectedChatGui || null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue