1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-08-04 14:55:43 +02:00

qmlregister: improve singleton registration

- better manage QML interop object lifetimes
- allow intellisense to pick up QML registered symbols
- fix for PreviewEngine threading

Change-Id: I416cdede70b155dc34fc3ee94f428ae2128c8950
This commit is contained in:
Andreas Traczyk 2023-11-16 15:27:07 -05:00
parent c8b52262bc
commit 35f850289f
58 changed files with 658 additions and 491 deletions

1
.clang-tidy Normal file
View file

@ -0,0 +1 @@
Checks: '-*,analyzer-cplusplus.NewDeleteLeaks'

View file

@ -22,7 +22,7 @@
#include "appsettingsmanager.h" #include "appsettingsmanager.h"
#include "qtutils.h" #include "qtutils.h"
#include "qmlregister.h" #include "accountlistmodel.h"
#include <QtConcurrent/QtConcurrent> #include <QtConcurrent/QtConcurrent>
@ -33,14 +33,7 @@ AccountAdapter::AccountAdapter(AppSettingsManager* settingsManager,
: QmlAdapterBase(instance, parent) : QmlAdapterBase(instance, parent)
, settingsManager_(settingsManager) , settingsManager_(settingsManager)
, systemTray_(systemTray) , systemTray_(systemTray)
, accountListModel_(new AccountListModel(instance))
, deviceItemListModel_(new DeviceItemListModel(instance, parent))
, moderatorListModel_(new ModeratorListModel(instance, parent))
{ {
QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, accountListModel_.get(), "AccountListModel");
QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, deviceItemListModel_.get(), "DeviceItemListModel");
QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, moderatorListModel_.get(), "ModeratorListModel");
connect(&lrcInstance_->accountModel(), connect(&lrcInstance_->accountModel(),
&AccountModel::accountStatusChanged, &AccountModel::accountStatusChanged,
this, this,
@ -53,8 +46,13 @@ AccountAdapter::AccountAdapter(AppSettingsManager* settingsManager,
connect(systemTray_, connect(systemTray_,
&SystemTray::countChanged, &SystemTray::countChanged,
accountListModel_.get(), qApp->property("AccountListModel").value<AccountListModel*>(),
&AccountListModel::updateNotifications); &AccountListModel::updateNotifications);
// Switch account to the specified index when an account is added.
connect(this, &AccountAdapter::accountAdded, this, [this](const QString&, int index) {
changeAccount(index);
});
} }
AccountModel* AccountModel*

View file

@ -20,21 +20,20 @@
#include "qmladapterbase.h" #include "qmladapterbase.h"
#include "accountlistmodel.h"
#include "deviceitemlistmodel.h"
#include "moderatorlistmodel.h"
#include "systemtray.h" #include "systemtray.h"
#include "lrcinstance.h" #include "lrcinstance.h"
#include "utils.h"
#include <QSettings> #include <QSettings>
#include <QString> #include <QString>
#include <QQmlEngine> // QML registration
#include <QApplication> // QML registration
class AppSettingsManager; class AppSettingsManager;
class AccountAdapter final : public QmlAdapterBase class AccountAdapter final : public QmlAdapterBase
{ {
Q_OBJECT Q_OBJECT
QML_SINGLETON
Q_PROPERTY(lrc::api::AccountModel* model READ getModel NOTIFY modelChanged) Q_PROPERTY(lrc::api::AccountModel* model READ getModel NOTIFY modelChanged)
@ -45,6 +44,13 @@ Q_SIGNALS:
void modelChanged(); void modelChanged();
public: public:
static AccountAdapter* create(QQmlEngine*, QJSEngine*)
{
return new AccountAdapter(qApp->property("AppSettingsManager").value<AppSettingsManager*>(),
qApp->property("SystemTray").value<SystemTray*>(),
qApp->property("LRCInstance").value<LRCInstance*>());
}
explicit AccountAdapter(AppSettingsManager* settingsManager, explicit AccountAdapter(AppSettingsManager* settingsManager,
SystemTray* systemTray, SystemTray* systemTray,
LRCInstance* instance, LRCInstance* instance,
@ -100,9 +106,5 @@ private:
AppSettingsManager* settingsManager_; AppSettingsManager* settingsManager_;
SystemTray* systemTray_; SystemTray* systemTray_;
QScopedPointer<AccountListModel> accountListModel_;
QScopedPointer<DeviceItemListModel> deviceItemListModel_;
QScopedPointer<ModeratorListModel> moderatorListModel_;
}; };
Q_DECLARE_METATYPE(AccountAdapter*) Q_DECLARE_METATYPE(AccountAdapter*)

View file

@ -20,11 +20,8 @@
#include "accountlistmodel.h" #include "accountlistmodel.h"
#include "lrcinstance.h" #include "lrcinstance.h"
#include "utils.h"
#include "api/account.h" #include "api/account.h"
#include "api/contact.h"
#include "api/conversation.h"
#include <QDateTime> #include <QDateTime>

View file

@ -51,11 +51,9 @@ public:
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
// reset the model when there's new account added
Q_INVOKABLE void reset(); Q_INVOKABLE void reset();
void updateNotifications(); void updateNotifications();
protected: private:
using Role = AccountList::Role; using Role = AccountList::Role;
}; };

View file

@ -20,23 +20,31 @@
#include "qmladapterbase.h" #include "qmladapterbase.h"
#include "lrcinstance.h" #include "lrcinstance.h"
#include "qtutils.h"
#include "rendererinformationlistmodel.h"
#include <QObject> #include <QObject>
#include <QVariant> #include <QVariant>
#include <QString> #include <QString>
#include <qtutils.h> #include <QQmlEngine> // QML registration
#include <QApplication> // QML registration
#include "rendererinformationlistmodel.h"
class AvAdapter final : public QmlAdapterBase class AvAdapter final : public QmlAdapterBase
{ {
Q_OBJECT Q_OBJECT
QML_SINGLETON
QML_PROPERTY(bool, muteCamera) QML_PROPERTY(bool, muteCamera)
QML_RO_PROPERTY(QStringList, windowsNames) QML_RO_PROPERTY(QStringList, windowsNames)
QML_RO_PROPERTY(QList<QVariant>, windowsIds) QML_RO_PROPERTY(QList<QVariant>, windowsIds)
QML_RO_PROPERTY(QVariant, renderersInfoList) QML_RO_PROPERTY(QVariant, renderersInfoList)
public: public:
static AvAdapter* create(QQmlEngine*, QJSEngine*)
{
return new AvAdapter(qApp->property("LRCInstance").value<LRCInstance*>());
}
explicit AvAdapter(LRCInstance* instance, QObject* parent = nullptr); explicit AvAdapter(LRCInstance* instance, QObject* parent = nullptr);
~AvAdapter() = default; ~AvAdapter() = default;

View file

@ -20,13 +20,22 @@
#include <QObject> #include <QObject>
#include <QMap> #include <QMap>
#include <QQmlEngine> // QML registration
#include <QApplication> // QML registration
class LRCInstance; class LRCInstance;
class AvatarRegistry : public QObject class AvatarRegistry : public QObject
{ {
Q_OBJECT Q_OBJECT
QML_SINGLETON
public: public:
static AvatarRegistry* create(QQmlEngine*, QJSEngine*)
{
return new AvatarRegistry(qApp->property("LRCInstance").value<LRCInstance*>());
}
explicit AvatarRegistry(LRCInstance* instance, QObject* parent = nullptr); explicit AvatarRegistry(LRCInstance* instance, QObject* parent = nullptr);
~AvatarRegistry() = default; ~AvatarRegistry() = default;

View file

@ -26,8 +26,8 @@
#include "calladapter.h" #include "calladapter.h"
#include "systemtray.h" #include "systemtray.h"
#include "qmlregister.h"
#include "appsettingsmanager.h" #include "appsettingsmanager.h"
#include "pttlistener.h"
#include <api/callmodel.h> #include <api/callmodel.h>
#include <api/callparticipantsmodel.h> #include <api/callparticipantsmodel.h>
@ -45,19 +45,15 @@ CallAdapter::CallAdapter(AppSettingsManager* settingsManager,
: QmlAdapterBase(instance, parent) : QmlAdapterBase(instance, parent)
, systemTray_(systemTray) , systemTray_(systemTray)
, callInformationListModel_(std::make_unique<CallInformationListModel>()) , callInformationListModel_(std::make_unique<CallInformationListModel>())
, listener_(new PTTListener(settingsManager, this))
{ {
// Expose the Push-to-talk listener to QML as a singleton // Get the PTTListener instance.
QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, listener_, "PttListener"); listener_ = qApp->property("PTTListener").value<PTTListener*>();
set_callInformationList(QVariant::fromValue(callInformationListModel_.get())); set_callInformationList(QVariant::fromValue(callInformationListModel_.get()));
timer = new QTimer(this); timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &CallAdapter::updateAdvancedInformation); connect(timer, &QTimer::timeout, this, &CallAdapter::updateAdvancedInformation);
overlayModel_.reset(new CallOverlayModel(lrcInstance_, listener_, this));
QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, overlayModel_.get(), "CallOverlayModel");
accountId_ = lrcInstance_->get_currentAccountId(); accountId_ = lrcInstance_->get_currentAccountId();
connectCallModel(accountId_); connectCallModel(accountId_);

View file

@ -23,32 +23,34 @@
#include "lrcinstance.h" #include "lrcinstance.h"
#include "qmladapterbase.h" #include "qmladapterbase.h"
#include "screensaver.h" #include "screensaver.h"
#include "calloverlaymodel.h" #include "callInformationListModel.h"
#ifdef HAVE_GLOBAL_PTT
#include "pttlistener.h"
#endif
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QVariant> #include <QVariant>
#include <QSystemTrayIcon> #include <QSystemTrayIcon>
#include <QQmlEngine> // QML registration
#include "callInformationListModel.h" #include <QApplication> // QML registration
class SystemTray; class SystemTray;
class AppSettingsManager; class AppSettingsManager;
class PTTListener;
class CallAdapter final : public QmlAdapterBase class CallAdapter final : public QmlAdapterBase
{ {
Q_OBJECT Q_OBJECT
QML_SINGLETON
QML_PROPERTY(bool, hasCall) QML_PROPERTY(bool, hasCall)
QML_RO_PROPERTY(QVariant, callInformationList) QML_RO_PROPERTY(QVariant, callInformationList)
public: public:
QTimer* timer; static CallAdapter* create(QQmlEngine*, QJSEngine*)
enum MuteStates { UNMUTED, LOCAL_MUTED, MODERATOR_MUTED, BOTH_MUTED }; {
Q_ENUM(MuteStates) return new CallAdapter(qApp->property("AppSettingsManager").value<AppSettingsManager*>(),
qApp->property("SystemTray").value<SystemTray*>(),
qApp->property("LRCInstance").value<LRCInstance*>());
}
explicit CallAdapter(AppSettingsManager* settingsManager, explicit CallAdapter(AppSettingsManager* settingsManager,
SystemTray* systemTray, SystemTray* systemTray,
@ -56,7 +58,10 @@ public:
QObject* parent = nullptr); QObject* parent = nullptr);
~CallAdapter(); ~CallAdapter();
public: QTimer* timer;
enum MuteStates { UNMUTED, LOCAL_MUTED, MODERATOR_MUTED, BOTH_MUTED };
Q_ENUM(MuteStates)
Q_INVOKABLE void startTimerInformation(); Q_INVOKABLE void startTimerInformation();
Q_INVOKABLE void stopTimerInformation(); Q_INVOKABLE void stopTimerInformation();
Q_INVOKABLE void placeAudioOnlyCall(); Q_INVOKABLE void placeAudioOnlyCall();
@ -131,7 +136,6 @@ private:
ScreenSaver screenSaver; ScreenSaver screenSaver;
SystemTray* systemTray_; SystemTray* systemTray_;
QScopedPointer<CallOverlayModel> overlayModel_;
VectorString currentConfSubcalls_; VectorString currentConfSubcalls_;
std::unique_ptr<CallInformationListModel> callInformationListModel_; std::unique_ptr<CallInformationListModel> callInformationListModel_;

View file

@ -21,8 +21,6 @@
#include "lrcinstance.h" #include "lrcinstance.h"
#include "qtutils.h" #include "qtutils.h"
#include "mainapplication.h"
#include "pttlistener.h" #include "pttlistener.h"
#include <QAbstractListModel> #include <QAbstractListModel>

View file

@ -37,7 +37,7 @@ BaseModalDialog {
button2Role: DialogButtonBox.RejectRole button2Role: DialogButtonBox.RejectRole
button1.onClicked: { button1.onClicked: {
if (!(pressedKey === Qt.Key_unknown)){ if (!(pressedKey === Qt.Key_unknown)){
PttListener.setPttKey(pressedKey); PTTListener.setPttKey(pressedKey);
choiceMade(pressedKey); choiceMade(pressedKey);
} }
close(); close();
@ -102,7 +102,7 @@ BaseModalDialog {
id: keyItem id: keyItem
Keys.onPressed: (event)=>{ Keys.onPressed: (event)=>{
keyLabel.text = PttListener.keyToString(event.key); keyLabel.text = PTTListener.keyToString(event.key);
pressedKey = event.key; pressedKey = event.key;
} }
} }

View file

@ -126,10 +126,6 @@ ConnectionInfoListModel::roleNames() const
void void
ConnectionInfoListModel::update() ConnectionInfoListModel::update()
{ {
const auto accountId = lrcInstance_->get_currentAccountId();
if (accountId.isEmpty()) {
return;
}
aggregateData(); aggregateData();
} }

View file

@ -41,8 +41,10 @@ enum Role {
Q_ENUM_NS(Role) Q_ENUM_NS(Role)
} // namespace ConnectionInfoList } // namespace ConnectionInfoList
class ConnectionInfoListModel : public AbstractListModelBase class ConnectionInfoListModel final : public AbstractListModelBase
{ {
Q_OBJECT
public: public:
explicit ConnectionInfoListModel(LRCInstance* instance, QObject* parent = nullptr); explicit ConnectionInfoListModel(LRCInstance* instance, QObject* parent = nullptr);
@ -56,7 +58,6 @@ private:
using Role = ConnectionInfoList::Role; using Role = ConnectionInfoList::Role;
VectorMapStringString connectionInfoList_; VectorMapStringString connectionInfoList_;
QVector<QString> peerIds_; QVector<QString> peerIds_;
QMap<QString, QMap<QString, QMap<QString, QVariant>>> peerData_; QMap<QString, QMap<QString, QMap<QString, QVariant>>> peerData_;
void aggregateData(); void aggregateData();

View file

@ -21,16 +21,10 @@
#include "contactadapter.h" #include "contactadapter.h"
#include "lrcinstance.h" #include "lrcinstance.h"
#include "qmlregister.h"
ContactAdapter::ContactAdapter(LRCInstance* instance, QObject* parent) ContactAdapter::ContactAdapter(LRCInstance* instance, QObject* parent)
: QmlAdapterBase(instance, parent) : QmlAdapterBase(instance, parent)
, connectionInfoListModel_(new ConnectionInfoListModel(lrcInstance_, this))
{ {
QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS,
connectionInfoListModel_.get(),
"ConnectionInfoListModel");
selectableProxyModel_.reset(new SelectableProxyModel(this)); selectableProxyModel_.reset(new SelectableProxyModel(this));
if (lrcInstance_) { if (lrcInstance_) {
connect(lrcInstance_, connect(lrcInstance_,
@ -252,12 +246,6 @@ ContactAdapter::removeContact(const QString& peerUri, bool banContact)
accInfo.contactModel->removeContact(peerUri, banContact); accInfo.contactModel->removeContact(peerUri, banContact);
} }
void
ContactAdapter::updateConnectionInfo()
{
connectionInfoListModel_->update();
}
void void
ContactAdapter::connectSignals() ContactAdapter::connectSignals()
{ {

View file

@ -20,12 +20,12 @@
#include "qmladapterbase.h" #include "qmladapterbase.h"
#include "smartlistmodel.h" #include "smartlistmodel.h"
#include "conversationlistmodel.h"
#include "connectioninfolistmodel.h"
#include <QObject> #include <QObject>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QString> #include <QString>
#include <QQmlEngine> // QML registration
#include <QApplication> // QML registration
class LRCInstance; class LRCInstance;
@ -80,8 +80,14 @@ private:
class ContactAdapter final : public QmlAdapterBase class ContactAdapter final : public QmlAdapterBase
{ {
Q_OBJECT Q_OBJECT
QML_SINGLETON
public: public:
static ContactAdapter* create(QQmlEngine*, QJSEngine*)
{
return new ContactAdapter(qApp->property("LRCInstance").value<LRCInstance*>());
}
explicit ContactAdapter(LRCInstance* instance, QObject* parent = nullptr); explicit ContactAdapter(LRCInstance* instance, QObject* parent = nullptr);
~ContactAdapter() = default; ~ContactAdapter() = default;
@ -91,7 +97,6 @@ public:
Q_INVOKABLE void setSearchFilter(const QString& filter); Q_INVOKABLE void setSearchFilter(const QString& filter);
Q_INVOKABLE void contactSelected(int index); Q_INVOKABLE void contactSelected(int index);
Q_INVOKABLE void removeContact(const QString& peerUri, bool banContact); Q_INVOKABLE void removeContact(const QString& peerUri, bool banContact);
Q_INVOKABLE void updateConnectionInfo();
void connectSignals(); void connectSignals();
@ -106,7 +111,6 @@ private:
SmartListModel::Type listModeltype_; SmartListModel::Type listModeltype_;
QScopedPointer<SmartListModel> smartListModel_; QScopedPointer<SmartListModel> smartListModel_;
QScopedPointer<SelectableProxyModel> selectableProxyModel_; QScopedPointer<SelectableProxyModel> selectableProxyModel_;
QScopedPointer<ConnectionInfoListModel> connectionInfoListModel_;
QStringList defaultModerators_; QStringList defaultModerators_;

View file

@ -55,10 +55,15 @@ ConversationListModel::ConversationListModel(LRCInstance* instance, QObject* par
&ConversationListModel::endRemoveRows, &ConversationListModel::endRemoveRows,
Qt::DirectConnection); Qt::DirectConnection);
connect(model_, &ConversationModel::dataChanged, this, [this](int position) { connect(
model_,
&ConversationModel::dataChanged,
this,
[this](int position) {
const auto index = createIndex(position, 0); const auto index = createIndex(position, 0);
Q_EMIT ConversationListModel::dataChanged(index, index); Q_EMIT ConversationListModel::dataChanged(index, index);
}, Qt::QueuedConnection); },
Qt::QueuedConnection);
} }
int int

View file

@ -24,7 +24,9 @@
#include "qmlregister.h" #include "qmlregister.h"
#include "qtutils.h" #include "qtutils.h"
#ifdef Q_OS_LINUX
#include "namedirectory.h" #include "namedirectory.h"
#endif
#include <QApplication> #include <QApplication>
#include <QJsonObject> #include <QJsonObject>
@ -33,18 +35,21 @@ using namespace lrc::api;
ConversationsAdapter::ConversationsAdapter(SystemTray* systemTray, ConversationsAdapter::ConversationsAdapter(SystemTray* systemTray,
LRCInstance* instance, LRCInstance* instance,
ConversationListProxyModel* convProxyModel,
SelectableListProxyModel* searchProxyModel,
QObject* parent) QObject* parent)
: QmlAdapterBase(instance, parent) : QmlAdapterBase(instance, parent)
, systemTray_(systemTray) , systemTray_(systemTray)
, convSrcModel_(new ConversationListModel(lrcInstance_)) , convSrcModel_(new ConversationListModel(lrcInstance_))
, convModel_(new ConversationListProxyModel(convSrcModel_.get())) , convModel_(convProxyModel)
, searchSrcModel_(new SearchResultsListModel(lrcInstance_)) , searchSrcModel_(new SearchResultsListModel(lrcInstance_))
, searchModel_(new SelectableListProxyModel(searchSrcModel_.get())) , searchModel_(searchProxyModel)
{ {
QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, convModel_.get(), "ConversationListModel"); convModel_->bindSourceModel(convSrcModel_.get());
QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, searchModel_.get(), "SearchResultsListModel"); searchModel_->bindSourceModel(searchSrcModel_.get());
new SelectableListProxyGroupModel({convModel_.data(), searchModel_.data()}, this); set_convListProxyModel(QVariant::fromValue(convModel_));
set_searchListProxyModel(QVariant::fromValue(searchModel_));
// this will trigger when the invite filter tab is selected // this will trigger when the invite filter tab is selected
connect(this, &ConversationsAdapter::filterRequestsChanged, [this]() { connect(this, &ConversationsAdapter::filterRequestsChanged, [this]() {
@ -112,32 +117,27 @@ ConversationsAdapter::ConversationsAdapter(SystemTray* systemTray,
connect(&lrcInstance_->behaviorController(), connect(&lrcInstance_->behaviorController(),
&BehaviorController::newUnreadInteraction, &BehaviorController::newUnreadInteraction,
this, this,
&ConversationsAdapter::onNewUnreadInteraction, &ConversationsAdapter::onNewUnreadInteraction);
Qt::UniqueConnection);
connect(&lrcInstance_->behaviorController(), connect(&lrcInstance_->behaviorController(),
&BehaviorController::newReadInteraction, &BehaviorController::newReadInteraction,
this, this,
&ConversationsAdapter::onNewReadInteraction, &ConversationsAdapter::onNewReadInteraction);
Qt::UniqueConnection);
connect(&lrcInstance_->behaviorController(), connect(&lrcInstance_->behaviorController(),
&BehaviorController::newTrustRequest, &BehaviorController::newTrustRequest,
this, this,
&ConversationsAdapter::onNewTrustRequest, &ConversationsAdapter::onNewTrustRequest);
Qt::UniqueConnection);
connect(&lrcInstance_->behaviorController(), connect(&lrcInstance_->behaviorController(),
&BehaviorController::trustRequestTreated, &BehaviorController::trustRequestTreated,
this, this,
&ConversationsAdapter::onTrustRequestTreated, &ConversationsAdapter::onTrustRequestTreated);
Qt::UniqueConnection);
connect(lrcInstance_, connect(lrcInstance_,
&LRCInstance::currentAccountIdChanged, &LRCInstance::currentAccountIdChanged,
this, this,
&ConversationsAdapter::onCurrentAccountIdChanged, &ConversationsAdapter::onCurrentAccountIdChanged);
Qt::UniqueConnection);
connectConversationModel(); connectConversationModel();
} }
@ -168,7 +168,6 @@ ConversationsAdapter::onNewUnreadInteraction(const QString& accountId,
if (interaction.authorUri == accountInfo.profileInfo.uri) if (interaction.authorUri == accountInfo.profileInfo.uri)
return; return;
auto from = accountInfo.contactModel->bestNameForContact(interaction.authorUri); auto from = accountInfo.contactModel->bestNameForContact(interaction.authorUri);
auto to = lrcInstance_->accountModel().bestNameForAccount(accountId);
auto body_ = interaction.body; auto body_ = interaction.body;
if (interaction.type == interaction::Type::DATA_TRANSFER) { if (interaction.type == interaction::Type::DATA_TRANSFER) {
@ -180,6 +179,7 @@ ConversationsAdapter::onNewUnreadInteraction(const QString& accountId,
if (preferences["ignoreNotifications"] == "true") if (preferences["ignoreNotifications"] == "true")
return; return;
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
auto to = lrcInstance_->accountModel().bestNameForAccount(accountId);
auto contactPhoto = Utils::contactPhoto(lrcInstance_, auto contactPhoto = Utils::contactPhoto(lrcInstance_,
interaction.authorUri, interaction.authorUri,
QSize(50, 50), QSize(50, 50),
@ -599,75 +599,49 @@ void
ConversationsAdapter::connectConversationModel() ConversationsAdapter::connectConversationModel()
{ {
// Signal connections // Signal connections
auto currentConversationModel = lrcInstance_->getCurrentConversationModel(); auto model = lrcInstance_->getCurrentConversationModel();
if (currentConversationModel == nullptr) { if (!model) {
return; return;
} }
QObject::connect(currentConversationModel, auto connectObjectSignal = [this](auto obj, auto signal, auto slot) {
connect(obj, signal, this, slot, Qt::UniqueConnection);
};
connectObjectSignal(model,
&ConversationModel::modelChanged, &ConversationModel::modelChanged,
this, &ConversationsAdapter::onModelChanged);
&ConversationsAdapter::onModelChanged, connectObjectSignal(model,
Qt::UniqueConnection); &ConversationModel::profileUpdated,
&ConversationsAdapter::onProfileUpdated);
QObject::connect(lrcInstance_->getCurrentContactModel(), connectObjectSignal(model,
&ContactModel::profileUpdated,
this,
&ConversationsAdapter::onProfileUpdated,
Qt::UniqueConnection);
QObject::connect(currentConversationModel,
&ConversationModel::conversationUpdated, &ConversationModel::conversationUpdated,
this, &ConversationsAdapter::onConversationUpdated);
&ConversationsAdapter::onConversationUpdated, connectObjectSignal(model,
Qt::UniqueConnection);
QObject::connect(currentConversationModel,
&ConversationModel::conversationRemoved, &ConversationModel::conversationRemoved,
this, &ConversationsAdapter::onConversationRemoved);
&ConversationsAdapter::onConversationRemoved, connectObjectSignal(model,
Qt::UniqueConnection);
QObject::connect(currentConversationModel,
&ConversationModel::filterChanged, &ConversationModel::filterChanged,
this, &ConversationsAdapter::onFilterChanged);
&ConversationsAdapter::onFilterChanged, connectObjectSignal(model,
Qt::UniqueConnection);
QObject::connect(currentConversationModel,
&ConversationModel::conversationCleared, &ConversationModel::conversationCleared,
this, &ConversationsAdapter::onConversationCleared);
&ConversationsAdapter::onConversationCleared, connectObjectSignal(model,
Qt::UniqueConnection);
QObject::connect(currentConversationModel,
&ConversationModel::searchStatusChanged, &ConversationModel::searchStatusChanged,
this, &ConversationsAdapter::onSearchStatusChanged);
&ConversationsAdapter::onSearchStatusChanged, connectObjectSignal(model,
Qt::UniqueConnection);
QObject::connect(currentConversationModel,
&ConversationModel::searchResultUpdated, &ConversationModel::searchResultUpdated,
this, &ConversationsAdapter::onSearchResultUpdated);
&ConversationsAdapter::onSearchResultUpdated, connectObjectSignal(model,
Qt::UniqueConnection);
QObject::connect(currentConversationModel,
&ConversationModel::searchResultEnded, &ConversationModel::searchResultEnded,
this, &ConversationsAdapter::onSearchResultEnded);
&ConversationsAdapter::onSearchResultEnded, connectObjectSignal(model,
Qt::UniqueConnection);
QObject::connect(currentConversationModel,
&ConversationModel::conversationReady, &ConversationModel::conversationReady,
this, &ConversationsAdapter::onConversationReady);
&ConversationsAdapter::onConversationReady,
Qt::UniqueConnection);
QObject::connect(lrcInstance_->getCurrentContactModel(), connectObjectSignal(lrcInstance_->getCurrentContactModel(),
&ContactModel::bannedStatusChanged, &ContactModel::bannedStatusChanged,
this, &ConversationsAdapter::onBannedStatusChanged);
&ConversationsAdapter::onBannedStatusChanged,
Qt::UniqueConnection);
convSrcModel_.reset(new ConversationListModel(lrcInstance_)); convSrcModel_.reset(new ConversationListModel(lrcInstance_));
convModel_->bindSourceModel(convSrcModel_.get()); convModel_->bindSourceModel(convSrcModel_.get());

View file

@ -26,19 +26,36 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QQmlEngine> // QML registration
#include <QApplication> // QML registration
class SystemTray; class SystemTray;
class ConversationsAdapter final : public QmlAdapterBase class ConversationsAdapter final : public QmlAdapterBase
{ {
Q_OBJECT Q_OBJECT
QML_SINGLETON
QML_PROPERTY(bool, filterRequests) QML_PROPERTY(bool, filterRequests)
QML_PROPERTY(int, totalUnreadMessageCount) QML_PROPERTY(int, totalUnreadMessageCount)
QML_PROPERTY(int, pendingRequestCount) QML_PROPERTY(int, pendingRequestCount)
QML_RO_PROPERTY(QVariant, convListProxyModel)
QML_RO_PROPERTY(QVariant, searchListProxyModel)
public: public:
static ConversationsAdapter* create(QQmlEngine*, QJSEngine*)
{
return new ConversationsAdapter(
qApp->property("SystemTray").value<SystemTray*>(),
qApp->property("LRCInstance").value<LRCInstance*>(),
qApp->property("ConvListProxyModel").value<ConversationListProxyModel*>(),
qApp->property("ConvSearchListProxyModel").value<SelectableListProxyModel*>());
}
explicit ConversationsAdapter(SystemTray* systemTray, explicit ConversationsAdapter(SystemTray* systemTray,
LRCInstance* instance, LRCInstance* instance,
ConversationListProxyModel* convProxyModel,
SelectableListProxyModel* searchProxyModel,
QObject* parent = nullptr); QObject* parent = nullptr);
~ConversationsAdapter() = default; ~ConversationsAdapter() = default;
@ -107,9 +124,9 @@ private:
SystemTray* systemTray_; SystemTray* systemTray_;
QScopedPointer<ConversationListModel> convSrcModel_; QScopedPointer<ConversationListModel> convSrcModel_;
QScopedPointer<ConversationListProxyModel> convModel_; ConversationListProxyModel* convModel_;
QScopedPointer<SearchResultsListModel> searchSrcModel_; QScopedPointer<SearchResultsListModel> searchSrcModel_;
QScopedPointer<SelectableListProxyModel> searchModel_; SelectableListProxyModel* searchModel_;
std::atomic_bool selectFirst_ {false}; std::atomic_bool selectFirst_ {false};
}; };

View file

@ -24,6 +24,8 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QQmlEngine> // QML registration
#include <QApplication> // QML registration
#define ACCOUNT_CONFIG_SETTINGS_PROPERTY_BASE(type, prop) \ #define ACCOUNT_CONFIG_SETTINGS_PROPERTY_BASE(type, prop) \
PROPERTY_GETTER_BASE(type, prop) \ PROPERTY_GETTER_BASE(type, prop) \
@ -97,7 +99,7 @@ private: \
class CurrentAccount final : public QObject class CurrentAccount final : public QObject
{ {
Q_OBJECT Q_OBJECT
// Basic settings QML_SINGLETON
QML_RO_PROPERTY(QString, id) QML_RO_PROPERTY(QString, id)
QML_RO_PROPERTY(QString, uri) QML_RO_PROPERTY(QString, uri)
@ -194,6 +196,12 @@ class CurrentAccount final : public QObject
QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(QJsonObject, uiCustomization) QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(QJsonObject, uiCustomization)
public: public:
static CurrentAccount* create(QQmlEngine*, QJSEngine*)
{
return new CurrentAccount(qApp->property("LRCInstance").value<LRCInstance*>(),
qApp->property("AppSettingsManager").value<AppSettingsManager*>());
}
explicit CurrentAccount(LRCInstance* lrcInstance, explicit CurrentAccount(LRCInstance* lrcInstance,
AppSettingsManager* settingsManager, AppSettingsManager* settingsManager,
QObject* parent = nullptr); QObject* parent = nullptr);

View file

@ -58,8 +58,6 @@ CurrentAccountToMigrate::CurrentAccountToMigrate(LRCInstance* instance, QObject*
&CurrentAccountToMigrate::slotAccountRemoved); &CurrentAccountToMigrate::slotAccountRemoved);
} }
CurrentAccountToMigrate::~CurrentAccountToMigrate() {}
void void
CurrentAccountToMigrate::removeCurrentAccountToMigrate() CurrentAccountToMigrate::removeCurrentAccountToMigrate()
{ {

View file

@ -19,15 +19,19 @@
#pragma once #pragma once
#include <QObject>
#include "qtutils.h" #include "qtutils.h"
#include <QObject>
#include <QQmlEngine> // QML registration
#include <QApplication> // QML registration
class LRCInstance; class LRCInstance;
class CurrentAccountToMigrate : public QObject class CurrentAccountToMigrate : public QObject
{ {
Q_OBJECT Q_OBJECT
QML_SINGLETON
QML_RO_PROPERTY(int, accountToMigrateListSize) QML_RO_PROPERTY(int, accountToMigrateListSize)
QML_RO_PROPERTY(QString, accountId) QML_RO_PROPERTY(QString, accountId)
QML_RO_PROPERTY(QString, managerUsername) QML_RO_PROPERTY(QString, managerUsername)
@ -36,8 +40,13 @@ class CurrentAccountToMigrate : public QObject
QML_RO_PROPERTY(QString, alias) QML_RO_PROPERTY(QString, alias)
public: public:
static CurrentAccountToMigrate* create(QQmlEngine*, QJSEngine*)
{
return new CurrentAccountToMigrate(qApp->property("LRCInstance").value<LRCInstance*>());
}
explicit CurrentAccountToMigrate(LRCInstance* lrcInstance, QObject* parent = nullptr); explicit CurrentAccountToMigrate(LRCInstance* lrcInstance, QObject* parent = nullptr);
~CurrentAccountToMigrate(); ~CurrentAccountToMigrate() = default;
Q_INVOKABLE void removeCurrentAccountToMigrate(); Q_INVOKABLE void removeCurrentAccountToMigrate();

View file

@ -16,7 +16,8 @@
*/ */
#include "currentcall.h" #include "currentcall.h"
#include "qmlregister.h"
#include "callparticipantsmodel.h"
#include <api/callparticipantsmodel.h> #include <api/callparticipantsmodel.h>
#include <api/devicemodel.h> #include <api/devicemodel.h>
@ -25,8 +26,7 @@ CurrentCall::CurrentCall(LRCInstance* lrcInstance, QObject* parent)
: QObject(parent) : QObject(parent)
, lrcInstance_(lrcInstance) , lrcInstance_(lrcInstance)
{ {
participantsModel_.reset(new CallParticipantsModel(lrcInstance_, this)); participantsModel_ = qApp->property("CallParticipantsModel").value<CallParticipantsModel*>();
QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, participantsModel_.get(), "CallParticipantsModel");
connect(lrcInstance_, connect(lrcInstance_,
&LRCInstance::currentAccountIdChanged, &LRCInstance::currentAccountIdChanged,

View file

@ -19,14 +19,18 @@
#include "lrcinstance.h" #include "lrcinstance.h"
#include "qtutils.h" #include "qtutils.h"
#include "callparticipantsmodel.h"
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QQmlEngine> // QML registration
#include <QApplication> // QML registration
class CallParticipantsModel;
class CurrentCall final : public QObject class CurrentCall final : public QObject
{ {
Q_OBJECT Q_OBJECT
QML_SINGLETON
QML_RO_PROPERTY(QString, id) QML_RO_PROPERTY(QString, id)
QML_RO_PROPERTY(QStringList, uris) QML_RO_PROPERTY(QStringList, uris)
@ -55,6 +59,11 @@ class CurrentCall final : public QObject
QML_PROPERTY(bool, flipSelf) QML_PROPERTY(bool, flipSelf)
public: public:
static CurrentCall* create(QQmlEngine*, QJSEngine*)
{
return new CurrentCall(qApp->property("LRCInstance").value<LRCInstance*>());
}
explicit CurrentCall(LRCInstance* lrcInstance, QObject* parent = nullptr); explicit CurrentCall(LRCInstance* lrcInstance, QObject* parent = nullptr);
~CurrentCall() = default; ~CurrentCall() = default;
Q_INVOKABLE QVariantList getConferencesInfos() const; Q_INVOKABLE QVariantList getConferencesInfos() const;
@ -85,5 +94,5 @@ private Q_SLOTS:
private: private:
LRCInstance* lrcInstance_; LRCInstance* lrcInstance_;
QScopedPointer<CallParticipantsModel> participantsModel_; CallParticipantsModel* participantsModel_;
}; };

View file

@ -24,7 +24,9 @@ CurrentConversation::CurrentConversation(LRCInstance* lrcInstance, QObject* pare
: QObject(parent) : QObject(parent)
, lrcInstance_(lrcInstance) , lrcInstance_(lrcInstance)
{ {
uris_ = new CurrentConversationMembers(lrcInstance, this); membersModel_ = new CurrentConversationMembers(lrcInstance, this);
set_members(QVariant::fromValue(membersModel_));
// whenever the account changes, reconnect the new conversation model // whenever the account changes, reconnect the new conversation model
// for updates to the conversation and call state/id // for updates to the conversation and call state/id
connect(lrcInstance_, connect(lrcInstance_,
@ -55,7 +57,7 @@ CurrentConversation::updateData()
// If the conversation is empty, clear the id and return. // If the conversation is empty, clear the id and return.
if (convId.isEmpty()) { if (convId.isEmpty()) {
set_id(); set_id();
uris_->setMembers({}, {}, {}); membersModel_->setMembers({}, {}, {});
return; return;
} }
@ -95,7 +97,7 @@ CurrentConversation::updateData()
for (const auto& banned : bannedUris) for (const auto& banned : bannedUris)
uris.push_back(banned); uris.push_back(banned);
} }
uris_->setMembers(accountId, convId, uris); membersModel_->setMembers(accountId, convId, uris);
set_isSwarm(convInfo.isSwarm()); set_isSwarm(convInfo.isSwarm());
set_isLegacy(convInfo.isLegacy()); set_isLegacy(convInfo.isLegacy());
set_isCoreDialog(convInfo.isCoreDialog()); set_isCoreDialog(convInfo.isCoreDialog());
@ -202,12 +204,6 @@ CurrentConversation::setInfo(const QString& key, const QString& value)
accInfo.conversationModel->updateConversationInfos(convId, infos); accInfo.conversationModel->updateConversationInfos(convId, infos);
} }
CurrentConversationMembers*
CurrentConversation::uris() const
{
return uris_;
}
void void
CurrentConversation::onConversationUpdated(const QString& convId) CurrentConversation::onConversationUpdated(const QString& convId)
{ {
@ -266,16 +262,22 @@ CurrentConversation::updateConversationPreferences(const QString& convId)
void void
CurrentConversation::connectModel() CurrentConversation::connectModel()
{ {
uris_->setMembers({}, {}, {}); membersModel_->setMembers({}, {}, {});
auto convModel = lrcInstance_->getCurrentConversationModel(); auto convModel = lrcInstance_->getCurrentConversationModel();
if (!convModel) if (!convModel)
return; return;
connect(lrcInstance_->getCurrentConversationModel(), auto connectObjectSignal = [this](auto obj, auto signal, auto slot) {
connect(obj, signal, this, slot, Qt::UniqueConnection);
};
connectObjectSignal(convModel,
&ConversationModel::conversationUpdated, &ConversationModel::conversationUpdated,
this, &CurrentConversation::onConversationUpdated);
&CurrentConversation::onConversationUpdated, connectObjectSignal(convModel,
Qt::UniqueConnection); &ConversationModel::profileUpdated,
&CurrentConversation::updateProfile);
connect(lrcInstance_->getCurrentConversationModel(), connect(lrcInstance_->getCurrentConversationModel(),
&ConversationModel::profileUpdated, &ConversationModel::profileUpdated,
this, this,

View file

@ -23,6 +23,8 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QQmlEngine> // QML registration
#include <QApplication> // QML registration
// an adapter object to expose a conversation::Info struct // an adapter object to expose a conversation::Info struct
// as a group of observable properties // as a group of observable properties
@ -30,6 +32,8 @@
class CurrentConversation final : public QObject class CurrentConversation final : public QObject
{ {
Q_OBJECT Q_OBJECT
QML_SINGLETON
QML_PROPERTY(QString, id) QML_PROPERTY(QString, id)
QML_PROPERTY(QString, title) QML_PROPERTY(QString, title)
QML_PROPERTY(QString, description) QML_PROPERTY(QString, description)
@ -56,10 +60,17 @@ class CurrentConversation final : public QObject
QML_PROPERTY(QStringList, backendErrors) QML_PROPERTY(QStringList, backendErrors)
QML_PROPERTY(QString, lastSelfMessageId) QML_PROPERTY(QString, lastSelfMessageId)
QML_RO_PROPERTY(bool, hasCall) QML_RO_PROPERTY(bool, hasCall)
QML_RO_PROPERTY(QVariant, members)
public: public:
static CurrentConversation* create(QQmlEngine*, QJSEngine*)
{
return new CurrentConversation(qApp->property("LRCInstance").value<LRCInstance*>());
}
explicit CurrentConversation(LRCInstance* lrcInstance, QObject* parent = nullptr); explicit CurrentConversation(LRCInstance* lrcInstance, QObject* parent = nullptr);
~CurrentConversation() = default; ~CurrentConversation() = default;
Q_INVOKABLE void scrollToMsg(const QString& msgId); Q_INVOKABLE void scrollToMsg(const QString& msgId);
Q_INVOKABLE void setPreference(const QString& key, const QString& value); Q_INVOKABLE void setPreference(const QString& key, const QString& value);
Q_INVOKABLE QString getPreference(const QString& key) const; Q_INVOKABLE QString getPreference(const QString& key) const;
@ -88,7 +99,7 @@ Q_SIGNALS:
private: private:
LRCInstance* lrcInstance_; LRCInstance* lrcInstance_;
CurrentConversationMembers* uris_; CurrentConversationMembers* membersModel_;
void connectModel(); void connectModel();
}; };

View file

@ -41,7 +41,7 @@ Q_ENUM_NS(Role)
class CurrentConversationMembers : public QAbstractListModel class CurrentConversationMembers : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
QML_PROPERTY(int, count) QML_RO_PROPERTY(int, count)
public: public:
explicit CurrentConversationMembers(LRCInstance* lrcInstance, QObject* parent = nullptr); explicit CurrentConversationMembers(LRCInstance* lrcInstance, QObject* parent = nullptr);

View file

@ -18,16 +18,28 @@
#pragma once #pragma once
#include "networkmanager.h" #include "networkmanager.h"
#include "connectivitymonitor.h"
#include "qtutils.h" #include "qtutils.h"
#include <QQmlEngine> // QML registration
#include <QApplication> // QML registration
class ImageDownloader : public NetworkManager class ImageDownloader : public NetworkManager
{ {
Q_OBJECT Q_OBJECT
QML_SINGLETON
QML_PROPERTY(QString, cachePath) QML_PROPERTY(QString, cachePath)
public: public:
static ImageDownloader* create(QQmlEngine*, QJSEngine*)
{
return new ImageDownloader(
qApp->property("ConnectivityMonitor").value<ConnectivityMonitor*>());
}
explicit ImageDownloader(ConnectivityMonitor* cm, QObject* parent = nullptr); explicit ImageDownloader(ConnectivityMonitor* cm, QObject* parent = nullptr);
~ImageDownloader() = default;
// Download an image and call onDownloadImageFinished when done // Download an image and call onDownloadImageFinished when done
Q_INVOKABLE void downloadImage(const QUrl& url, const QString& localPath); Q_INVOKABLE void downloadImage(const QUrl& url, const QString& localPath);

View file

@ -26,6 +26,8 @@
#include "connectivitymonitor.h" #include "connectivitymonitor.h"
#include "systemtray.h" #include "systemtray.h"
#include "videoprovider.h" #include "videoprovider.h"
#include "previewengine.h"
#include "conversationlistmodel.h"
#include <QWKQuick/qwkquickglobal.h> #include <QWKQuick/qwkquickglobal.h>
@ -42,7 +44,6 @@
#include <QQuickWindow> #include <QQuickWindow>
#include <QLoggingCategory> #include <QLoggingCategory>
#include <locale.h>
#include <thread> #include <thread>
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@ -188,6 +189,7 @@ MainApplication::init()
connectivityMonitor_ = new ConnectivityMonitor(this); connectivityMonitor_ = new ConnectivityMonitor(this);
settingsManager_ = new AppSettingsManager(this); settingsManager_ = new AppSettingsManager(this);
systemTray_ = new SystemTray(settingsManager_, this); systemTray_ = new SystemTray(settingsManager_, this);
previewEngine_ = new PreviewEngine(connectivityMonitor_, this);
// These should should be QueuedConnection to ensure that the // These should should be QueuedConnection to ensure that the
// they are executed after the QML engine has been initialized, // they are executed after the QML engine has been initialized,
@ -407,6 +409,7 @@ MainApplication::initQmlLayer()
systemTray_, systemTray_,
settingsManager_, settingsManager_,
connectivityMonitor_, connectivityMonitor_,
previewEngine_,
&screenInfo_, &screenInfo_,
this); this);

View file

@ -35,6 +35,7 @@
class ConnectivityMonitor; class ConnectivityMonitor;
class AppSettingsManager; class AppSettingsManager;
class SystemTray; class SystemTray;
class PreviewEngine;
// Provides information about the screen the app is displayed on // Provides information about the screen the app is displayed on
class ScreenInfo : public QObject class ScreenInfo : public QObject
@ -120,6 +121,7 @@ private:
ConnectivityMonitor* connectivityMonitor_; ConnectivityMonitor* connectivityMonitor_;
SystemTray* systemTray_; SystemTray* systemTray_;
AppSettingsManager* settingsManager_; AppSettingsManager* settingsManager_;
PreviewEngine* previewEngine_;
ScreenInfo screenInfo_; ScreenInfo screenInfo_;
}; };

View file

@ -57,8 +57,8 @@ Rectangle {
Layout.leftMargin: 4 Layout.leftMargin: 4
Layout.rightMargin: 4 Layout.rightMargin: 4
// Reset the model if visible or the CurrentConversationMembers.count changes (0 or greater) // Reset the model if visible or the current conv member count changes (0 or greater)
model: visible && CurrentConversationMembers.count >= 0 ? ContactAdapter.getContactSelectableModel(type) : null model: visible && CurrentConversation.members.count >= 0 ? ContactAdapter.getContactSelectableModel(type) : null
delegate: ContactPickerItemDelegate { delegate: ContactPickerItemDelegate {
id: contactPickerItemDelegate id: contactPickerItemDelegate

View file

@ -65,12 +65,12 @@ StackLayout {
} }
Connections { Connections {
target: CurrentConversationMembers target: CurrentConversation.members
function onCountChanged() { function onCountChanged() {
// Close the panel if there are 8 or more members in the // Close the panel if there are 8 or more members in the
// conversation AND the "Add Member" panel is currently open. // conversation AND the "Add Member" panel is currently open.
if (CurrentConversationMembers.count >= 8 && isOpen(ChatView.AddMemberPanel)) { if (CurrentConversation.members.count >= 8 && isOpen(ChatView.AddMemberPanel)) {
closePanel(); closePanel();
} }
} }

View file

@ -70,28 +70,6 @@ JamiListView {
onCountChanged: positionViewAtBeginning() onCountChanged: positionViewAtBeginning()
add: Transition {
NumberAnimation {
property: "opacity"
from: 0
to: 1.0
duration: JamiTheme.smartListTransitionDuration
}
}
displaced: Transition {
NumberAnimation {
properties: "x,y"
easing.type: Easing.OutCubic
duration: JamiTheme.smartListTransitionDuration
}
NumberAnimation {
property: "opacity"
to: 1.0
duration: JamiTheme.smartListTransitionDuration * (1 - from)
}
}
Behavior on opacity { Behavior on opacity {
NumberAnimation { NumberAnimation {
easing.type: Easing.OutCubic easing.type: Easing.OutCubic

View file

@ -57,7 +57,7 @@ Popup {
if (isCall) { if (isCall) {
pluginhandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(CurrentCall.id); pluginhandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(CurrentCall.id);
} else { } else {
var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversationMembers[0]; var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversation.members[0];
pluginhandlerPickerListView.model = PluginAdapter.getChatHandlerSelectableModel(LRCInstance.currentAccountId, peerId); pluginhandlerPickerListView.model = PluginAdapter.getChatHandlerSelectableModel(LRCInstance.currentAccountId, peerId);
} }
} }
@ -69,7 +69,7 @@ Popup {
pluginhandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(CurrentCall.id); pluginhandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(CurrentCall.id);
} else { } else {
var accountId = LRCInstance.currentAccountId; var accountId = LRCInstance.currentAccountId;
var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversationMembers[0]; var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversation.members[0];
PluginModel.toggleChatHandler(handlerId, accountId, peerId, !isLoaded); PluginModel.toggleChatHandler(handlerId, accountId, peerId, !isLoaded);
pluginhandlerPickerListView.model = PluginAdapter.getChatHandlerSelectableModel(accountId, peerId); pluginhandlerPickerListView.model = PluginAdapter.getChatHandlerSelectableModel(accountId, peerId);
} }
@ -124,7 +124,7 @@ Popup {
if (isCall) { if (isCall) {
return PluginAdapter.getMediaHandlerSelectableModel(CurrentCall.id); return PluginAdapter.getMediaHandlerSelectableModel(CurrentCall.id);
} else { } else {
var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversationMembers[0]; var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversation.members[0];
return PluginAdapter.getChatHandlerSelectableModel(LRCInstance.currentAccountId, peerId); return PluginAdapter.getChatHandlerSelectableModel(LRCInstance.currentAccountId, peerId);
} }
} }

View file

@ -378,7 +378,7 @@ SidePanelBase {
return parent.height; return parent.height;
} }
model: SearchResultsListModel model: ConversationsAdapter.searchListProxyModel
headerLabel: JamiStrings.searchResults headerLabel: JamiStrings.searchResults
headerVisible: true headerVisible: true
} }
@ -389,7 +389,7 @@ SidePanelBase {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
model: ConversationListModel model: ConversationsAdapter.convListProxyModel
headerLabel: JamiStrings.conversations headerLabel: JamiStrings.conversations
headerVisible: count && searchResultsListView.visible headerVisible: count && searchResultsListView.visible
} }
@ -411,7 +411,7 @@ SidePanelBase {
Layout.preferredWidth: parent.width Layout.preferredWidth: parent.width
Layout.fillHeight: true Layout.fillHeight: true
model: ConversationListModel model: ConversationsAdapter.convListProxyModel
delegate: SmartListItemDelegate { delegate: SmartListItemDelegate {
interactive: false interactive: false

View file

@ -205,7 +205,7 @@ Rectangle {
objectName: "members" objectName: "members"
visible: !CurrentConversation.isCoreDialog visible: !CurrentConversation.isCoreDialog
labelText: { labelText: {
var membersNb = CurrentConversationMembers.count; var membersNb = CurrentConversation.members.count;
if (membersNb > 1) if (membersNb > 1)
return JamiStrings.members.arg(membersNb); return JamiStrings.members.arg(membersNb);
return JamiStrings.member; return JamiStrings.member;
@ -581,7 +581,7 @@ Rectangle {
} }
} }
model: CurrentConversationMembers model: CurrentConversation.members
delegate: ItemDelegate { delegate: ItemDelegate {
id: member id: member

View file

@ -26,6 +26,7 @@
#include "appsettingsmanager.h" #include "appsettingsmanager.h"
#include "qtutils.h" #include "qtutils.h"
#include "messageparser.h" #include "messageparser.h"
#include "previewengine.h"
#include <api/datatransfermodel.h> #include <api/datatransfermodel.h>

View file

@ -20,16 +20,18 @@
#include "lrcinstance.h" #include "lrcinstance.h"
#include "qmladapterbase.h" #include "qmladapterbase.h"
#include "previewengine.h" #include "previewengine.h"
#include "messageparser.h"
#include "appsettingsmanager.h"
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QTimer> #include <QTimer>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QQmlEngine> // QML registration
class AppSettingsManager; #include <QApplication> // QML registration
class MessageParser;
class FilteredMsgListModel final : public QSortFilterProxyModel class FilteredMsgListModel final : public QSortFilterProxyModel
{ {
@ -63,6 +65,8 @@ public:
class MessagesAdapter final : public QmlAdapterBase class MessagesAdapter final : public QmlAdapterBase
{ {
Q_OBJECT Q_OBJECT
QML_SINGLETON
QML_RO_PROPERTY(QVariant, messageListModel) QML_RO_PROPERTY(QVariant, messageListModel)
QML_PROPERTY(QString, replyToId) QML_PROPERTY(QString, replyToId)
QML_PROPERTY(QString, editId) QML_PROPERTY(QString, editId)
@ -71,23 +75,19 @@ class MessagesAdapter final : public QmlAdapterBase
QML_PROPERTY(QString, searchbarPrompt) QML_PROPERTY(QString, searchbarPrompt)
public: public:
static MessagesAdapter* create(QQmlEngine*, QJSEngine*)
{
return new MessagesAdapter(qApp->property("AppSettingsManager").value<AppSettingsManager*>(),
qApp->property("PreviewEngine").value<PreviewEngine*>(),
qApp->property("LRCInstance").value<LRCInstance*>());
}
explicit MessagesAdapter(AppSettingsManager* settingsManager, explicit MessagesAdapter(AppSettingsManager* settingsManager,
PreviewEngine* previewEngine, PreviewEngine* previewEngine,
LRCInstance* instance, LRCInstance* instance,
QObject* parent = nullptr); QObject* parent = nullptr);
~MessagesAdapter() = default; ~MessagesAdapter() = default;
Q_SIGNALS:
void newInteraction(const QString& id, int type);
void newMessageBarPlaceholderText(QString& placeholderText);
void newFilePasted(QString filePath);
void newTextPasted();
void moreMessagesLoaded(qint32 loadingRequestId);
void timestampUpdated();
void fileCopied(const QString& dest);
void messageParsed(const QString& msgId, const QString& msg);
protected:
Q_INVOKABLE bool isDocument(const interaction::Type& type); Q_INVOKABLE bool isDocument(const interaction::Type& type);
Q_INVOKABLE void loadMoreMessages(); Q_INVOKABLE void loadMoreMessages();
Q_INVOKABLE void connectConversationModel(); Q_INVOKABLE void connectConversationModel();
@ -149,6 +149,15 @@ protected:
inline MessageListModel* getMsgListSourceModel() const; inline MessageListModel* getMsgListSourceModel() const;
Q_SIGNALS:
void newInteraction(const QString& id, int type);
void newFilePasted(const QString& filePath);
void newTextPasted();
void moreMessagesLoaded(qint32 loadingRequestId);
void timestampUpdated();
void fileCopied(const QString& dest);
void messageParsed(const QString& msgId, const QString& msg);
private Q_SLOTS: private Q_SLOTS:
void onNewInteraction(const QString& convUid, void onNewInteraction(const QString& convUid,
const QString& interactionId, const QString& interactionId,

View file

@ -24,7 +24,8 @@
#include "networkmanager.h" #include "networkmanager.h"
#include "lrcinstance.h" #include "lrcinstance.h"
#include "appsettingsmanager.h" #include "appsettingsmanager.h"
#include "qmlregister.h"
#include "api/pluginmodel.h"
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
@ -35,8 +36,7 @@
PluginAdapter::PluginAdapter(LRCInstance* instance, PluginAdapter::PluginAdapter(LRCInstance* instance,
AppSettingsManager* settingsManager, AppSettingsManager* settingsManager,
QObject* parent, QObject* parent)
QString baseUrl)
: QmlAdapterBase(instance, parent) : QmlAdapterBase(instance, parent)
, pluginStoreListModel_(new PluginStoreListModel(instance, this)) , pluginStoreListModel_(new PluginStoreListModel(instance, this))
, pluginVersionManager_(new PluginVersionManager(instance, settingsManager, this)) , pluginVersionManager_(new PluginVersionManager(instance, settingsManager, this))
@ -44,8 +44,9 @@ PluginAdapter::PluginAdapter(LRCInstance* instance,
, lrcInstance_(instance) , lrcInstance_(instance)
, settingsManager_(settingsManager) , settingsManager_(settingsManager)
{ {
QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, pluginStoreListModel_, "PluginStoreListModel"); pluginListModel_ = qApp->property("PluginListModel").value<PluginListModel*>();
QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, pluginListModel_, "PluginListModel") pluginStoreListModel_ = qApp->property("PluginStoreListModel").value<PluginStoreListModel*>();
updateHandlersListCount(); updateHandlersListCount();
connect(&lrcInstance_->pluginModel(), connect(&lrcInstance_->pluginModel(),
&lrc::api::PluginModel::modelUpdated, &lrc::api::PluginModel::modelUpdated,

View file

@ -19,33 +19,37 @@
#pragma once #pragma once
#include "qmladapterbase.h" #include "qmladapterbase.h"
#include "pluginlistmodel.h"
#include "pluginhandlerlistmodel.h" #include "pluginhandlerlistmodel.h"
#include "pluginlistpreferencemodel.h"
#include "appsettingsmanager.h"
#include "pluginversionmanager.h"
#include "pluginstorelistmodel.h"
#include "preferenceitemlistmodel.h"
#include <QObject> #include <QObject>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QString> #include <QString>
#include <QQmlEngine> // QML registration
#include <QApplication> // QML registration
class PluginVersionManager; class PluginListModel;
class PluginStoreListModel; class PluginStoreListModel;
class PluginVersionManager;
class AppSettingsManager; class AppSettingsManager;
class PluginAdapter final : public QmlAdapterBase class PluginAdapter final : public QmlAdapterBase
{ {
Q_OBJECT Q_OBJECT
QML_SINGLETON
QML_PROPERTY(int, callMediaHandlersListCount) QML_PROPERTY(int, callMediaHandlersListCount)
QML_PROPERTY(int, chatHandlersListCount) QML_PROPERTY(int, chatHandlersListCount)
public: public:
static PluginAdapter* create(QQmlEngine*, QJSEngine*)
{
return new PluginAdapter(qApp->property("LRCInstance").value<LRCInstance*>(),
qApp->property("AppSettingsManager").value<AppSettingsManager*>());
}
explicit PluginAdapter(LRCInstance* instance, explicit PluginAdapter(LRCInstance* instance,
AppSettingsManager* settingsManager, AppSettingsManager* settingsManager,
QObject* parent = nullptr, QObject* parent = nullptr);
QString baseUrl = "https://plugins.jami.net");
~PluginAdapter() = default; ~PluginAdapter() = default;
Q_INVOKABLE void getPluginsFromStore(); Q_INVOKABLE void getPluginsFromStore();
@ -73,9 +77,9 @@ Q_SIGNALS:
private: private:
void updateHandlersListCount(); void updateHandlersListCount();
PluginListModel* pluginListModel_;
PluginStoreListModel* pluginStoreListModel_; PluginStoreListModel* pluginStoreListModel_;
PluginVersionManager* pluginVersionManager_; PluginVersionManager* pluginVersionManager_;
PluginListModel* pluginListModel_;
std::unique_ptr<PluginHandlerListModel> pluginHandlerListModel_; std::unique_ptr<PluginHandlerListModel> pluginHandlerListModel_;

View file

@ -27,16 +27,27 @@
#include <QMutex> #include <QMutex>
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QQmlEngine> // QML registration
#include <QApplication> // QML registration
class PositionManager : public QmlAdapterBase class PositionManager : public QmlAdapterBase
{ {
Q_OBJECT Q_OBJECT
QML_SINGLETON
// map of elements : map key and isUnpin // map of elements : map key and isUnpin
QML_PROPERTY(QVariantMap, mapStatus) QML_PROPERTY(QVariantMap, mapStatus)
QML_PROPERTY(bool, mapAutoOpening) QML_PROPERTY(bool, mapAutoOpening)
QML_PROPERTY(int, positionShareConvIdsCount) QML_PROPERTY(int, positionShareConvIdsCount)
QML_PROPERTY(int, sharingUrisCount) QML_PROPERTY(int, sharingUrisCount)
public: public:
static PositionManager* create(QQmlEngine*, QJSEngine*)
{
return new PositionManager(qApp->property("AppSettingsManager").value<AppSettingsManager*>(),
qApp->property("SystemTray").value<SystemTray*>(),
qApp->property("LRCInstance").value<LRCInstance*>());
}
explicit PositionManager(AppSettingsManager* settingsManager, explicit PositionManager(AppSettingsManager* settingsManager,
SystemTray* systemTray, SystemTray* systemTray,
LRCInstance* instance, LRCInstance* instance,

View file

@ -17,32 +17,72 @@
#include "previewengine.h" #include "previewengine.h"
#include "htmlparser.h"
#include <QRegularExpression> #include <QRegularExpression>
#include <QThread> #include <QThread>
const QRegularExpression PreviewEngine::newlineRe("\\r?\\n"); class PreviewEngine::Parser : public QObject
{
Q_OBJECT
public:
explicit Parser(QObject* parent = nullptr);
~Parser() = default;
Q_SIGNAL void infoReady(const QString& id, const QVariantMap& info);
public:
Q_SLOT void processHTML(const QString& id, const QString& link, const QString& data);
private:
// An instance of HtmlParser used to parse HTML.
HtmlParser* htmlParser_;
QString getTagContent(const QList<QString>& tags, const QString& value);
QString getTitle(const QList<QString>& metaTags);
QString getDescription(const QList<QString>& metaTags);
QString getImage(const QList<QString>& metaTags);
static const QRegularExpression newlineRe;
};
const QRegularExpression PreviewEngine::Parser::newlineRe("\\r?\\n");
PreviewEngine::PreviewEngine(ConnectivityMonitor* cm, QObject* parent) PreviewEngine::PreviewEngine(ConnectivityMonitor* cm, QObject* parent)
: NetworkManager(cm, parent) : NetworkManager(cm, parent)
, htmlParser_(new HtmlParser(this)) , parser_(new PreviewEngine::Parser)
{ {
// Run this object in a separate thread. parserThread_ = new QThread();
thread_ = new QThread(); parser_->moveToThread(parserThread_);
moveToThread(thread_);
thread_->start();
// Connect on a queued connection to avoid blocking caller thread. connect(this, &PreviewEngine::parseLink, this, &PreviewEngine::onParseLink);
connect(this, &PreviewEngine::parseLink, this, &PreviewEngine::onParseLink, Qt::QueuedConnection); connect(this, &PreviewEngine::htmlReady, parser_.get(), &Parser::processHTML);
connect(parser_.get(), &Parser::infoReady, this, &PreviewEngine::infoReady);
parserThread_->start();
} }
PreviewEngine::~PreviewEngine() PreviewEngine::~PreviewEngine()
{ {
thread_->quit(); parserThread_->quit();
thread_->wait(); parserThread_->wait();
} }
void
PreviewEngine::onParseLink(const QString& id, const QString& link)
{
sendGetRequest(link,
[this, id, link](const QByteArray& html) { Q_EMIT htmlReady(id, link, html); });
}
PreviewEngine::Parser::Parser(QObject* parent)
: QObject(parent)
, htmlParser_(new HtmlParser(this))
{}
QString QString
PreviewEngine::getTagContent(const QList<QString>& tags, const QString& value) PreviewEngine::Parser::getTagContent(const QList<QString>& tags, const QString& value)
{ {
Q_FOREACH (auto tag, tags) { Q_FOREACH (auto tag, tags) {
const QRegularExpression re("(property|name)=\"(og:|twitter:|)" + value const QRegularExpression re("(property|name)=\"(og:|twitter:|)" + value
@ -56,7 +96,7 @@ PreviewEngine::getTagContent(const QList<QString>& tags, const QString& value)
} }
QString QString
PreviewEngine::getTitle(const QList<QString>& metaTags) PreviewEngine::Parser::getTitle(const QList<QString>& metaTags)
{ {
// Try with opengraph/twitter props // Try with opengraph/twitter props
QString title = getTagContent(metaTags, "title"); QString title = getTagContent(metaTags, "title");
@ -73,7 +113,7 @@ PreviewEngine::getTitle(const QList<QString>& metaTags)
} }
QString QString
PreviewEngine::getDescription(const QList<QString>& metaTags) PreviewEngine::Parser::getDescription(const QList<QString>& metaTags)
{ {
// Try with og/twitter props // Try with og/twitter props
QString desc = getTagContent(metaTags, "description"); QString desc = getTagContent(metaTags, "description");
@ -84,7 +124,7 @@ PreviewEngine::getDescription(const QList<QString>& metaTags)
} }
QString QString
PreviewEngine::getImage(const QList<QString>& metaTags) PreviewEngine::Parser::getImage(const QList<QString>& metaTags)
{ {
// Try with og/twitter props // Try with og/twitter props
QString image = getTagContent(metaTags, "image"); QString image = getTagContent(metaTags, "image");
@ -101,10 +141,9 @@ PreviewEngine::getImage(const QList<QString>& metaTags)
} }
void void
PreviewEngine::onParseLink(const QString& messageId, const QString& link) PreviewEngine::Parser::processHTML(const QString& id, const QString& link, const QString& data)
{ {
sendGetRequest(QUrl(link), [this, messageId, link](const QByteArray& html) { htmlParser_->parseHtmlString(data);
htmlParser_->parseHtmlString(html);
auto tagsNodes = htmlParser_->getTagsNodes({TidyTag_META}); auto tagsNodes = htmlParser_->getTagsNodes({TidyTag_META});
auto metaTagNodes = tagsNodes[TidyTag_META]; auto metaTagNodes = tagsNodes[TidyTag_META];
QList<QString> metaTags; QList<QString> metaTags;
@ -115,11 +154,12 @@ PreviewEngine::onParseLink(const QString& messageId, const QString& link)
if (domain.isEmpty()) { if (domain.isEmpty()) {
domain = link; domain = link;
} }
Q_EMIT infoReady(messageId, Q_EMIT infoReady(id,
{{"title", getTitle(metaTags)}, {{"title", getTitle(metaTags)},
{"description", getDescription(metaTags)}, {"description", getDescription(metaTags)},
{"image", getImage(metaTags)}, {"image", getImage(metaTags)},
{"url", link}, {"url", link},
{"domain", domain}}); {"domain", domain}});
});
} }
#include "previewengine.moc"

View file

@ -19,8 +19,6 @@
#include "networkmanager.h" #include "networkmanager.h"
#include "htmlparser.h"
class PreviewEngine final : public NetworkManager class PreviewEngine final : public NetworkManager
{ {
Q_OBJECT Q_OBJECT
@ -30,21 +28,14 @@ public:
~PreviewEngine(); ~PreviewEngine();
Q_SIGNALS: Q_SIGNALS:
void parseLink(const QString& messageId, const QString& link); void parseLink(const QString& id, const QString& link);
void infoReady(const QString& messageId, const QVariantMap& info); void infoReady(const QString& id, const QVariantMap& info);
private: private:
Q_SLOT void onParseLink(const QString& messageId, const QString& link); Q_SLOT void onParseLink(const QString& id, const QString& link);
Q_SIGNAL void htmlReady(const QString& id, const QString& link, const QByteArray& data);
// An instance of HtmlParser used to parse HTML. class Parser;
HtmlParser* htmlParser_; QScopedPointer<Parser> parser_;
QThread* parserThread_;
QString getTagContent(const QList<QString>& tags, const QString& value);
QString getTitle(const QList<QString>& metaTags);
QString getDescription(const QList<QString>& metaTags);
QString getImage(const QList<QString>& metaTags);
static const QRegularExpression newlineRe;
QThread* thread_;
}; };

View file

@ -27,7 +27,6 @@
#include "positionmanager.h" #include "positionmanager.h"
#include "tipsmodel.h" #include "tipsmodel.h"
#include "connectivitymonitor.h" #include "connectivitymonitor.h"
#include "previewengine.h"
#include "imagedownloader.h" #include "imagedownloader.h"
#include "utilsadapter.h" #include "utilsadapter.h"
#include "conversationsadapter.h" #include "conversationsadapter.h"
@ -36,7 +35,8 @@
#include "currentaccount.h" #include "currentaccount.h"
#include "videodevices.h" #include "videodevices.h"
#include "currentaccounttomigrate.h" #include "currentaccounttomigrate.h"
#include "pttlistener.h"
#include "calloverlaymodel.h"
#include "accountlistmodel.h" #include "accountlistmodel.h"
#include "mediacodeclistmodel.h" #include "mediacodeclistmodel.h"
#include "audiodevicemodel.h" #include "audiodevicemodel.h"
@ -47,7 +47,10 @@
#include "smartlistmodel.h" #include "smartlistmodel.h"
#include "filestosendlistmodel.h" #include "filestosendlistmodel.h"
#include "callInformationListModel.h" #include "callInformationListModel.h"
#include "rendererinformationlistmodel.h" #include "connectioninfolistmodel.h"
#include "callparticipantsmodel.h"
#include "pluginlistmodel.h"
#include "pluginstorelistmodel.h"
#include "qrimageprovider.h" #include "qrimageprovider.h"
#include "avatarimageprovider.h" #include "avatarimageprovider.h"
@ -75,6 +78,7 @@
// clang-format off // clang-format off
// TODO: remove this // TODO: remove this
#define QML_REGISTERSINGLETONTYPE_WITH_INSTANCE(T) \ #define QML_REGISTERSINGLETONTYPE_WITH_INSTANCE(T) \
QQmlEngine::setObjectOwnership(&T::instance(), QQmlEngine::CppOwnership); \
qmlRegisterSingletonType<T>(NS_MODELS, MODULE_VER_MAJ, MODULE_VER_MIN, #T, \ qmlRegisterSingletonType<T>(NS_MODELS, MODULE_VER_MAJ, MODULE_VER_MIN, #T, \
[](QQmlEngine* e, QJSEngine* se) -> QObject* { \ [](QQmlEngine* e, QJSEngine* se) -> QObject* { \
Q_UNUSED(e); Q_UNUSED(se); \ Q_UNUSED(e); Q_UNUSED(se); \
@ -97,6 +101,10 @@
MODULE_VER_MAJ, MODULE_VER_MIN, #T, \ MODULE_VER_MAJ, MODULE_VER_MIN, #T, \
"Don't try to add to a qml definition of " #T); "Don't try to add to a qml definition of " #T);
#define REG_QML_SINGLETON qmlRegisterSingletonType
#define REG_MODEL NS_MODELS, MODULE_VER_MAJ, MODULE_VER_MIN
#define CREATE(Obj) [=](QQmlEngine*, QJSEngine*) -> QObject* { return Obj; }
namespace Utils { namespace Utils {
/*! /*!
@ -108,65 +116,103 @@ registerTypes(QQmlEngine* engine,
SystemTray* systemTray, SystemTray* systemTray,
AppSettingsManager* settingsManager, AppSettingsManager* settingsManager,
ConnectivityMonitor* connectivityMonitor, ConnectivityMonitor* connectivityMonitor,
PreviewEngine* previewEngine,
ScreenInfo* screenInfo, ScreenInfo* screenInfo,
QObject* app) QObject* app)
{ {
// setup the adapters (their lifetimes are that of MainApplication) /* Used in ContactAdapter */
auto avatarRegistry = new AvatarRegistry(lrcInstance, engine); auto connectionInfoListModel = new ConnectionInfoListModel(lrcInstance, app);
auto callAdapter = new CallAdapter(settingsManager, systemTray, lrcInstance, engine); qApp->setProperty("ConnectionInfoListModel", QVariant::fromValue(connectionInfoListModel));
auto previewEngine = new PreviewEngine(connectivityMonitor, engine); QQmlEngine::setObjectOwnership(connectionInfoListModel, QQmlEngine::CppOwnership);
auto imageDownloader = new ImageDownloader(connectivityMonitor, engine); REG_QML_SINGLETON<ConnectionInfoListModel>(REG_MODEL, "ConnectionInfoListModel", CREATE(connectionInfoListModel));
auto messagesAdapter = new MessagesAdapter(settingsManager, previewEngine, lrcInstance, engine);
auto positionManager = new PositionManager(settingsManager, systemTray, lrcInstance, engine); /* Used in AccountAdapter */
auto conversationsAdapter = new ConversationsAdapter(systemTray, lrcInstance, engine); auto accountListModel = new AccountListModel(lrcInstance, app);
auto avAdapter = new AvAdapter(lrcInstance, engine); qApp->setProperty("AccountListModel", QVariant::fromValue(accountListModel));
auto contactAdapter = new ContactAdapter(lrcInstance, engine); QQmlEngine::setObjectOwnership(accountListModel, QQmlEngine::CppOwnership);
auto accountAdapter = new AccountAdapter(settingsManager, systemTray, lrcInstance, engine); REG_QML_SINGLETON<AccountListModel>(REG_MODEL, "AccountListModel", CREATE(accountListModel));
auto utilsAdapter = new UtilsAdapter(settingsManager, systemTray, lrcInstance, engine);
auto pluginAdapter = new PluginAdapter(lrcInstance, settingsManager, engine); auto deviceItemListModel = new DeviceItemListModel(lrcInstance, app);
auto currentCall = new CurrentCall(lrcInstance, engine); qApp->setProperty("DeviceItemListModel", QVariant::fromValue(deviceItemListModel));
auto currentConversation = new CurrentConversation(lrcInstance, engine); QQmlEngine::setObjectOwnership(deviceItemListModel, QQmlEngine::CppOwnership);
auto currentAccount = new CurrentAccount(lrcInstance, settingsManager, engine); REG_QML_SINGLETON<DeviceItemListModel>(REG_MODEL, "DeviceItemListModel", CREATE(deviceItemListModel));
auto tipsModel = new TipsModel(settingsManager, engine);
auto videoDevices = new VideoDevices(lrcInstance, engine); auto moderatorListModel = new ModeratorListModel(lrcInstance, app);
auto currentAccountToMigrate = new CurrentAccountToMigrate(lrcInstance, engine); qApp->setProperty("ModeratorListModel", QVariant::fromValue(moderatorListModel));
auto wizardViewStepModel = new WizardViewStepModel(lrcInstance, accountAdapter, settingsManager, engine); QQmlEngine::setObjectOwnership(moderatorListModel, QQmlEngine::CppOwnership);
REG_QML_SINGLETON<ModeratorListModel>(REG_MODEL, "ModeratorListModel", CREATE(moderatorListModel));
/* Used in CallAdapter */
auto pttListener = new PTTListener(settingsManager, app);
qApp->setProperty("PTTListener", QVariant::fromValue(pttListener));
QQmlEngine::setObjectOwnership(pttListener, QQmlEngine::CppOwnership);
REG_QML_SINGLETON<PTTListener>(REG_MODEL, "PTTListener", CREATE(pttListener));
auto callOverlayModel = new CallOverlayModel(lrcInstance, pttListener, app);
qApp->setProperty("CallOverlayModel", QVariant::fromValue(callOverlayModel));
QQmlEngine::setObjectOwnership(callOverlayModel, QQmlEngine::CppOwnership);
REG_QML_SINGLETON<CallOverlayModel>(REG_MODEL, "CallOverlayModel", CREATE(callOverlayModel));
/* Used in CurrentCall */
auto callParticipantsModel = new CallParticipantsModel(lrcInstance, app);
qApp->setProperty("CallParticipantsModel", QVariant::fromValue(callParticipantsModel));
QQmlEngine::setObjectOwnership(callParticipantsModel, QQmlEngine::CppOwnership);
REG_QML_SINGLETON<CallParticipantsModel>(REG_MODEL, "CallParticipantsModel", CREATE(callParticipantsModel));
/* Used in ConversationsAdapter */
auto convListProxyModel = new ConversationListProxyModel(nullptr, app);
qApp->setProperty("ConvListProxyModel", QVariant::fromValue(convListProxyModel));
auto searchProxyListModel = new SelectableListProxyModel(nullptr, app);
qApp->setProperty("ConvSearchListProxyModel", QVariant::fromValue(searchProxyListModel));
// This causes mutually exclusive selection between the two proxy models.
new SelectableListProxyGroupModel({convListProxyModel, searchProxyListModel}, app);
/* Used in PluginManager */
auto pluginListModel = new PluginListModel(lrcInstance, app);
qApp->setProperty("PluginListModel", QVariant::fromValue(pluginListModel));
QQmlEngine::setObjectOwnership(pluginListModel, QQmlEngine::CppOwnership);
REG_QML_SINGLETON<PluginListModel>(REG_MODEL, "PluginListModel", CREATE(pluginListModel));
auto pluginStoreListModel = new PluginStoreListModel(lrcInstance, app);
qApp->setProperty("PluginStoreListModel", QVariant::fromValue(pluginStoreListModel));
QQmlEngine::setObjectOwnership(pluginStoreListModel, QQmlEngine::CppOwnership);
REG_QML_SINGLETON<PluginStoreListModel>(REG_MODEL, "PluginStoreListModel", CREATE(pluginStoreListModel));
// Register app-level objects that are used by QML created objects.
// These MUST be set prior to loading the initial QML file, in order to
// be available to the QML adapter class factory creation methods.
qApp->setProperty("LRCInstance", QVariant::fromValue(lrcInstance));
qApp->setProperty("SystemTray", QVariant::fromValue(systemTray));
qApp->setProperty("AppSettingsManager", QVariant::fromValue(settingsManager));
qApp->setProperty("ConnectivityMonitor", QVariant::fromValue(connectivityMonitor));
qApp->setProperty("PreviewEngine", QVariant::fromValue(previewEngine));
// qml adapter registration // qml adapter registration
QML_REGISTERSINGLETONTYPE_POBJECT(NS_HELPERS, avatarRegistry, "AvatarRegistry"); QML_REGISTERSINGLETON_TYPE(NS_HELPERS, AvatarRegistry);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, callAdapter, "CallAdapter"); QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, AccountAdapter);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, tipsModel, "TipsModel"); QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, CallAdapter);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, messagesAdapter, "MessagesAdapter"); QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, MessagesAdapter);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, positionManager, "PositionManager"); QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, ConversationsAdapter);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, conversationsAdapter, "ConversationsAdapter"); QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, ContactAdapter);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, avAdapter, "AvAdapter"); QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, UtilsAdapter);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, contactAdapter, "ContactAdapter"); QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, PositionManager);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, accountAdapter, "AccountAdapter"); QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, AvAdapter);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, utilsAdapter, "UtilsAdapter"); QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, PluginAdapter);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, pluginAdapter, "PluginAdapter"); QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, CurrentAccount);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentCall, "CurrentCall"); QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, CurrentConversation);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentConversation, "CurrentConversation"); QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, CurrentCall);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentConversation->uris(), "CurrentConversationMembers"); QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, TipsModel);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentAccount, "CurrentAccount"); QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, VideoDevices);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, videoDevices, "VideoDevices"); QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, CurrentAccountToMigrate);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentAccountToMigrate, "CurrentAccountToMigrate") QML_REGISTERSINGLETON_TYPE(NS_MODELS, WizardViewStepModel);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, wizardViewStepModel, "WizardViewStepModel") QML_REGISTERSINGLETON_TYPE(NS_HELPERS, ImageDownloader);
QML_REGISTERSINGLETONTYPE_POBJECT(NS_HELPERS, imageDownloader, "ImageDownloader")
// TODO: remove these // TODO: remove these
QML_REGISTERSINGLETONTYPE_CUSTOM(NS_MODELS, AVModel, &lrcInstance->avModel()) QML_REGISTERSINGLETONTYPE_CUSTOM(NS_MODELS, AVModel, &lrcInstance->avModel())
QML_REGISTERSINGLETONTYPE_CUSTOM(NS_MODELS, PluginModel, &lrcInstance->pluginModel()) QML_REGISTERSINGLETONTYPE_CUSTOM(NS_MODELS, PluginModel, &lrcInstance->pluginModel())
QML_REGISTERSINGLETONTYPE_CUSTOM(NS_HELPERS, AppVersionManager, lrcInstance->getAppVersionManager()) QML_REGISTERSINGLETONTYPE_CUSTOM(NS_HELPERS, AppVersionManager, lrcInstance->getAppVersionManager())
QML_REGISTERSINGLETONTYPE_WITH_INSTANCE(NameDirectory); // C++ singleton
// Hack for QtCreator autocomplete (part 2)
// https://bugreports.qt.io/browse/QTCREATORBUG-20569
// Use a dummy object to register the import namespace.
// This occurs when we register from within MainApplication
QML_REGISTERNAMESPACE(NS_MODELS, dummy::staticMetaObject, "");
QML_REGISTERNAMESPACE(NS_ADAPTERS, dummy::staticMetaObject, "");
QML_REGISTERNAMESPACE(NS_CONSTANTS, dummy::staticMetaObject, "");
QML_REGISTERNAMESPACE(NS_HELPERS, dummy::staticMetaObject, "");
QML_REGISTERNAMESPACE(NS_ENUMS, dummy::staticMetaObject, "");
// QAbstractListModels // QAbstractListModels
QML_REGISTERTYPE(NS_MODELS, BannedListModel); QML_REGISTERTYPE(NS_MODELS, BannedListModel);
@ -176,10 +222,7 @@ registerTypes(QQmlEngine* engine,
QML_REGISTERTYPE(NS_MODELS, PreferenceItemListModel); QML_REGISTERTYPE(NS_MODELS, PreferenceItemListModel);
QML_REGISTERTYPE(NS_MODELS, PluginListPreferenceModel); QML_REGISTERTYPE(NS_MODELS, PluginListPreferenceModel);
QML_REGISTERTYPE(NS_MODELS, FilesToSendListModel); QML_REGISTERTYPE(NS_MODELS, FilesToSendListModel);
QML_REGISTERTYPE(NS_MODELS, SmartListModel);
QML_REGISTERTYPE(NS_MODELS, MessageListModel);
QML_REGISTERTYPE(NS_MODELS, CallInformationListModel); QML_REGISTERTYPE(NS_MODELS, CallInformationListModel);
QML_REGISTERTYPE(NS_MODELS, RendererInformationListModel);
// Roles & type enums for models // Roles & type enums for models
QML_REGISTERNAMESPACE(NS_MODELS, AccountList::staticMetaObject, "AccountList"); QML_REGISTERNAMESPACE(NS_MODELS, AccountList::staticMetaObject, "AccountList");
@ -201,10 +244,6 @@ registerTypes(QQmlEngine* engine,
QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, lrcInstance, "LRCInstance") QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, lrcInstance, "LRCInstance")
QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, settingsManager, "AppSettingsManager") QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, settingsManager, "AppSettingsManager")
// C++ singletons
// TODO: remove this
QML_REGISTERSINGLETONTYPE_WITH_INSTANCE(NameDirectory);
// Lrc namespaces, models, and singletons // Lrc namespaces, models, and singletons
QML_REGISTERNAMESPACE(NS_MODELS, lrc::api::staticMetaObject, "Lrc"); QML_REGISTERNAMESPACE(NS_MODELS, lrc::api::staticMetaObject, "Lrc");
QML_REGISTERNAMESPACE(NS_MODELS, lrc::api::account::staticMetaObject, "Account"); QML_REGISTERNAMESPACE(NS_MODELS, lrc::api::account::staticMetaObject, "Account");
@ -239,13 +278,7 @@ registerTypes(QQmlEngine* engine,
QML_REGISTERUNCREATABLE(NS_ENUMS, VideoFormatFpsModel) QML_REGISTERUNCREATABLE(NS_ENUMS, VideoFormatFpsModel)
engine->addImageProvider(QLatin1String("qrImage"), new QrImageProvider(lrcInstance)); engine->addImageProvider(QLatin1String("qrImage"), new QrImageProvider(lrcInstance));
engine->addImageProvider(QLatin1String("avatarimage"), engine->addImageProvider(QLatin1String("avatarimage"), new AvatarImageProvider(lrcInstance));
new AvatarImageProvider(lrcInstance));
engine->setObjectOwnership(&lrcInstance->avModel(), QQmlEngine::CppOwnership);
engine->setObjectOwnership(&lrcInstance->pluginModel(), QQmlEngine::CppOwnership);
engine->setObjectOwnership(lrcInstance->getAppVersionManager(), QQmlEngine::CppOwnership);
engine->setObjectOwnership(&NameDirectory::instance(), QQmlEngine::CppOwnership);
} }
// clang-format on // clang-format on
} // namespace Utils } // namespace Utils

View file

@ -46,6 +46,9 @@ Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
} // namespace dummy } // namespace dummy
// clang-format off // clang-format off
#define QML_REGISTERSINGLETON_TYPE(NS, T) \
qmlRegisterSingletonType<T>(NS, MODULE_VER_MAJ, MODULE_VER_MIN, #T, T::create);
#define QML_REGISTERSINGLETONTYPE_POBJECT(NS, I, N) \ #define QML_REGISTERSINGLETONTYPE_POBJECT(NS, I, N) \
QQmlEngine::setObjectOwnership(I, QQmlEngine::CppOwnership); \ QQmlEngine::setObjectOwnership(I, QQmlEngine::CppOwnership); \
{ using T = std::remove_reference<decltype(*I)>::type; \ { using T = std::remove_reference<decltype(*I)>::type; \
@ -67,6 +70,7 @@ void registerTypes(QQmlEngine* engine,
SystemTray* systemTray, SystemTray* systemTray,
AppSettingsManager* appSettingsManager, AppSettingsManager* appSettingsManager,
ConnectivityMonitor* connectivityMonitor, ConnectivityMonitor* connectivityMonitor,
PreviewEngine* previewEngine,
ScreenInfo* screenInfo, ScreenInfo* screenInfo,
QObject* app); QObject* app);
} }

View file

@ -34,7 +34,7 @@ SettingsPageBase {
property bool isSIP: CurrentAccount.type === Profile.Type.SIP property bool isSIP: CurrentAccount.type === Profile.Type.SIP
property int itemWidth: 132 property int itemWidth: 132
property string key: PttListener.keyToString(PttListener.getCurrentKey()) property string key: PTTListener.keyToString(PTTListener.getCurrentKey())
title: JamiStrings.callSettingsTitle title: JamiStrings.callSettingsTitle
function updateAndShowModeratorsSlot() { function updateAndShowModeratorsSlot() {
@ -437,7 +437,7 @@ SettingsPageBase {
onClicked: { onClicked: {
var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/ChangePttKeyPopup.qml"); var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/ChangePttKeyPopup.qml");
dlg.choiceMade.connect(function (chosenKey) { dlg.choiceMade.connect(function (chosenKey) {
keyLabel.text = PttListener.keyToString(chosenKey); keyLabel.text = PTTListener.keyToString(chosenKey);
}); });
} }
} }

View file

@ -14,13 +14,16 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import net.jami.Adapters 1.1 import net.jami.Adapters 1.1
import net.jami.Constants 1.1 import net.jami.Constants 1.1
import net.jami.Enums 1.1 import net.jami.Enums 1.1
import net.jami.Models 1.1 import net.jami.Models 1.1
import "../../commoncomponents" import "../../commoncomponents"
import "../js/logviewwindowcreation.js" as LogViewWindowCreation import "../js/logviewwindowcreation.js" as LogViewWindowCreation
@ -109,16 +112,14 @@ ListView {
model: ConnectionInfoListModel model: ConnectionInfoListModel
Component.onCompleted: { Component.onCompleted: ConnectionInfoListModel.update()
ContactAdapter.updateConnectionInfo();
}
Timer { Timer {
interval: 1000 interval: 1000
running: root.visible running: root.visible
repeat: true repeat: true
onTriggered: { onTriggered: {
ContactAdapter.updateConnectionInfo(); ConnectionInfoListModel.update();
listview.rota = listview.rota + 5; listview.rota = listview.rota + 5;
} }
} }

View file

@ -17,12 +17,14 @@
*/ */
#pragma once #pragma once
#include "lrcinstance.h"
#include "appsettingsmanager.h" #include "appsettingsmanager.h"
#include "qtutils.h"
#include "typedefs.h"
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QObject> #include <QObject>
#include <QQmlEngine> // QML registration
#include <QApplication> // QML registration
#define TIPS_ROLES \ #define TIPS_ROLES \
X(TipId) \ X(TipId) \
@ -44,8 +46,14 @@ Q_ENUM_NS(Role)
class TipsModel : public QAbstractListModel class TipsModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
QML_SINGLETON
public: public:
static TipsModel* create(QQmlEngine*, QJSEngine*)
{
return new TipsModel(qApp->property("AppSettingsManager").value<AppSettingsManager*>());
}
TipsModel(AppSettingsManager* sm, QObject* parent = nullptr); TipsModel(AppSettingsManager* sm, QObject* parent = nullptr);
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;

View file

@ -30,12 +30,15 @@
#include <QApplication> #include <QApplication>
#include <QObject> #include <QObject>
#include <QQmlEngine> // QML registration
#include <QApplication> // QML registration
#if __has_include(<gio/gio.h>) #if __has_include(<gio/gio.h>)
#include <gio/gio.h> #include <gio/gio.h>
#endif #endif
#if defined(WIN32) && __has_include(<winrt/Windows.Foundation.h>) #if defined(WIN32) && __has_include(<winrt/Windows.Foundation.h>)
#define _SILENCE_CLANG_COROUTINE_MESSAGE
#include <winrt/Windows.Foundation.h> #include <winrt/Windows.Foundation.h>
#define WATCHSYSTEMTHEME __has_include(<winrt/Windows.UI.ViewManagement.h>) #define WATCHSYSTEMTHEME __has_include(<winrt/Windows.UI.ViewManagement.h>)
@ -67,9 +70,18 @@ bool readAppsUseLightThemeRegistry(bool getValue);
class UtilsAdapter final : public QmlAdapterBase class UtilsAdapter final : public QmlAdapterBase
{ {
Q_OBJECT Q_OBJECT
QML_SINGLETON
QML_PROPERTY(QStringList, logList) QML_PROPERTY(QStringList, logList)
QML_RO_PROPERTY(bool, isRTL) QML_RO_PROPERTY(bool, isRTL)
public: public:
static UtilsAdapter* create(QQmlEngine*, QJSEngine*)
{
return new UtilsAdapter(qApp->property("AppSettingsManager").value<AppSettingsManager*>(),
qApp->property("SystemTray").value<SystemTray*>(),
qApp->property("LRCInstance").value<LRCInstance*>());
}
explicit UtilsAdapter(AppSettingsManager* settingsManager, explicit UtilsAdapter(AppSettingsManager* settingsManager,
SystemTray* systemTray, SystemTray* systemTray,
LRCInstance* instance, LRCInstance* instance,

View file

@ -18,7 +18,8 @@
#include "videodevices.h" #include "videodevices.h"
// VideoInputDeviceModel #include "api/devicemodel.h"
VideoInputDeviceModel::VideoInputDeviceModel(LRCInstance* lrcInstance, VideoInputDeviceModel::VideoInputDeviceModel(LRCInstance* lrcInstance,
VideoDevices* videoDeviceInstance) VideoDevices* videoDeviceInstance)
: QAbstractListModel(videoDeviceInstance) : QAbstractListModel(videoDeviceInstance)
@ -124,7 +125,6 @@ VideoFormatResolutionModel::getCurrentIndex() const
return resultList.size() > 0 ? resultList[0].row() : 0; return resultList.size() > 0 ? resultList[0].row() : 0;
} }
// VideoFormatFpsModel
VideoFormatFpsModel::VideoFormatFpsModel(LRCInstance* lrcInstance, VideoDevices* videoDeviceInstance) VideoFormatFpsModel::VideoFormatFpsModel(LRCInstance* lrcInstance, VideoDevices* videoDeviceInstance)
: QAbstractListModel(videoDeviceInstance) : QAbstractListModel(videoDeviceInstance)
, lrcInstance_(lrcInstance) , lrcInstance_(lrcInstance)
@ -177,7 +177,6 @@ VideoFormatFpsModel::getCurrentIndex() const
return resultList.size() > 0 ? resultList[0].row() : 0; return resultList.size() > 0 ? resultList[0].row() : 0;
} }
// VideoDevices
VideoDevices::VideoDevices(LRCInstance* lrcInstance, QObject* parent) VideoDevices::VideoDevices(LRCInstance* lrcInstance, QObject* parent)
: QObject(parent) : QObject(parent)
, lrcInstance_(lrcInstance) , lrcInstance_(lrcInstance)

View file

@ -21,9 +21,9 @@
#include "lrcinstance.h" #include "lrcinstance.h"
#include "qtutils.h" #include "qtutils.h"
#include "api/devicemodel.h"
#include <QObject> #include <QObject>
#include <QQmlEngine> // QML registration
#include <QApplication> // QML registration
class VideoDevices; class VideoDevices;
@ -115,6 +115,8 @@ private:
class VideoDevices : public QObject class VideoDevices : public QObject
{ {
Q_OBJECT Q_OBJECT
QML_SINGLETON
QML_RO_PROPERTY(int, listSize) QML_RO_PROPERTY(int, listSize)
QML_RO_PROPERTY(QString, defaultChannel) QML_RO_PROPERTY(QString, defaultChannel)
@ -130,6 +132,11 @@ class VideoDevices : public QObject
QML_RO_PROPERTY(QVariant, sharingFpsSourceModel) QML_RO_PROPERTY(QVariant, sharingFpsSourceModel)
public: public:
static VideoDevices* create(QQmlEngine*, QJSEngine*)
{
return new VideoDevices(qApp->property("LRCInstance").value<LRCInstance*>());
}
explicit VideoDevices(LRCInstance* lrcInstance, QObject* parent = nullptr); explicit VideoDevices(LRCInstance* lrcInstance, QObject* parent = nullptr);
~VideoDevices() = default; ~VideoDevices() = default;

View file

@ -50,6 +50,31 @@ BaseView {
} }
} }
// Handle the end of the wizard account creation process.
Connections {
target: WizardViewStepModel
function onCreateAccountRequested(creationOption) {
switch (creationOption) {
case WizardViewStepModel.AccountCreationOption.CreateJamiAccount:
case WizardViewStepModel.AccountCreationOption.CreateRendezVous:
case WizardViewStepModel.AccountCreationOption.ImportFromBackup:
case WizardViewStepModel.AccountCreationOption.ImportFromDevice:
AccountAdapter.createJamiAccount(WizardViewStepModel.accountCreationInfo);
break;
case WizardViewStepModel.AccountCreationOption.ConnectToAccountManager:
AccountAdapter.createJAMSAccount(WizardViewStepModel.accountCreationInfo);
break;
case WizardViewStepModel.AccountCreationOption.CreateSipAccount:
AccountAdapter.createSIPAccount(WizardViewStepModel.accountCreationInfo);
break;
default:
print("Bad account creation option: " + creationOption);
WizardViewStepModel.closeWizardView();
break;
}
}
}
Connections { Connections {
target: WizardViewStepModel target: WizardViewStepModel

View file

@ -18,26 +18,23 @@
#include "wizardviewstepmodel.h" #include "wizardviewstepmodel.h"
#include "accountadapter.h"
#include "appsettingsmanager.h" #include "appsettingsmanager.h"
#include "lrcinstance.h"
#include "api/accountmodel.h"
WizardViewStepModel::WizardViewStepModel(LRCInstance* lrcInstance, WizardViewStepModel::WizardViewStepModel(LRCInstance* lrcInstance,
AccountAdapter* accountAdapter,
AppSettingsManager* appSettingsManager, AppSettingsManager* appSettingsManager,
QObject* parent) QObject* parent)
: QObject(parent) : QObject(parent)
, lrcInstance_(lrcInstance) , lrcInstance_(lrcInstance)
, accountAdapter_(accountAdapter)
, appSettingsManager_(appSettingsManager) , appSettingsManager_(appSettingsManager)
{ {
reset(); reset();
connect(&lrcInstance_->accountModel(),
connect(accountAdapter_, &AccountModel::accountAdded,
&AccountAdapter::accountAdded,
this, this,
[this](QString accountId, int index) { [this](const QString& accountId) {
accountAdapter_->changeAccount(index);
auto accountCreationOption = get_accountCreationOption(); auto accountCreationOption = get_accountCreationOption();
if (accountCreationOption == AccountCreationOption::ConnectToAccountManager if (accountCreationOption == AccountCreationOption::ConnectToAccountManager
|| accountCreationOption == AccountCreationOption::CreateSipAccount) { || accountCreationOption == AccountCreationOption::CreateSipAccount) {
@ -66,30 +63,8 @@ WizardViewStepModel::startAccountCreationFlow(AccountCreationOption accountCreat
void void
WizardViewStepModel::nextStep() WizardViewStepModel::nextStep()
{ {
auto accountCreationOption = get_accountCreationOption(); if (mainStep_ != MainSteps::Initial) {
if (get_mainStep() == MainSteps::Initial Q_EMIT createAccountRequested(accountCreationOption_);
|| accountCreationOption == AccountCreationOption::None) {
return;
}
switch (accountCreationOption) {
case AccountCreationOption::CreateJamiAccount:
case AccountCreationOption::CreateRendezVous:
case AccountCreationOption::ImportFromBackup:
case AccountCreationOption::ImportFromDevice: {
accountAdapter_->createJamiAccount(get_accountCreationInfo());
break;
}
case AccountCreationOption::ConnectToAccountManager: {
accountAdapter_->createJAMSAccount(get_accountCreationInfo());
break;
}
case AccountCreationOption::CreateSipAccount: {
accountAdapter_->createSIPAccount(get_accountCreationInfo());
break;
}
default:
return;
} }
} }

View file

@ -18,11 +18,13 @@
#pragma once #pragma once
#include "qtutils.h"
#include <QObject> #include <QObject>
#include <QVariant> #include <QVariant>
#include <QMap> #include <QMap>
#include <QQmlEngine> // QML registration
#include "qtutils.h" #include <QApplication> // QML registration
class AccountAdapter; class AccountAdapter;
class LRCInstance; class LRCInstance;
@ -31,6 +33,7 @@ class AppSettingsManager;
class WizardViewStepModel : public QObject class WizardViewStepModel : public QObject
{ {
Q_OBJECT Q_OBJECT
QML_SINGLETON
Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
public: public:
@ -54,12 +57,17 @@ public:
QML_PROPERTY(MainSteps, mainStep) QML_PROPERTY(MainSteps, mainStep)
QML_PROPERTY(AccountCreationOption, accountCreationOption) QML_PROPERTY(AccountCreationOption, accountCreationOption)
QML_PROPERTY(QVariantMap, accountCreationInfo) QML_PROPERTY(QVariantMap, accountCreationInfo)
public: public:
static WizardViewStepModel* create(QQmlEngine*, QJSEngine*)
{
return new WizardViewStepModel(qApp->property("LRCInstance").value<LRCInstance*>(),
qApp->property("AppSettingsManager")
.value<AppSettingsManager*>());
}
explicit WizardViewStepModel(LRCInstance* lrcInstance, explicit WizardViewStepModel(LRCInstance* lrcInstance,
AccountAdapter* accountAdapter,
AppSettingsManager* appSettingsManager, AppSettingsManager* appSettingsManager,
QObject* parent = nullptr); QObject* parent = nullptr);
@ -70,11 +78,11 @@ public:
Q_SIGNALS: Q_SIGNALS:
void accountIsReady(QString accountId); void accountIsReady(QString accountId);
void closeWizardView(); void closeWizardView();
void createAccountRequested(AccountCreationOption);
private: private:
void reset(); void reset();
LRCInstance* lrcInstance_; LRCInstance* lrcInstance_;
AccountAdapter* accountAdapter_;
AppSettingsManager* appSettingsManager_; AppSettingsManager* appSettingsManager_;
}; };

View file

@ -136,6 +136,7 @@ public Q_SLOTS:
systemTray_.get(), systemTray_.get(),
settingsManager_.get(), settingsManager_.get(),
connectivityMonitor_.get(), connectivityMonitor_.get(),
previewEngine_.get(),
&screenInfo_, &screenInfo_,
this); this);

View file

@ -17,6 +17,7 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtTest
import "../../../src/app/" import "../../../src/app/"
@ -24,8 +25,18 @@ import "../../../src/app/"
// each UUT from having to manage its own top level app management objects // each UUT from having to manage its own top level app management objects
// (currently ViewManager, ViewCoordinator, and ApplicationWindow). // (currently ViewManager, ViewCoordinator, and ApplicationWindow).
Item { Item {
// This will be our UUT. id: tw
required default property var uut
width: childrenRect.width
height: childrenRect.height
// A binding to the windowShown property
Binding {
tw.appWindow: uut.Window.window
when: QTestRootObject.windowShown
}
Component.onCompleted: viewCoordinator.init(this)
// These are our fake app management objects. The caveat is that they // These are our fake app management objects. The caveat is that they
// must be maintained in sync with the actual objects in the app for now. // must be maintained in sync with the actual objects in the app for now.
@ -33,7 +44,7 @@ Item {
// sync them. // sync them.
property ViewManager viewManager: ViewManager {} property ViewManager viewManager: ViewManager {}
property ViewCoordinator viewCoordinator: ViewCoordinator {} property ViewCoordinator viewCoordinator: ViewCoordinator {}
property ApplicationWindow appWindow: ApplicationWindow { property QtObject appWindow: QtObject {
property bool useFrameless: false property bool useFrameless: false
} }
} }

View file

@ -28,17 +28,20 @@ import "../../../src/app/mainview"
import "../../../src/app/mainview/components" import "../../../src/app/mainview/components"
import "../../../src/app/commoncomponents" import "../../../src/app/commoncomponents"
CallMessageDelegate { TestWrapper {
CallMessageDelegate {
id: uut id: uut
type: Interaction.Type.CALL type: Interaction.Type.CALL
TestCase { TestCase {
name: "Check basic visibility for header buttons" name: "Check basic visibility for option buttons"
function test_checkBasicVisibility() { function test_checkOptionButtonsVisibility() {
var moreButton = findChild(uut, "more") var moreButton = findChild(uut, "more")
var replyButton = findChild(uut, "reply") var replyButton = findChild(uut, "reply")
compare(moreButton.visible, false) compare(moreButton.visible, false)
compare(replyButton.visible, false) compare(replyButton.visible, false)
} }
} }
}
} }

View file

@ -25,27 +25,22 @@ import net.jami.Constants 1.1
import "../../../src/app/" import "../../../src/app/"
import "../../../src/app/mainview/components" import "../../../src/app/mainview/components"
OngoingCallPage { TestWrapper {
OngoingCallPage {
id: uut id: uut
width: 800 width: 800
height: 600 height: 600
property QtObject appWindow
property ViewManager viewManager: ViewManager {}
property ViewCoordinator viewCoordinator: ViewCoordinator {}
TestCase { TestCase {
name: "Check basic visibility of action bar during a call" name: "Check basic visibility of action bar during a call"
when: windowShown // Mouse events can only be handled when: windowShown // Mouse events can only be handled
// after the window has been shown. // after the window has been shown.
property var callOverlay
property var mainOverlay property var mainOverlay
function initTestCase() { function initTestCase() {
callOverlay = findChild(uut, "callOverlay") mainOverlay = findChild(uut, "mainOverlay")
mainOverlay = findChild(callOverlay, "mainOverlay")
// The CallActionBar on the OngoingCallPage starts out invisible and // The CallActionBar on the OngoingCallPage starts out invisible and
// is made visible whenever the user moves their mouse. // is made visible whenever the user moves their mouse.
@ -54,15 +49,11 @@ OngoingCallPage {
// visible. In the actual Jami application, this happens when a call // visible. In the actual Jami application, this happens when a call
// is started, but we need to toggle the visiblity manually here // is started, but we need to toggle the visiblity manually here
// because the MainOverlay is visible at the beginning of the test. // because the MainOverlay is visible at the beginning of the test.
appWindow = uut.Window.window
mainOverlay.visible = false mainOverlay.visible = false
mainOverlay.visible = true mainOverlay.visible = true
// Calling mouseMove() will generate warnings if we don't call init first.
viewCoordinator.init(uut)
} }
function test_checkBasicVisibility() { function test_checkCallActionBarVisibility() {
var callActionBar = findChild(mainOverlay, "callActionBar") var callActionBar = findChild(mainOverlay, "callActionBar")
// The primary and secondary actions in the CallActionBar are currently being added // The primary and secondary actions in the CallActionBar are currently being added
@ -88,4 +79,5 @@ OngoingCallPage {
compare(callActionBar.visible, true) compare(callActionBar.visible, true)
} }
} }
}
} }

View file

@ -24,9 +24,12 @@ import "../../../src/app/"
import "../../../src/app/mainview/components" import "../../../src/app/mainview/components"
TestWrapper { TestWrapper {
uut: WelcomePage { WelcomePage {
id: uut
TestCase { TestCase {
name: "Open 'About Jami' popup" name: "Open 'About Jami' popup"
when: windowShown
function test_openAboutPopup() { function test_openAboutPopup() {
var aboutJamiButton = findChild(uut, "aboutJami") var aboutJamiButton = findChild(uut, "aboutJami")