1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-04-21 21:52:03 +02:00

swarmdetailspanel: show kicked contacts if administrator

This patch avoid for non-admins to try to re-add kicked members
as filtered out from the list. However kicked members are only
visible for administrators.

Change-Id: Ie01b7071c072d147bbc0f39e477cc24d7fd58b1a
This commit is contained in:
Sébastien Blin 2023-01-05 16:08:44 -05:00
parent 06ab19f213
commit 3570b23d8a
16 changed files with 216 additions and 44 deletions

View file

@ -215,6 +215,7 @@ set(COMMON_SOURCES
${APP_SRC_DIR}/wizardviewstepmodel.cpp ${APP_SRC_DIR}/wizardviewstepmodel.cpp
${APP_SRC_DIR}/avatarregistry.cpp ${APP_SRC_DIR}/avatarregistry.cpp
${APP_SRC_DIR}/currentconversation.cpp ${APP_SRC_DIR}/currentconversation.cpp
${APP_SRC_DIR}/currentconversationmembers.cpp
${APP_SRC_DIR}/currentaccount.cpp ${APP_SRC_DIR}/currentaccount.cpp
${APP_SRC_DIR}/videodevices.cpp ${APP_SRC_DIR}/videodevices.cpp
${APP_SRC_DIR}/videoprovider.cpp ${APP_SRC_DIR}/videoprovider.cpp
@ -275,6 +276,7 @@ set(COMMON_HEADERS
${APP_SRC_DIR}/wizardviewstepmodel.h ${APP_SRC_DIR}/wizardviewstepmodel.h
${APP_SRC_DIR}/avatarregistry.h ${APP_SRC_DIR}/avatarregistry.h
${APP_SRC_DIR}/currentconversation.h ${APP_SRC_DIR}/currentconversation.h
${APP_SRC_DIR}/currentconversationmembers.h
${APP_SRC_DIR}/currentaccount.h ${APP_SRC_DIR}/currentaccount.h
${APP_SRC_DIR}/videodevices.h ${APP_SRC_DIR}/videodevices.h
${APP_SRC_DIR}/videoprovider.h ${APP_SRC_DIR}/videoprovider.h

2
daemon

@ -1 +1 @@
Subproject commit 47f3fd14ab0532ff94fd5317f885689928d9293b Subproject commit 3481da56c29e378bf3003d067c06e409d5c52c32

View file

@ -828,6 +828,7 @@ Item {
property string goToConversation: qsTr("Go to conversation") property string goToConversation: qsTr("Go to conversation")
property string promoteAdministrator: qsTr("Promote to administrator") property string promoteAdministrator: qsTr("Promote to administrator")
property string kickMember: qsTr("Kick member") property string kickMember: qsTr("Kick member")
property string reinstateMember: qsTr("Reinstate member")
property string administrator: qsTr("Administrator") property string administrator: qsTr("Administrator")
property string invited: qsTr("Invited") property string invited: qsTr("Invited")
property string removeMember: qsTr("Remove member") property string removeMember: qsTr("Remove member")

View file

@ -24,6 +24,7 @@ CurrentConversation::CurrentConversation(LRCInstance* lrcInstance, QObject* pare
: QObject(parent) : QObject(parent)
, lrcInstance_(lrcInstance) , lrcInstance_(lrcInstance)
{ {
uris_ = new CurrentConversationMembers(lrcInstance, this);
// 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_,
@ -59,7 +60,23 @@ CurrentConversation::updateData()
if (auto optConv = accInfo.conversationModel->getConversationForUid(convId)) { if (auto optConv = accInfo.conversationModel->getConversationForUid(convId)) {
auto& convInfo = optConv->get(); auto& convInfo = optConv->get();
set_lastSelfMessageId(convInfo.lastSelfMessageId); set_lastSelfMessageId(convInfo.lastSelfMessageId);
set_uris(convInfo.participantsUris()); QStringList uris, bannedUris;
auto isAdmin = false;
for (const auto& p : convInfo.participants) {
if (p.uri == accInfo.profileInfo.uri) {
isAdmin = p.role == member::Role::ADMIN;
}
if (p.role == member::Role::BANNED) {
bannedUris.push_back(p.uri);
} else {
uris.push_back(p.uri);
}
}
if (isAdmin) {
for (const auto& banned : bannedUris)
uris.push_back(banned);
}
uris_->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());
@ -170,6 +187,12 @@ 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)
{ {

View file

@ -19,6 +19,7 @@
#pragma once #pragma once
#include "lrcinstance.h" #include "lrcinstance.h"
#include "currentconversationmembers.h"
#include <QObject> #include <QObject>
#include <QString> #include <QString>
@ -32,7 +33,6 @@ class CurrentConversation final : public QObject
QML_PROPERTY(QString, id) QML_PROPERTY(QString, id)
QML_PROPERTY(QString, title) QML_PROPERTY(QString, title)
QML_PROPERTY(QString, description) QML_PROPERTY(QString, description)
QML_PROPERTY(QStringList, uris)
QML_PROPERTY(bool, isSwarm) QML_PROPERTY(bool, isSwarm)
QML_PROPERTY(bool, isLegacy) QML_PROPERTY(bool, isLegacy)
QML_PROPERTY(bool, isCoreDialog) QML_PROPERTY(bool, isCoreDialog)
@ -66,6 +66,7 @@ public:
Q_INVOKABLE QString getPreference(const QString& key) const; Q_INVOKABLE QString getPreference(const QString& key) const;
Q_INVOKABLE MapStringString getPreferences() const; Q_INVOKABLE MapStringString getPreferences() const;
Q_INVOKABLE void setInfo(const QString& key, const QString& value); Q_INVOKABLE void setInfo(const QString& key, const QString& value);
CurrentConversationMembers* uris() const;
Q_SIGNALS: Q_SIGNALS:
void scrollTo(const QString& msgId); void scrollTo(const QString& msgId);
@ -87,6 +88,7 @@ Q_SIGNALS:
private: private:
LRCInstance* lrcInstance_; LRCInstance* lrcInstance_;
CurrentConversationMembers* uris_;
void connectModel(); void connectModel();
}; };

View file

@ -0,0 +1,76 @@
/*
* Copyright (C) 2023 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "currentconversationmembers.h"
#include <algorithm>
#include <random>
CurrentConversationMembers::CurrentConversationMembers(LRCInstance* lrcInstance, QObject* parent)
: QAbstractListModel(parent)
, lrcInstance_(lrcInstance)
{}
int
CurrentConversationMembers::rowCount(const QModelIndex& parent) const
{
if (parent.isValid())
return 0;
return members_.size();
}
void
CurrentConversationMembers::setMembers(const QString& accountId,
const QString& convId,
const QStringList& members)
{
beginResetModel();
accountId_ = accountId;
convId_ = convId;
members_ = members;
set_count(members.size());
endResetModel();
}
QVariant
CurrentConversationMembers::data(const QModelIndex& index, int role) const
{
if (!index.isValid())
return QVariant();
auto member = members_.at(index.row());
switch (role) {
case Members::Role::MemberUri:
return QVariant::fromValue(member);
case Members::Role::MemberRole:
return QVariant::fromValue(
lrcInstance_->getAccountInfo(accountId_).conversationModel->memberRole(convId_, member));
}
return QVariant();
}
QHash<int, QByteArray>
CurrentConversationMembers::roleNames() const
{
using namespace Members;
QHash<int, QByteArray> roles;
#define X(role) roles[role] = #role;
MEMBERS_ROLES
#undef X
return roles;
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (C) 2023 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "lrcinstance.h"
#include "appsettingsmanager.h"
#include "qtutils.h"
#include <QAbstractListModel>
#include <QObject>
#define MEMBERS_ROLES \
X(MemberUri) \
X(MemberRole)
namespace Members {
Q_NAMESPACE
enum Role {
DummyRole = Qt::UserRole + 1,
#define X(role) role,
MEMBERS_ROLES
#undef X
};
Q_ENUM_NS(Role)
} // namespace Members
class CurrentConversationMembers : public QAbstractListModel
{
Q_OBJECT
QML_PROPERTY(int, count)
public:
explicit CurrentConversationMembers(LRCInstance* lrcInstance, QObject* parent = nullptr);
void setMembers(const QString& accountId, const QString& convId, const QStringList& members);
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
private:
LRCInstance* lrcInstance_;
QString accountId_;
QString convId_;
QStringList members_;
};

View file

@ -63,10 +63,9 @@ Rectangle {
model: ContactAdapter.getContactSelectableModel(type) model: ContactAdapter.getContactSelectableModel(type)
Connections { Connections {
enabled: visible target: CurrentConversationMembers
target: CurrentConversation
function onUrisChanged(uris) { function onCountChanged() {
contactPickerListView.model = ContactAdapter.getContactSelectableModel(type) contactPickerListView.model = ContactAdapter.getContactSelectableModel(type)
} }
} }

View file

@ -157,13 +157,6 @@ Rectangle {
Connections { Connections {
target: CurrentConversation target: CurrentConversation
function onUrisChanged(uris) {
if (CurrentConversation.uris.length >= 8 && addMemberPanel.visible) {
swarmDetailsPanel.visible = false
addMemberPanel.visible = !addMemberPanel.visible
}
}
function onNeedsHost() { function onNeedsHost() {
viewCoordinator.presentDialog( viewCoordinator.presentDialog(
appWindow, appWindow,
@ -171,6 +164,17 @@ Rectangle {
} }
} }
Connections {
target: CurrentConversationMembers
function onCountChanged() {
if (CurrentConversationMembers.count >= 8 && addMemberPanel.visible) {
swarmDetailsPanel.visible = false
addMemberPanel.visible = !addMemberPanel.visible
}
}
}
onAddToConversationClicked: { onAddToConversationClicked: {
swarmDetailsPanel.visible = false swarmDetailsPanel.visible = false
if (addMemberPanel.visible) { if (addMemberPanel.visible) {

View file

@ -208,7 +208,7 @@ Rectangle {
normalColor: JamiTheme.chatviewBgColor normalColor: JamiTheme.chatviewBgColor
imageColor: JamiTheme.chatviewButtonColor imageColor: JamiTheme.chatviewButtonColor
visible: CurrentConversation.uris.length < 8 && addMemberVisibility visible: CurrentConversationMembers.count < 8 && addMemberVisibility
onClicked: addToConversationClicked() onClicked: addToConversationClicked()
} }

View file

@ -60,7 +60,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 : CurrentConversation.uris[0] var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversationMembers[0]
pluginhandlerPickerListView.model = PluginAdapter.getChatHandlerSelectableModel(LRCInstance.currentAccountId, peerId) pluginhandlerPickerListView.model = PluginAdapter.getChatHandlerSelectableModel(LRCInstance.currentAccountId, peerId)
} }
} }
@ -72,7 +72,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 : CurrentConversation.uris[0] var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversationMembers[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)
} }
@ -127,7 +127,7 @@ Popup {
if (isCall) { if (isCall) {
return PluginAdapter.getMediaHandlerSelectableModel(CurrentCall.id) return PluginAdapter.getMediaHandlerSelectableModel(CurrentCall.id)
} else { } else {
var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversation.uris[0] var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversationMembers[0]
return PluginAdapter.getChatHandlerSelectableModel(LRCInstance.currentAccountId, peerId) return PluginAdapter.getChatHandlerSelectableModel(LRCInstance.currentAccountId, peerId)
} }
} }

View file

@ -238,7 +238,7 @@ Rectangle {
down: tabBar.currentIndex === 1 down: tabBar.currentIndex === 1
labelText: { labelText: {
var membersNb = CurrentConversation.uris.length; var membersNb = CurrentConversationMembers.count;
if (membersNb > 1) if (membersNb > 1)
return JamiStrings.members.arg(membersNb) return JamiStrings.members.arg(membersNb)
return JamiStrings.member return JamiStrings.member
@ -605,7 +605,7 @@ Rectangle {
} }
} }
model: CurrentConversation.uris model: CurrentConversationMembers
delegate: ItemDelegate { delegate: ItemDelegate {
id: member id: member
@ -626,11 +626,11 @@ Rectangle {
id: memberMouseArea id: memberMouseArea
anchors.fill: parent anchors.fill: parent
enabled: modelData !== CurrentAccount.uri enabled: MemberUri !== CurrentAccount.uri
acceptedButtons: Qt.RightButton acceptedButtons: Qt.RightButton
onClicked: function (mouse) { onClicked: function (mouse) {
var position = mapToItem(members, mouse.x, mouse.y) var position = mapToItem(members, mouse.x, mouse.y)
contextMenu.openMenuAt(position.x, position.y, modelData) contextMenu.openMenuAt(position.x, position.y, MemberUri)
} }
} }
@ -640,19 +640,17 @@ Rectangle {
anchors.rightMargin: JamiTheme.preferredMarginSize anchors.rightMargin: JamiTheme.preferredMarginSize
Avatar { Avatar {
id: avatar
width: JamiTheme.smartListAvatarSize width: JamiTheme.smartListAvatarSize
height: JamiTheme.smartListAvatarSize height: JamiTheme.smartListAvatarSize
Layout.leftMargin: JamiTheme.preferredMarginSize Layout.leftMargin: JamiTheme.preferredMarginSize
Layout.topMargin: JamiTheme.preferredMarginSize / 2 Layout.topMargin: JamiTheme.preferredMarginSize / 2
z: -index z: -index
opacity: { opacity: (MemberRole === Member.Role.INVITED || MemberRole === Member.Role.BANNED)? 0.5 : 1
var role = UtilsAdapter.getParticipantRole(CurrentAccount.id, CurrentConversation.id, modelData)
return role === Member.Role.INVITED ? 0.5 : 1
}
imageId: CurrentAccount.uri == modelData ? CurrentAccount.id : modelData imageId: CurrentAccount.uri == MemberUri ? CurrentAccount.id : MemberUri
showPresenceIndicator: UtilsAdapter.getContactPresence(CurrentAccount.id, modelData) showPresenceIndicator: UtilsAdapter.getContactPresence(CurrentAccount.id, MemberUri)
mode: CurrentAccount.uri == modelData ? Avatar.Mode.Account : Avatar.Mode.Contact mode: CurrentAccount.uri == MemberUri ? Avatar.Mode.Account : Avatar.Mode.Contact
} }
ElidedTextLabel { ElidedTextLabel {
@ -662,16 +660,12 @@ Rectangle {
Layout.topMargin: JamiTheme.preferredMarginSize / 2 Layout.topMargin: JamiTheme.preferredMarginSize / 2
Layout.fillWidth: true Layout.fillWidth: true
eText: UtilsAdapter.getContactBestName(CurrentAccount.id, modelData) eText: UtilsAdapter.getContactBestName(CurrentAccount.id, MemberUri)
maxWidth: width maxWidth: width
font.pointSize: JamiTheme.participantFontSize font.pointSize: JamiTheme.participantFontSize
color: JamiTheme.primaryForegroundColor color: JamiTheme.primaryForegroundColor
opacity: { opacity: (MemberRole === Member.Role.INVITED || MemberRole === Member.Role.BANNED)? 0.5 : 1
var role = UtilsAdapter.getParticipantRole(CurrentAccount.id, CurrentConversation.id, modelData)
return role === Member.Role.INVITED ? 0.5 : 1
}
font.kerning: true font.kerning: true
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
@ -682,27 +676,25 @@ Rectangle {
} }
ElidedTextLabel { ElidedTextLabel {
id: role id: roleLabel
Layout.preferredHeight: JamiTheme.preferredFieldHeight Layout.preferredHeight: JamiTheme.preferredFieldHeight
Layout.topMargin: JamiTheme.preferredMarginSize / 2 Layout.topMargin: JamiTheme.preferredMarginSize / 2
eText: { eText: {
var role = UtilsAdapter.getParticipantRole(CurrentAccount.id, CurrentConversation.id, modelData) if (MemberRole === Member.Role.ADMIN)
if (role === Member.Role.ADMIN)
return JamiStrings.administrator return JamiStrings.administrator
if (role === Member.Role.INVITED) if (MemberRole === Member.Role.INVITED)
return JamiStrings.invited return JamiStrings.invited
if (MemberRole === Member.Role.BANNED)
return JamiStrings.banned
return "" return ""
} }
maxWidth: JamiTheme.preferredFieldWidth maxWidth: JamiTheme.preferredFieldWidth
font.pointSize: JamiTheme.participantFontSize font.pointSize: JamiTheme.participantFontSize
color: JamiTheme.textColorHovered color: JamiTheme.textColorHovered
opacity: { opacity: (MemberRole === Member.Role.INVITED || MemberRole === Member.Role.BANNED)? 0.5 : 1
var role = UtilsAdapter.getParticipantRole(CurrentAccount.id, CurrentConversation.id, modelData)
return role === Member.Role.INVITED ? 0.5 : 1
}
font.kerning: true font.kerning: true
horizontalAlignment: Text.AlignRight horizontalAlignment: Text.AlignRight

View file

@ -74,12 +74,17 @@ ContextMenuAutoLoader {
}, },
GeneralMenuItem { GeneralMenuItem {
id: kickMember id: kickMember
itemName: JamiStrings.kickMember property var memberRole: UtilsAdapter.getParticipantRole(CurrentAccount.id, conversationId, participantUri)
itemName: memberRole === Member.Role.BANNED ? JamiStrings.reinstateMember : JamiStrings.kickMember
iconSource: JamiResources.kick_member_svg iconSource: JamiResources.kick_member_svg
canTrigger: role === Member.Role.ADMIN canTrigger: role === Member.Role.ADMIN
onClicked: { onClicked: {
MessagesAdapter.removeConversationMember(conversationId, participantUri) if (memberRole === Member.Role.BANNED) {
MessagesAdapter.addConversationMember(conversationId, participantUri)
} else {
MessagesAdapter.removeConversationMember(conversationId, participantUri)
}
} }
} }
] ]

View file

@ -516,6 +516,13 @@ MessagesAdapter::removeConversationMember(const QString& convUid, const QString&
accInfo.conversationModel->removeConversationMember(convUid, memberUri); accInfo.conversationModel->removeConversationMember(convUid, memberUri);
} }
void
MessagesAdapter::addConversationMember(const QString& convUid, const QString& memberUri)
{
auto& accInfo = lrcInstance_->getCurrentAccountInfo();
accInfo.conversationModel->addConversationMember(convUid, memberUri);
}
void void
MessagesAdapter::removeContact(const QString& convUid, bool banContact) MessagesAdapter::removeContact(const QString& convUid, bool banContact)
{ {

View file

@ -85,6 +85,7 @@ protected:
Q_INVOKABLE void connectConversationModel(); Q_INVOKABLE void connectConversationModel();
Q_INVOKABLE void sendConversationRequest(); Q_INVOKABLE void sendConversationRequest();
Q_INVOKABLE void removeConversation(const QString& convUid); Q_INVOKABLE void removeConversation(const QString& convUid);
Q_INVOKABLE void addConversationMember(const QString& convUid, const QString& participantUri);
Q_INVOKABLE void removeConversationMember(const QString& convUid, const QString& participantUri); Q_INVOKABLE void removeConversationMember(const QString& convUid, const QString& participantUri);
Q_INVOKABLE void removeContact(const QString& convUid, bool banContact = false); Q_INVOKABLE void removeContact(const QString& convUid, bool banContact = false);
Q_INVOKABLE void clearConversationHistory(const QString& accountId, const QString& convUid); Q_INVOKABLE void clearConversationHistory(const QString& accountId, const QString& convUid);

View file

@ -141,6 +141,7 @@ registerTypes(QQmlEngine* engine,
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, pluginAdapter, "PluginAdapter"); QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, pluginAdapter, "PluginAdapter");
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentCall, "CurrentCall"); QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentCall, "CurrentCall");
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentConversation, "CurrentConversation"); QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentConversation, "CurrentConversation");
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentConversation->uris(), "CurrentConversationMembers");
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentAccount, "CurrentAccount"); QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentAccount, "CurrentAccount");
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, videoDevices, "VideoDevices"); QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, videoDevices, "VideoDevices");
QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentAccountToMigrate, "CurrentAccountToMigrate") QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentAccountToMigrate, "CurrentAccountToMigrate")