1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-03-28 14:56:19 +01:00

PluginStore: add view for plugin store

Gitlab: #1163
Change-Id: If9d9a27a296c5810b9f99126bed6453cc6ab6852
This commit is contained in:
Xavier Jouslin de Noray 2023-06-28 10:47:36 -04:00 committed by Sébastien Blin
parent 7581f9397a
commit 5530649f07
30 changed files with 1066 additions and 605 deletions

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 428 428" style="enable-background:new 0 0 428 428;" xml:space="preserve">
<style type="text/css">
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_1_);}
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_2_);}
.st2{fill:url(#SVGID_3_);}
.st3{fill:url(#SVGID_4_);}
</style>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-12.6549" y1="165.4979" x2="165.4206" y2="343.5734">
<stop offset="0" style="stop-color:#3A3A3A"/>
<stop offset="1" style="stop-color:#818181"/>
</linearGradient>
<path class="st0" d="M1.6,154v21.5v1.4c0.2,0,0.4,0,0.6,0c21.8,0,39.5,17.7,39.5,39.5c0,21.8-17.7,39.5-39.5,39.5
c-0.2,0-0.4,0-0.6,0V408H101V152c0.2,0.1,0.3,0.1,0.5,0.2V51.4L1.6,154z"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="227.3426" y1="84.8295" x2="405.4313" y2="262.9182">
<stop offset="0" style="stop-color:#3A3A3A"/>
<stop offset="1" style="stop-color:#717171"/>
</linearGradient>
<path class="st1" d="M386.9,176.8L386.9,176.8l0-157.8h-93.7v256c-0.1-0.1-0.3-0.1-0.4-0.2v100.8l94.2-96.7v-23.2h0
c21.8,0,39.5-17.7,39.5-39.5C426.4,194.5,408.7,176.8,386.9,176.8z"/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="1.6097" y1="86.4966" x2="395.2845" y2="86.4966">
<stop offset="0" style="stop-color:#818181"/>
<stop offset="1" style="stop-color:#3A3A3A"/>
</linearGradient>
<path class="st2" d="M181.3,19C126.4,19,1.6,31.5,1.6,154c0,0,49.3-42.7,136.9-25s160.9-0.7,207.6-49.1
c18.6-19.3,32.4-39.6,40.2-60.9H181.3z"/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-553.2536" y1="-1072.2665" x2="-159.5788" y2="-1072.2665" gradientTransform="matrix(-1 0 0 -1 -166.3346 -728.8083)">
<stop offset="0" style="stop-color:#818181"/>
<stop offset="1" style="stop-color:#3A3A3A"/>
</linearGradient>
<path class="st3" d="M207.2,408c54.9,0,179.7-11.9,179.7-129.1c0,0-49.3,40.8-136.9,23.9c-87.6-16.9-160.9,0.7-207.6,47
C23.8,368.3,10.1,387.6,2.2,408H207.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,75 @@
/*
* Copyright (C) 2022-2023 Savoir-faire Linux Inc.
* Author: Xavier Jouslin <xavier.jouslindenoray@savoirfairelinux.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt.labs.platform
import net.jami.Models 1.1
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
RowLayout {
id: root
property string labelText: ""
property int widthOfSwitch: 50
property int heightOfSwitch: 10
property string tooltipText: ""
property alias toggleSwitch: autoupdate
property alias checked: autoupdate.checked
signal switchToggled
Layout.alignment: Qt.AlignRight
JamiSwitch {
id: autoupdate
Layout.alignment: Qt.AlignLeft
Layout.preferredWidth: widthOfSwitch
hoverEnabled: true
toolTipText: tooltipText
Accessible.role: Accessible.Button
Accessible.name: JamiStrings.autoUpdate
Accessible.description: root.tooltipText
onToggled: switchToggled()
}
Text {
id: description
Layout.rightMargin: JamiTheme.preferredMarginSize
text: JamiStrings.autoUpdate
font.pixelSize: JamiTheme.wizardViewButtonFontPixelSize
visible: labelText !== ""
font.kerning: true
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
color: JamiTheme.textColor
}
TapHandler {
target: parent
enabled: parent.visible
onTapped: function onTapped(eventPoint) {
// switchToggled should be emitted as onToggled is not called (because it's only called if the user click on the switch)
autoupdate.toggle();
switchToggled();
}
}
}

View file

@ -68,6 +68,7 @@ Item {
antialiasing: true
asynchronous: true
visible: false
mipmap: true
function setSourceSize() {
sourceSize = undefined;

View file

@ -32,9 +32,8 @@ Item {
property int spinningAnimationWidth: 4
property real outerCutRadius: root.height / 2
property int spinningAnimationDuration: 1000
property color color: "white"
visible: mode !== SpinningAnimation.Mode.Disabled
ConicalGradient {
id: conicalGradientOne
@ -48,7 +47,7 @@ Item {
}
GradientStop {
position: 1.0
color: "white"
color: mode === SpinningAnimation.Mode.Disabled ? "transparent" : root.color
}
}
@ -90,7 +89,7 @@ Item {
}
GradientStop {
position: 1.0
color: "white"
color: mode === SpinningAnimation.Mode.Disabled ? "transparent" : root.color
}
}

View file

@ -166,7 +166,7 @@ Item {
property string back: qsTr("Back")
property string accountSettingsMenuTitle: qsTr("Account")
property string generalSettingsTitle: qsTr("General")
property string pluginSettingsTitle: qsTr("Plugins")
property string pluginSettingsTitle: qsTr("Extensions")
property string enableAccountSettingsTitle: qsTr("Enable account")
property string manageAccountSettingsTitle: qsTr("Manage account")
property string linkedDevicesSettingsTitle: qsTr("Linked devices")
@ -659,9 +659,19 @@ Item {
// Plugins
property string enable: qsTr("Enable")
property string pluginPreferences: qsTr("Preferences")
property string reset: qsTr("Reset")
property string autoUpdate: qsTr("Auto update")
property string disableAll: qsTr("Disable all")
property string installed: qsTr("Installed")
property string install: qsTr("Install")
property string installing: qsTr("Installing")
property string installManually: qsTr("Install manually")
property string installMannuallyDescription: qsTr("Install an extension directly from your device.")
property string pluginStoreTitle: qsTr("Available")
property string pluginStoreNotAvailable: qsTr("Plugins store is not available")
property string pluginPreferences: qsTr("Preferences")
property string installationFailed: qsTr("Installation failed")
property string pluginInstallationFailed: qsTr("The installation of the plugin failed")
property string reset: qsTr("Reset")
property string uninstall: qsTr("Uninstall")
property string resetPreferences: qsTr("Reset Preferences")
property string selectPluginInstall: qsTr("Select a plugin to install")
@ -677,7 +687,6 @@ Item {
property string chooseImageFile: qsTr("Choose image file")
property string tipGeneralPluginSettingsDisplay: qsTr("Display or hide General plugin settings")
property string tipAccountPluginSettingsDisplay: qsTr("Display or hide Account plugin settings")
property string installedPlugins: qsTr("Installed plugins")
property string pluginFiles: qsTr("Plugin Files (*.jpl)")
property string loadUnload: qsTr("Load/Unload")
property string selectAnImage: qsTr("Select An Image to %1")

View file

@ -383,6 +383,10 @@ Item {
property real minimumMapWidth: 250
property real pluginHandlersPopupViewHeight: 200
property real pluginHandlersPopupViewDelegateHeight: 50
property color pluginDefaultBackgroundColor: "#666666"
property real remotePluginWidthDelegate: 350
property real remotePluginHeightDelegate: 400
property color pluginViewBackgroundColor: darkTheme ? "#000000" : "#F0EFEF"
property real secondaryDialogDimension: 500
property real lineEditContextMenuItemsHeight: 15

View file

@ -42,15 +42,11 @@ Item {
antialiasing: true
property bool isGif: getIsGif(this)
Image {
id: default_img
objectName: "default_img"
anchors.fill: parent
source: defaultImage
visible: image.status != Image.Ready
smooth: true
antialiasing: true
property bool isGif: getIsGif(this)
source: defaultImage
onStatusChanged: {
if (status === Image.Error) {
source = defaultImage;
}
}
}
@ -70,13 +66,6 @@ Item {
function onDownloadImageSuccessful(localPath) {
if (localPath === cachedImage.localPath) {
image.source = "file://" + localPath;
print("onDownloadImageSuccessful", localPath);
}
}
function onDownloadImageFailed(localPath) {
if (localPath === cachedImage.localPath) {
print("Failed to download image: " + downloadUrl);
image.source = defaultImage;
}
}
}
@ -99,7 +88,6 @@ Item {
}
if (downloadUrl && downloadUrl !== "" && localPath !== "") {
if (!UtilsAdapter.fileExists(localPath)) {
print("ImageDownloader.downloadImage", downloadUrl, localPath);
ImageDownloader.downloadImage(downloadUrl, localPath);
} else {
image.source = "file://" + localPath;

View file

@ -18,9 +18,13 @@
#include "pluginadapter.h"
#include "pluginversionmanager.h"
#include "pluginlistmodel.h"
#include "pluginstorelistmodel.h"
#include "networkmanager.h"
#include "lrcinstance.h"
#include "utilsadapter.h"
#include "qmlregister.h"
#include <QJsonArray>
#include <QJsonDocument>
@ -30,14 +34,16 @@
PluginAdapter::PluginAdapter(LRCInstance* instance, QObject* parent, QString baseUrl)
: QmlAdapterBase(instance, parent)
, pluginStoreListModel_(new PluginStoreListModel(this))
, pluginStoreListModel_(new PluginStoreListModel(instance, this))
, pluginVersionManager_(new PluginVersionManager(instance, baseUrl, this))
, pluginListModel_(new PluginListModel(this))
, pluginListModel_(new PluginListModel(instance, this))
, lrcInstance_(instance)
, tempPath_(QDir::tempPath())
, baseUrl_(baseUrl)
{
QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, pluginStoreListModel_, "PluginStoreListModel");
QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, pluginListModel_, "PluginListModel")
set_isEnabled(lrcInstance_->pluginModel().getPluginsEnabled());
updateHandlersListCount();
connect(&lrcInstance_->pluginModel(),
@ -75,24 +81,34 @@ PluginAdapter::PluginAdapter(LRCInstance* instance, QObject* parent, QString bas
void
PluginAdapter::getPluginsFromStore()
{
pluginVersionManager_->sendGetRequest(QUrl(baseUrl_), [this](const QByteArray& data) {
auto result = QJsonDocument::fromJson(data).array();
auto pluginsInstalled = lrcInstance_->pluginModel().getPluginsId();
QList<QVariantMap> plugins;
for (const auto& plugin : result) {
auto qPlugin = plugin.toVariant().toMap();
if (!pluginsInstalled.contains(qPlugin["id"].toString())) {
plugins.append(qPlugin);
}
}
pluginStoreListModel_->setPlugins(plugins);
});
const auto& errorHandler = connect(pluginVersionManager_,
&PluginVersionManager::errorOccurred,
this,
[this](NetworkManager::GetError error, const QString& msg) {
Q_EMIT storeNotAvailable();
});
pluginVersionManager_
->sendGetRequest(QUrl(baseUrl_ + "?arch=" + Utils::getPlatformString()),
[this, errorHandler](const QByteArray& data) {
auto result = QJsonDocument::fromJson(data).array();
auto pluginsInstalled = lrcInstance_->pluginModel().getPluginsId();
QList<QVariantMap> plugins;
for (const auto& plugin : result) {
auto qPlugin = plugin.toVariant().toMap();
if (!pluginsInstalled.contains(qPlugin["name"].toString())) {
plugins.append(qPlugin);
}
}
pluginStoreListModel_->setPlugins(plugins);
disconnect(errorHandler);
});
}
void
PluginAdapter::getPluginDetails(const QString& pluginId)
{
pluginVersionManager_->sendGetRequest(QUrl(baseUrl_ + "/details/" + pluginId),
pluginVersionManager_->sendGetRequest(QUrl(baseUrl_ + "/details/" + pluginId
+ "?arch=" + Utils::getPlatformString()),
[this](const QByteArray& data) {
auto result = QJsonDocument::fromJson(data).object();
// my response is a json object and I want to convert
@ -114,6 +130,18 @@ PluginAdapter::isAutoUpdaterEnabled()
return pluginVersionManager_->isAutoUpdaterEnabled();
}
void
PluginAdapter::setAutoUpdate(bool state)
{
pluginVersionManager_->setAutoUpdate(state);
}
void
PluginAdapter::cancelDownload(const QString& pluginId)
{
pluginVersionManager_->cancelUpdate(pluginId);
}
QVariant
PluginAdapter::getMediaHandlerSelectableModel(const QString& callId)
{
@ -169,3 +197,9 @@ PluginAdapter::baseUrl() const
{
return baseUrl_;
}
QString
PluginAdapter::getIconUrl(const QString& pluginId) const
{
return baseUrl_ + "/icons/" + pluginId + "?arch=" + Utils::getPlatformString();
}

View file

@ -30,6 +30,9 @@
#include <QSortFilterProxyModel>
#include <QString>
class PluginVersionManager;
class PluginStoreListModel;
class PluginAdapter final : public QmlAdapterBase
{
Q_OBJECT
@ -49,6 +52,9 @@ public:
Q_INVOKABLE QString baseUrl() const;
Q_INVOKABLE void checkVersionStatus(const QString& pluginId);
Q_INVOKABLE bool isAutoUpdaterEnabled();
Q_INVOKABLE void cancelDownload(const QString& pluginId);
Q_INVOKABLE void setAutoUpdate(bool state);
Q_INVOKABLE QString getIconUrl(const QString& pluginId) const;
protected:
Q_INVOKABLE QVariant getMediaHandlerSelectableModel(const QString& callId);
@ -57,14 +63,18 @@ protected:
Q_INVOKABLE QVariant getPluginPreferencesCategories(const QString& pluginId,
const QString& accountId,
bool removeLast = false);
Q_SIGNALS:
void storeNotAvailable();
private:
void updateHandlersListCount();
std::unique_ptr<PluginHandlerListModel> pluginHandlerListModel_;
PluginStoreListModel* pluginStoreListModel_;
PluginVersionManager* pluginVersionManager_;
PluginListModel* pluginListModel_;
std::unique_ptr<PluginHandlerListModel> pluginHandlerListModel_;
LRCInstance* lrcInstance_;
std::mutex mtx_;
QString tempPath_;

View file

@ -22,9 +22,12 @@
#include "api/pluginmodel.h"
PluginListModel::PluginListModel(QObject* parent)
PluginListModel::PluginListModel(LRCInstance* lrcInstance, QObject* parent)
: AbstractListModelBase(parent)
{}
, lrcInstance_(lrcInstance)
{
reset();
}
PluginListModel::~PluginListModel() {}
@ -55,16 +58,20 @@ PluginListModel::data(const QModelIndex& index, int role) const
}
auto details = lrcInstance_->pluginModel().getPluginDetails(installedPlugins_.at(index.row()));
installedPlugins_.at(index.row());
switch (role) {
case Role::PluginName:
return QVariant(details.name);
case Role::PluginDescription:
return QVariant(details.description);
case Role::PluginId:
return QVariant(installedPlugins_.at(index.row()));
case Role::PluginIcon:
return QVariant(details.iconPath);
case Role::IsLoaded:
return QVariant(details.loaded);
case Role::Status:
return QVariant(pluginStatus_.value(installedPlugins_.at(index.row())));
}
return QVariant();
}
@ -77,7 +84,8 @@ PluginListModel::roleNames() const
roles[PluginId] = "PluginId";
roles[PluginIcon] = "PluginIcon";
roles[IsLoaded] = "IsLoaded";
roles[Status] = "Status";
roles[PluginDescription] = "PluginDescription";
return roles;
}
@ -87,6 +95,9 @@ PluginListModel::reset()
beginResetModel();
installedPlugins_.clear();
installedPlugins_ = lrcInstance_->pluginModel().getInstalledPlugins();
for (auto plugin : installedPlugins_) {
pluginStatus_[plugin] = PluginStatus::INSTALLED;
}
filterPlugins(installedPlugins_);
endResetModel();
}
@ -97,6 +108,7 @@ PluginListModel::removePlugin(int index)
beginRemoveRows(QModelIndex(), index, index);
installedPlugins_.removeAt(index);
endRemoveRows();
reset();
}
void
@ -120,9 +132,18 @@ PluginListModel::addPlugin()
index++;
}
beginInsertRows(QModelIndex(), index, index);
installedPlugins_ = newList;
endInsertRows();
reset();
}
void
PluginListModel::disableAllPlugins()
{
for (auto& plugin : installedPlugins_) {
auto& pluginModel = lrcInstance_->pluginModel();
const auto& details = pluginModel.getPluginDetails(plugin);
pluginModel.unloadPlugin(details.path);
disabled(details.path);
}
}
void
@ -165,14 +186,11 @@ PluginListModel::onVersionStatusChanged(const QString& pluginId, PluginStatus::R
if (pluginIndex == -1) {
return;
}
pluginStatus_[pluginId] = status;
switch (status) {
case PluginStatus::INSTALLABLE:
removePlugin(pluginIndex);
break;
case PluginStatus::FAILED:
qWarning() << "Failed to install plugin" << pluginId;
break;
default:
break;
}

View file

@ -28,10 +28,17 @@ class PluginListModel : public AbstractListModelBase
Q_OBJECT
QML_PROPERTY(bool, filterAccount)
public:
enum Role { PluginName = Qt::UserRole + 1, PluginId, PluginIcon, IsLoaded };
enum Role {
PluginName = Qt::UserRole + 1,
PluginDescription,
PluginId,
PluginIcon,
IsLoaded,
Status
};
Q_ENUM(Role)
explicit PluginListModel(QObject* parent = nullptr);
explicit PluginListModel(LRCInstance* lrcInstance, QObject* parent = nullptr);
~PluginListModel();
/*
@ -52,16 +59,19 @@ public:
Q_INVOKABLE void removePlugin(int index);
Q_INVOKABLE void pluginChanged(int index);
Q_INVOKABLE void addPlugin();
Q_INVOKABLE void disableAllPlugins();
Q_SIGNALS:
void versionCheckRequested(const QString& pluginId);
void setVersionStatus(const QString& pluginId, PluginStatus::Role status);
void autoUpdateChanged(bool state);
void disabled(const QString& pluginId);
public Q_SLOTS:
void onVersionStatusChanged(const QString& pluginId, PluginStatus::Role status);
private:
LRCInstance* lrcInstance_ = nullptr;
void filterPlugins(VectorString& list) const;
VectorString installedPlugins_ {};
QMap<QString, PluginStatus::Role> pluginStatus_ {};
};

View file

@ -17,10 +17,17 @@
#include "pluginstorelistmodel.h"
#include "lrcinstance.h"
#include "api/pluginmodel.h"
#include <QUrl>
PluginStoreListModel::PluginStoreListModel(QObject* parent)
#include <algorithm>
PluginStoreListModel::PluginStoreListModel(LRCInstance* lrcInstance, QObject* parent)
: AbstractListModelBase(parent)
, lrcInstance_(lrcInstance)
{}
int
@ -41,20 +48,16 @@ PluginStoreListModel::data(const QModelIndex& index, int role) const
}
auto plugin = plugins_.at(index.row());
switch (role) {
case Role::Id:
return QVariant(plugin["id"].toString());
case Role::Title:
case Role::Name:
return QVariant(plugin["name"].toString());
case Role::IconPath:
return QVariant(plugin["iconPath"].toString());
case Role::Background:
return QVariant(plugin["background"].toString());
case Role::Description:
return QVariant(plugin["description"].toString());
case Role::Author:
return QVariant(plugin["author"].toString());
case Role::Status:
return QVariant(plugin.value("status", PluginStatus::INSTALLABLE).toString());
return QVariant(plugin.value("status", PluginStatus::INSTALLABLE));
}
return QVariant();
}
@ -84,14 +87,16 @@ PluginStoreListModel::addPlugin(const QVariantMap& plugin)
beginInsertRows(QModelIndex(), plugins_.size(), plugins_.size());
plugins_.append(plugin);
endInsertRows();
sort();
}
void
PluginStoreListModel::setPlugins(const QList<QVariantMap>& plugins)
{
beginResetModel();
plugins_ = plugins;
plugins_ = filterPlugins(plugins);
endResetModel();
sort();
}
void
@ -99,7 +104,7 @@ PluginStoreListModel::removePlugin(const QString& pluginId)
{
auto index = 0;
for (auto& plugin : plugins_) {
if (plugin["id"].toString() == pluginId) {
if (plugin["name"].toString() == pluginId) {
beginRemoveRows(QModelIndex(), index, index);
plugins_.removeAt(index);
endRemoveRows();
@ -107,6 +112,7 @@ PluginStoreListModel::removePlugin(const QString& pluginId)
}
index++;
}
sort();
}
void
@ -114,13 +120,14 @@ PluginStoreListModel::updatePlugin(const QVariantMap& plugin)
{
auto index = 0;
for (auto& p : plugins_) {
if (p["id"].toString() == plugin["id"].toString()) {
if (p["name"].toString() == plugin["name"].toString()) {
p = plugin;
Q_EMIT dataChanged(createIndex(index, 0), createIndex(index, 0));
return;
}
index++;
}
sort();
}
QColor
@ -151,7 +158,7 @@ PluginStoreListModel::computeAverageColorOfImage(const QString& file)
blue += pixelColor.blue();
}
}
return QColor(red / nPixels, green / nPixels, blue / nPixels, 70);
return QColor(red / nPixels, green / nPixels, blue / nPixels);
} else {
// Return an invalid color.
return QColor();
@ -161,36 +168,80 @@ PluginStoreListModel::computeAverageColorOfImage(const QString& file)
void
PluginStoreListModel::onVersionStatusChanged(const QString& pluginId, PluginStatus::Role status)
{
auto plugin = QVariantMap();
for (auto& p : plugins_) {
if (p["id"].toString() == pluginId) {
plugin = p;
break;
}
}
auto it = std::find_if(plugins_.begin(), plugins_.end(), [&pluginId](const QVariantMap& p) {
return p["name"].toString() == pluginId;
});
switch (status) {
case PluginStatus::INSTALLABLE:
if (!plugin.isEmpty())
if (it != plugins_.end()) {
break;
}
pluginAdded(pluginId);
break;
default:
break;
}
if (plugin.isEmpty()) {
if (it == plugins_.end()) {
return;
}
plugin["status"] = status;
auto& plugin = *it;
plugin["status"] = status;
auto index = createIndex(rowFromPluginId(pluginId), 0);
if (index.isValid()) {
Q_EMIT dataChanged(index, index, {PluginStoreList::Role::Status});
}
switch (status) {
case PluginStatus::INSTALLED:
removePlugin(pluginId);
break;
case PluginStatus::FAILED:
qWarning() << "Failed to install plugin" << pluginId;
break;
default:
break;
}
}
}
int
PluginStoreListModel::rowFromPluginId(const QString& pluginId) const
{
const auto it = std::find_if(plugins_.begin(),
plugins_.end(),
[&pluginId](const QVariantMap& p) {
return p["name"].toString() == pluginId;
});
if (it != plugins_.end()) {
return std::distance(plugins_.begin(), it);
}
return -1;
}
void
PluginStoreListModel::sort()
{
beginResetModel();
std::sort(plugins_.begin(), plugins_.end(), [](const QVariantMap& a, const QVariantMap& b) {
return a["timestamp"].toString() < b["timestamp"].toString();
});
endResetModel();
}
QList<QVariantMap>
PluginStoreListModel::filterPlugins(const QList<QVariantMap>& plugins)
{
auto& pluginModel = lrcInstance_->pluginModel();
auto installedPlugins = pluginModel.getInstalledPlugins();
QList<QVariantMap> filterPluginsNotInstalled;
for (auto& remotePlugin : plugins) {
if (std::find_if(installedPlugins.begin(),
installedPlugins.end(),
[remotePlugin, &pluginModel, this](const QString& installedPlugin) {
const auto& details = pluginModel.getPluginDetails(installedPlugin);
return remotePlugin["name"].toString() == details.name;
})
== installedPlugins.end()) {
filterPluginsNotInstalled.append(remotePlugin);
}
}
return filterPluginsNotInstalled;
}

View file

@ -24,8 +24,7 @@ class QColor;
class QString;
#define PLUGINSTORE_ROLES \
X(Id) \
X(Title) \
X(Name) \
X(IconPath) \
X(Background) \
X(Description) \
@ -48,7 +47,7 @@ class PluginStoreListModel : public AbstractListModelBase
Q_OBJECT
public:
explicit PluginStoreListModel(QObject* parent = nullptr);
explicit PluginStoreListModel(LRCInstance* lrcInstance, QObject* parent = nullptr);
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
@ -69,6 +68,10 @@ public Q_SLOTS:
void onVersionStatusChanged(const QString& pluginId, PluginStatus::Role status);
private:
QList<QVariantMap> filterPlugins(const QList<QVariantMap>& plugins);
int rowFromPluginId(const QString& pluginId) const;
void sort();
using Role = PluginStoreList::Role;
QList<QVariantMap> plugins_;
LRCInstance* lrcInstance_ {};
};

View file

@ -45,7 +45,6 @@ public:
return;
}
for (const auto& pluginId : qAsConst(pluginsId)) {
Q_EMIT parent_.versionStatusChanged(pluginId, PluginStatus::Role::INSTALLING);
parent_.pluginRepliesId.remove(pluginId);
}
});
@ -74,6 +73,7 @@ public:
return;
}
parent_.cancelDownload(parent_.pluginRepliesId[pluginId]);
parent_.versionStatusChanged(pluginId, PluginStatus::Role::INSTALLABLE);
};
bool isAutoUpdaterEnabled()
@ -103,7 +103,8 @@ public:
return;
}
parent_.sendGetRequest(QUrl(parent_.baseUrl + "/versions/" + plugin.id),
parent_.sendGetRequest(QUrl(parent_.baseUrl + "/versions/" + plugin.id
+ "?arch=" + Utils::getPlatformString()),
[this, plugin](const QByteArray& data) {
// `data` represents the version in this case.
if (plugin.version < data) {
@ -120,7 +121,7 @@ public:
void installRemotePlugin(const QString& pluginId)
{
parent_.downloadFile(
QUrl(parent_.baseUrl + "/download/" + pluginId),
QUrl(parent_.baseUrl + "/download/" + Utils::getPlatformString() + "/" + pluginId),
pluginId,
0,
[this, pluginId](bool success, const QString& error) {
@ -129,14 +130,17 @@ public:
parent_.versionStatusChanged(pluginId, PluginStatus::Role::FAILED);
return;
}
auto res = lrcInstance_->pluginModel().installPlugin(QDir(tempPath_).filePath(
pluginId + ".jpl"),
true);
if (res) {
parent_.versionStatusChanged(pluginId, PluginStatus::Role::INSTALLED);
} else {
parent_.versionStatusChanged(pluginId, PluginStatus::Role::FAILED);
}
QThreadPool::globalInstance()->start([this, pluginId] {
auto res = lrcInstance_->pluginModel().installPlugin(QDir(tempPath_).filePath(
pluginId + ".jpl"),
true);
if (res) {
parent_.versionStatusChanged(pluginId, PluginStatus::Role::INSTALLED);
} else {
parent_.versionStatusChanged(pluginId, PluginStatus::Role::FAILED);
}
});
parent_.versionStatusChanged(pluginId, PluginStatus::Role::INSTALLING);
},
tempPath_ + '/');
Q_EMIT parent_.versionStatusChanged(pluginId, PluginStatus::Role::DOWNLOADING);
@ -167,7 +171,7 @@ PluginVersionManager::PluginVersionManager(LRCInstance* instance, QString& baseU
PluginVersionManager::~PluginVersionManager()
{
for (const auto& pluginReplyId : qAsConst(pluginRepliesId)) {
for (const auto& pluginReplyId : pluginRepliesId.values()) {
cancelDownload(pluginReplyId);
}
pluginRepliesId.clear();

View file

@ -72,7 +72,7 @@ Q_SIGNALS:
private:
QString baseUrl;
bool autoUpdateCheck = false;
QMap<QString, unsigned int> pluginRepliesId {};
QMap<QString, int> pluginRepliesId {};
struct Impl;
friend struct Impl;
std::unique_ptr<Impl> pimpl_;

View file

@ -174,7 +174,6 @@ registerTypes(QQmlEngine* engine,
QML_REGISTERTYPE(NS_MODELS, MediaCodecListModel);
QML_REGISTERTYPE(NS_MODELS, AudioDeviceModel);
QML_REGISTERTYPE(NS_MODELS, AudioManagerListModel);
QML_REGISTERTYPE(NS_MODELS, PluginListModel);
QML_REGISTERTYPE(NS_MODELS, PreferenceItemListModel);
QML_REGISTERTYPE(NS_MODELS, PluginListPreferenceModel);
QML_REGISTERTYPE(NS_MODELS, FilesToSendListModel);

View file

@ -59,14 +59,14 @@ ListSelectionView {
leftPaneItem: viewCoordinator.getView("SettingsSidePanel")
Component.onCompleted: {
leftPaneItem.updateModel()
leftPaneItem.updateModel();
}
Connections {
target: viewNode
function onIsSinglePaneChanged() {
leftPaneItem.isSinglePane = viewNode.isSinglePane
leftPaneItem.isSinglePane = viewNode.isSinglePane;
}
}

View file

@ -0,0 +1,78 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt.labs.platform
import net.jami.Models 1.1
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
import "../../commoncomponents"
ColumnLayout {
function installPlugin() {
var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/JamiFileDialog.qml", {
"title": JamiStrings.selectPluginInstall,
"fileMode": JamiFileDialog.OpenFile,
"folder": StandardPaths.writableLocation(StandardPaths.DownloadLocation),
"nameFilters": [JamiStrings.pluginFiles, JamiStrings.allFiles]
});
dlg.fileAccepted.connect(function (file) {
var url = UtilsAdapter.getAbsPath(file.toString());
var isInstall = PluginModel.installPlugin(url, true);
if (isInstall) {
PluginListModel.addPlugin();
} else {
presentErrorMessage();
}
});
}
function presentErrorMessage() {
viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
"title": JamiStrings.installationFailed,
"infoText": JamiStrings.pluginInstallationFailed,
"buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue],
"buttonTitles": [JamiStrings.optionOk],
"buttonCallBacks": []
});
}
Label {
Layout.fillWidth: true
Layout.bottomMargin: 20
text: JamiStrings.installManually
font.pixelSize: JamiTheme.settingsTitlePixelSize
font.kerning: true
color: JamiTheme.textColor
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
}
Text {
id: descriptionInstallManually
Layout.fillWidth: true
font.pixelSize: JamiTheme.popuptextSize
color: JamiTheme.textColor
text: JamiStrings.installMannuallyDescription
wrapMode: Text.WordWrap
horizontalAlignment: Qt.AlignLeft
lineHeight: 1.5
textFormat: Text.PlainText
}
MaterialButton {
id: installManually
radius: JamiTheme.chatViewHeaderButtonRadius
TextMetrics {
id: textSize
font.weight: Font.Black
font.pixelSize: JamiTheme.wizardViewButtonFontPixelSize
font.capitalization: Font.AllUppercase
text: installManually.text
}
primary: true
preferredWidth: textSize.width + 2 * JamiTheme.buttontextWizzardPadding
text: JamiStrings.install
fontSize: JamiTheme.popuptextSize
onClicked: installPlugin()
}
}

View file

@ -27,114 +27,195 @@ import "../../mainview/components"
ItemDelegate {
id: root
property string pluginId
property string pluginTitle
property string pluginName
property string pluginIcon
property string pluginBackground
property string pluginBackground: JamiTheme.pluginDefaultBackgroundColor
property string pluginDescription
property string pluginAuthor
property string pluginShortDescription
property int pluginStatus
property string installButtonStatus: {
switch (pluginStatus) {
case PluginStatus.DOWNLOADING:
return JamiStrings.cancel;
case PluginStatus.INSTALLABLE:
return JamiStrings.install;
case PluginStatus.INSTALLING:
return JamiStrings.installing;
default:
return JamiStrings.install;
}
}
onPluginStatusChanged: {
if (pluginStatus === PluginStatus.FAILED) {
presentErrorMessage();
}
}
background: null
function presentErrorMessage() {
viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
"title": JamiStrings.installationFailed,
"infoText": JamiStrings.pluginInstallationFailed,
"buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue],
"buttonTitles": [JamiStrings.optionOk],
"buttonCallBacks": []
});
}
Rectangle {
id: rect
Scaffold {
}
color: Qt.rgba(0, 0, 0, 1)
id: mask
anchors.fill: parent
radius: 15
radius: 5
}
Page {
id: plugin
anchors.fill: parent
layer {
enabled: true
effect: OpacityMask {
maskSource: mask
}
}
header: Control {
padding: 10
leftPadding: 20
rightPadding: 5
bottomPadding: 20
topPadding: 5
background: Rectangle {
color: pluginBackground
id: headerBackground
color: hovered ? Qt.lighter(pluginBackground, 1.9) : Qt.lighter(pluginBackground, 2)
}
contentItem: ColumnLayout {
RowLayout {
SpinningAnimation {
id: buttonContainer
visible: true
Layout.alignment: Qt.AlignTop | Qt.AlignRight
Layout.rightMargin: 8
Layout.topMargin: 2
Layout.preferredHeight: install.height
Layout.preferredWidth: install.width
color: "black"
outerCutRadius: install.radius
spinningAnimationDuration: 5000
mode: {
if (pluginStatus === PluginStatus.INSTALLABLE || pluginStatus === PluginStatus.FAILED) {
SpinningAnimation.Mode.Disabled;
} else {
SpinningAnimation.Mode.Radial;
}
}
MaterialButton {
id: install
Layout.alignment: Qt.AlignRight
Layout.rightMargin: 8
Layout.topMargin: 8
Layout.preferredHeight: 20
hoverEnabled: pluginStatus !== PluginStatus.INSTALLING
secHoveredColor: Qt.darker(headerBackground.color, 1.1)
buttontextHeightMargin: 10.0
radius: JamiTheme.chatViewHeaderButtonRadius
TextMetrics {
id: installTextSize
font.weight: Font.Black
font.pixelSize: JamiTheme.wizardViewButtonFontPixelSize
font.capitalization: Font.Medium
text: isDownloading() ? JamiStrings.cancel : JamiStrings.install
text: install.text
}
contentColorProvider: "black"
onClicked: installPlugin()
secondary: true
preferredWidth: installTextSize.width + JamiTheme.buttontextWizzardPadding
text: isDownloading() ? JamiStrings.cancel : JamiStrings.install
fontSize: 15
text: {
switch (pluginStatus) {
case PluginStatus.DOWNLOADING:
return JamiStrings.cancel;
case PluginStatus.INSTALLABLE:
return JamiStrings.install;
case PluginStatus.INSTALLING:
return JamiStrings.installing;
default:
return JamiStrings.install;
}
}
}
}
RowLayout {
spacing: 10
CachedImage {
id: icon
Component.onCompleted: {
pluginBackground = PluginStoreListModel.computeAverageColorOfImage("file://" + UtilsAdapter.getCachePath() + '/plugins/' + pluginId + '.svg');
defaultImage: JamiResources.plugins_default_icon_svg
onSourceChanged: {
if (source == defaultImage) {
pluginBackground = JamiTheme.pluginDefaultBackgroundColor;
return;
}
pluginBackground = PluginStoreListModel.computeAverageColorOfImage(source);
}
width: 50
height: 50
downloadUrl: PluginAdapter.baseUrl + "/icons/" + pluginId
width: 55
height: 55
downloadUrl: PluginAdapter.getIconUrl(pluginName)
fileExtension: '.svg'
localPath: UtilsAdapter.getCachePath() + '/plugins/' + pluginId + '.svg'
localPath: UtilsAdapter.getCachePath() + '/plugins/' + pluginName + '.svg'
}
ColumnLayout {
width: parent.width
Label {
text: pluginTitle
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
text: pluginName
font.kerning: true
color: JamiTheme.textColor
font.pointSize: JamiTheme.settingsFontSize
verticalAlignment: Text.AlignVCenter
}
Label {
color: JamiTheme.textColor
text: pluginShortDescription
font.kerning: true
font.pointSize: JamiTheme.settingsFontSize
verticalAlignment: Text.AlignVCenter
color: "black"
font.pointSize: JamiTheme.tinyCreditsTextSize
textFormat: Text.PlainText
wrapMode: Text.WrapAnywhere
}
// Label {
// Layout.fillWidth: true
// color: "black"
// text: pluginShortDescription
// font.pointSize: JamiTheme.settingsFontSize
// textFormat: Text.PlainText
// wrapMode: Text.WordWrap
// }
}
}
}
}
Rectangle {
id: contentContainer
anchors.fill: parent
color: JamiTheme.pluginViewBackgroundColor
color: hovered ? JamiTheme.smartListHoveredColor : JamiTheme.pluginViewBackgroundColor
}
Flickable {
JamiFlickable {
anchors.fill: parent
anchors.margins: 10
contentWidth: description.width
anchors.margins: 20
contentHeight: description.height
clip: true
flickableDirection: Flickable.VerticalFlick
ScrollBar.vertical: ScrollBar {
ScrollBar.vertical: JamiScrollBar {
id: scrollBar
policy: ScrollBar.AsNeeded
}
Text {
id: description
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ColumnLayout {
width: parent.width
color: JamiTheme.textColor
text: pluginDescription
wrapMode: Text.WordWrap
Text {
id: description
Layout.preferredWidth: contentContainer.width
font.pixelSize: JamiTheme.popuptextSize
color: JamiTheme.textColor
text: pluginDescription
wrapMode: Text.WordWrap
horizontalAlignment: Qt.AlignLeft
lineHeight: 1.5
textFormat: Text.MarkdownText
rightPadding: 40
}
}
}
footer: Control {
padding: 10
padding: 20
background: Rectangle {
color: JamiTheme.pluginViewBackgroundColor
color: hovered ? JamiTheme.smartListHoveredColor : JamiTheme.pluginViewBackgroundColor
}
contentItem: Text {
Layout.fillWidth: true
@ -145,32 +226,26 @@ ItemDelegate {
font.pointSize: JamiTheme.settingsFontSize
font.kerning: true
font.italic: true
text: "By " + pluginAuthor
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
}
}
DropShadow {
z: 2
visible: hovered
width: root.width
height: root.height
radius: 16
color: Qt.rgba(0, 0.34, 0.6, 0.16)
source: root
transparentBorder: true
samples: radius + 1
cached: true
}
}
function installPlugin() {
if (isDownloading()) {
return;
switch (pluginStatus) {
case PluginStatus.DOWNLOADING:
PluginAdapter.cancelDownload(pluginName);
break;
case PluginStatus.INSTALLABLE:
PluginAdapter.installRemotePlugin(pluginName);
break;
case PluginStatus.FAILED:
PluginAdapter.installRemotePlugin(pluginName);
break;
case PluginStatus.INSTALLING:
break;
}
PluginAdapter.installRemotePlugin(pluginId);
}
function isDownloading() {
return pluginStatus === PluginStatus.DOWNLOADING;
}
}

View file

@ -20,109 +20,133 @@ import QtQuick.Controls
import QtQuick.Layouts
import net.jami.Models 1.1
import net.jami.Adapters 1.1
import Qt5Compat.GraphicalEffects
import net.jami.Constants 1.1
import "../../commoncomponents"
ItemDelegate {
id: root
property string pluginName: ""
property string pluginId: ""
property string pluginIcon: ""
property int pluginStatus
property bool isLoaded: false
property string activeId: ""
height: pluginPreferencesView.visible ? implicitHeight + pluginPreferencesView.childrenRect.height : implicitHeight
height: implicitHeight
Connections {
target: PluginListModel
function onDisabled(id) {
if (root.pluginId === id) {
isLoaded = false;
loadSwitch.checked = false;
}
}
}
signal settingsClicked
onClicked: {
pluginListView.currentIndex = index;
}
onActiveIdChanged: pluginPreferencesView.visible = activeId != pluginId ? false : !pluginPreferencesView.visible
Rectangle {
id: mask
anchors.fill: parent
color: {
if (pluginHover.hovered && pluginListView.currentIndex !== index) {
return JamiTheme.smartListHoveredColor;
} else {
return JamiTheme.pluginViewBackgroundColor;
}
}
border.width: 2
border.color: {
if (pluginListView.currentIndex === index) {
return JamiTheme.switchHandleCheckedBorderColor;
}
return "transparent";
}
radius: 5
}
ColumnLayout {
width: parent.width
RowLayout {
width: parent.width - 20
height: parent.height
anchors.centerIn: parent
Item {
Layout.fillWidth: true
Layout.preferredHeight: implicitHeight
Layout.alignment: Qt.AlignCenter
Layout.preferredHeight: childrenRect.height
Label {
id: pluginImage
Layout.leftMargin: 8
Layout.topMargin: 8
Layout.alignment: Qt.AlignLeft | Qt.AlingVCenter
anchors.left: parent.left
width: JamiTheme.preferredFieldHeight
Layout.fillHeight: true
height: parent.height
background: Rectangle {
color: "transparent"
Image {
ResponsiveImage {
anchors.centerIn: parent
source: "file:" + pluginIcon
sourceSize: Qt.size(256, 256)
mipmap: true
width: JamiTheme.preferredFieldHeight
height: JamiTheme.preferredFieldHeight
containerWidth: JamiTheme.preferredFieldHeight
containerHeight: JamiTheme.preferredFieldHeight
}
}
}
Label {
Layout.fillHeight: true
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 8
width: contentWidth
height: parent.height
anchors.left: pluginImage.right
anchors.leftMargin: 8
color: JamiTheme.textColor
font.pointSize: JamiTheme.settingsFontSize
font.pointSize: JamiTheme.tinyCreditsTextSize
font.kerning: true
text: pluginName === "" ? pluginId : pluginName
verticalAlignment: Text.AlignVCenter
}
ToggleSwitch {
id: loadSwitch
Layout.fillHeight: true
property bool isHovering: false
Layout.topMargin: 8
Layout.rightMargin: 8
width: 20
MaterialButton {
id: update
anchors.right: itemSwitch.left
buttontextHeightMargin: 10.0
TextMetrics {
id: updateTextSize
font.weight: Font.Bold
font.pixelSize: JamiTheme.wizardViewButtonFontPixelSize
font.capitalization: Font.AllUppercase
text: JamiStrings.updateDialogTitle
}
visible: pluginStatus === PluginStatus.UPDATABLE
secondary: true
preferredWidth: updateTextSize.width
text: JamiStrings.updateDialogTitle
fontSize: 15
}
Item {
id: itemSwitch
height: parent.height
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
width: 78
ToggleSwitch {
id: loadSwitch
anchors.topMargin: parent.height / 2
width: parent.width
height: parent.height
property bool isHovering: false
tooltipText: JamiStrings.loadUnload
tooltipText: JamiStrings.loadUnload
checked: isLoaded
onSwitchToggled: {
if (isLoaded)
PluginModel.unloadPlugin(pluginId);
else
PluginModel.loadPlugin(pluginId);
installedPluginsModel.pluginChanged(index);
checked: isLoaded
onSwitchToggled: {
if (isLoaded)
PluginModel.unloadPlugin(pluginId);
else
PluginModel.loadPlugin(pluginId);
PluginListModel.pluginChanged(index);
}
}
}
PushButton {
id: btnPreferencesPlugin
Layout.alignment: Qt.AlingVCenter | Qt.AlignRight
Layout.topMargin: 8
Layout.rightMargin: 8
source: JamiResources.round_settings_24dp_svg
normalColor: JamiTheme.primaryBackgroundColor
imageColor: JamiTheme.textColor
toolTipText: JamiStrings.showHidePrefs
onClicked: settingsClicked()
}
}
PluginPreferencesView {
id: pluginPreferencesView
pluginId: root.pluginId
Layout.fillWidth: true
Layout.leftMargin: JamiTheme.preferredMarginSize
Layout.rightMargin: JamiTheme.preferredMarginSize
Layout.preferredHeight: pluginPreferencesView.childrenRect.height
}
}
}

View file

@ -26,114 +26,105 @@ import "../../commoncomponents"
Rectangle {
id: root
property string activePlugin: ""
property int count: pluginLoader.item !== undefined ? pluginLoader.item.count : 0
property bool isAutoUpdate: PluginAdapter.isAutoUpdaterEnabled()
property int currentIndex: {
if (pluginLoader.item !== undefined) {
return -1;
} else {
if (pluginListView.currentIndex === null) {
return -1;
}
return pluginListView.currentIndex;
}
}
visible: PluginAdapter.isEnabled && count
color: JamiTheme.secondaryBackgroundColor
ColumnLayout {
anchors.left: root.left
anchors.right: root.right
anchors.bottomMargin: 20
Label {
RowLayout {
Layout.preferredHeight: JamiTheme.settingsHeaderpreferredHeight
Layout.fillWidth: true
Layout.preferredHeight: 25
Layout.bottomMargin: 20
Layout.alignment: Qt.AlignRight
Label {
Layout.fillWidth: true
text: JamiStrings.installed
font.pixelSize: JamiTheme.settingsTitlePixelSize
font.kerning: true
color: JamiTheme.textColor
text: JamiStrings.installedPlugins
font.pointSize: JamiTheme.headerFontSize
font.kerning: true
color: JamiTheme.textColor
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
}
MaterialButton {
id: disableAll
TextMetrics {
id: disableTextSize
font.weight: Font.Bold
font.pixelSize: JamiTheme.wizardViewButtonFontPixelSize
font.capitalization: Font.AllUppercase
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
}
HeaderToggleSwitch {
labelText: "auto update"
tooltipText: "auto update"
checked: isAutoUpdate
onSwitchToggled: {
isAutoUpdate = !isAutoUpdate;
PluginAdapter.setAutoUpdate(isAutoUpdate);
}
}
MaterialButton {
id: disableAll
radius: JamiTheme.chatViewHeaderButtonRadius
buttontextHeightMargin: 10.0
TextMetrics {
id: disableTextSize
font.weight: Font.Bold
font.pixelSize: JamiTheme.wizardViewButtonFontPixelSize
font.capitalization: Font.AllUppercase
text: JamiStrings.disableAll
}
secondary: true
preferredWidth: disableTextSize.width + 2
text: JamiStrings.disableAll
}
secondary: true
preferredWidth: disableTextSize.width
text: JamiStrings.disableAll
fontSize: 15
}
MaterialButton {
id: installButton
Layout.alignment: Qt.AlignCenter
Layout.topMargin: JamiTheme.preferredMarginSize / 2
preferredWidth: JamiTheme.preferredFieldWidth
buttontextHeightMargin: JamiTheme.buttontextHeightMargin
color: JamiTheme.buttonTintedBlack
hoveredColor: JamiTheme.buttonTintedBlackHovered
pressedColor: JamiTheme.buttonTintedBlackPressed
secondary: true
toolTipText: JamiStrings.addNewPlugin
iconSource: JamiResources.round_add_24dp_svg
text: JamiStrings.installPlugin
onClicked: {
var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/JamiFileDialog.qml", {
"title": JamiStrings.selectPluginInstall,
"fileMode": JamiFileDialog.OpenFile,
"folder": StandardPaths.writableLocation(StandardPaths.DownloadLocation),
"nameFilters": [JamiStrings.pluginFiles, JamiStrings.allFiles]
});
dlg.fileAccepted.connect(function (file) {
var url = UtilsAdapter.getAbsPath(file.toString());
PluginModel.installPlugin(url, true);
installedPluginsModel.addPlugin();
});
fontSize: JamiTheme.wizardViewButtonFontPixelSize
onClicked: PluginListModel.disableAllPlugins()
}
}
ListView {
id: pluginList
Loader {
id: pluginLoader
Layout.fillWidth: true
Layout.bottomMargin: 10
Layout.preferredHeight: childrenRect.height
clip: true
Layout.preferredHeight: pluginLoader.item.contentHeight
Layout.topMargin: 10
active: true
asynchronous: true
model: PluginListModel {
id: installedPluginsModel
lrcInstance: LRCInstance
onLrcInstanceChanged: {
this.reset();
sourceComponent: ListView {
id: pluginListView
clip: true
model: PluginListModel
spacing: 10
currentIndex: -1
onCurrentIndexChanged: {
root.currentIndex = currentIndex;
}
}
delegate: PluginItemDelegate {
id: pluginItemDelegate
width: pluginLoader.width
implicitHeight: 50
delegate: PluginItemDelegate {
id: pluginItemDelegate
width: pluginList.width
implicitHeight: 50
pluginName: PluginName
pluginId: PluginId
pluginIcon: PluginIcon
isLoaded: IsLoaded
activeId: root.activePlugin
background: Rectangle {
anchors.fill: parent
color: "transparent"
pluginName: PluginName
pluginId: PluginId
pluginIcon: PluginIcon
pluginStatus: Status
isLoaded: IsLoaded
HoverHandler {
id: pluginHover
target: parent
enabled: true
}
}
onSettingsClicked: {
root.activePlugin = root.activePlugin === pluginId ? "" : pluginId;
Connections {
target: pluginPreferencesView
function onClosed() {
pluginListView.currentIndex = -1;
}
}
}
}

View file

@ -25,9 +25,9 @@ import "../../commoncomponents"
Rectangle {
id: root
property string accountId: ""
required property string pluginId
width: parent.width
property int count: pluginPreferenceView.count + pluginPreferenceViewCategory.count
@ -40,7 +40,6 @@ Rectangle {
}
color: "transparent"
Connections {
target: LRCInstance
@ -56,8 +55,6 @@ Rectangle {
property var categories: PluginAdapter.getPluginPreferencesCategories(pluginId, accountId)
property string generalCategory: categories.length <= 1 ? "all" : ""
visible: false
function setPreference(pluginId, preferenceKey, preferenceNewValue) {
PluginModel.setPluginPreference(pluginId, accountId, preferenceKey, preferenceNewValue);
}
@ -66,7 +63,7 @@ Rectangle {
anchors.left: root.left
anchors.right: root.right
Rectangle {
Item {
id: prefsByCategory
visible: categories.length > 1
@ -74,7 +71,6 @@ Rectangle {
Layout.topMargin: 24
Layout.fillWidth: true
implicitHeight: childrenRect.height
color: JamiTheme.backgroundColor
ColumnLayout {
anchors.left: parent.left
@ -263,7 +259,7 @@ Rectangle {
MaterialButton {
id: resetButton
visible: count > 0
Layout.alignment: Qt.AlignCenter
preferredWidth: JamiTheme.preferredFieldWidth
@ -274,8 +270,6 @@ Rectangle {
pressedColor: JamiTheme.buttonTintedBlackPressed
secondary: true
iconSource: JamiResources.settings_backup_restore_24dp_svg
text: JamiStrings.reset
onClicked: viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {

View file

@ -19,179 +19,181 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import net.jami.Adapters 1.1
import SortFilterProxyModel 0.2
import net.jami.Models 1.1
import net.jami.Constants 1.1
import "../../commoncomponents"
Rectangle {
Item {
id: root
required property string pluginId
color: "transparent"
visible: false
ColumnLayout {
anchors.left: root.left
anchors.right: root.right
anchors.bottomMargin: 10
Label {
Layout.topMargin: 34
Layout.alignment: Qt.AlignHCenter
height: 64
required property int currentIndex
signal closed
ListView {
id: pluginPreferenceListView
height: parent.height
width: parent.width
model: SortFilterProxyModel {
sourceModel: PluginListModel
filters: [
ExpressionFilter {
expression: index === currentIndex
enabled: true
}
]
}
delegate: Page {
id: settings
width: root.width
height: root.height
background: Rectangle {
Image {
anchors.centerIn: parent
source: pluginIcon === "" ? JamiResources.plugins_24dp_svg : "file:" + pluginIcon
sourceSize: Qt.size(256, 256)
height: 64
width: 64
mipmap: true
color: JamiTheme.pluginViewBackgroundColor
}
header: Control {
padding: 10
background: Rectangle {
color: JamiTheme.pluginViewBackgroundColor
}
contentItem: ColumnLayout {
width: parent.width
PushButton {
id: closeButton
normalColor: "transparent"
hoveredColor: JamiTheme.smartListHoveredColor
Layout.alignment: Qt.AlignRight
Layout.preferredWidth: JamiTheme.preferredFieldHeight
Layout.preferredHeight: childrenRect.height
imageColor: JamiTheme.textColor
toolTipText: JamiStrings.closeSettings
preferredSize: 32
source: JamiResources.round_close_24dp_svg
onClicked: {
closed();
}
}
RowLayout {
Layout.preferredWidth: parent.width
ResponsiveImage {
Layout.bottomMargin: 10
Layout.rightMargin: 10
containerWidth: 64
containerHeight: 64
source: PluginIcon === "" ? JamiResources.plugins_default_icon_svg : "file:" + PluginIcon
}
Label {
text: PluginName
font.pixelSize: JamiTheme.settingsTitlePixelSize
font.kerning: true
color: JamiTheme.textColor
textFormat: Text.PlainText
}
Item {
Layout.fillHeight: true
Layout.fillWidth: true
MaterialButton {
id: update
anchors.right: parent.right
buttontextHeightMargin: 0.0
TextMetrics {
id: updateTextSize
font.weight: Font.Bold
font.pixelSize: JamiTheme.wizardViewButtonFontPixelSize
font.capitalization: Font.AllUppercase
text: JamiStrings.updateDialogTitle
}
visible: Status === PluginStatus.UPDATABLE
secondary: true
preferredWidth: updateTextSize.width
text: JamiStrings.updateDialogTitle
fontSize: 15
}
}
}
JamiFlickable {
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
Layout.minimumHeight: childrenRect.height
Layout.maximumHeight: 88
contentWidth: description.width
contentHeight: description.height
clip: true
flickableDirection: Flickable.VerticalFlick
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical: ScrollBar {
id: scrollBar
policy: ScrollBar.AsNeeded
}
Text {
id: description
width: settings.width - 2 * scrollBar.width
text: PluginDescription
font.pixelSize: JamiTheme.popuptextSize
color: JamiTheme.textColor
wrapMode: Text.WordWrap
textFormat: Text.PlainText
}
}
}
}
}
Label {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 24
height: JamiTheme.preferredFieldHeight
text: "%1\n%2".arg(pluginName).arg(JamiStrings.pluginPreferences)
font.pointSize: JamiTheme.headerFontSize
font.kerning: true
color: JamiTheme.textColor
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
RowLayout {
Layout.fillWidth: true
Text {
Layout.fillWidth: true
Layout.preferredHeight: JamiTheme.preferredFieldHeight
font.pointSize: JamiTheme.headerFontSize
font.kerning: true
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
color: JamiTheme.textColor
text: JamiStrings.generalSettingsTitle
elide: Text.ElideRight
Rectangle {
anchors.fill: parent
color: JamiTheme.primaryBackgroundColor
}
JamiFlickable {
anchors.fill: parent
contentHeight: contentItem.childrenRect.height
topMargin: JamiTheme.preferredSettingsBottomMarginSize
bottomMargin: JamiTheme.preferredSettingsBottomMarginSize
ScrollBar.horizontal.visible: false
contentItem.children: ColumnLayout {
width: root.width
PluginPreferencesListView {
id: pluginGeneralSettingsView
Layout.fillWidth: true
pluginId: PluginId
}
PluginPreferencesListView {
id: pluginAccountSettingsView
Layout.fillWidth: true
accountId: LRCInstance.currentAccountId
pluginId: PluginId
}
MaterialButton {
id: uninstallButton
PushButton {
Layout.preferredWidth: JamiTheme.preferredFieldHeight
Layout.preferredHeight: JamiTheme.preferredFieldHeight
Layout.alignment: Qt.AlignHCenter
Layout.alignment: Qt.AlignCenter
imageColor: JamiTheme.textColor
toolTipText: JamiStrings.tipGeneralPluginSettingsDisplay
preferredWidth: JamiTheme.preferredFieldWidth
buttontextHeightMargin: JamiTheme.buttontextHeightMargin
contentColorProvider: JamiTheme.buttonTintedRed
color: JamiTheme.buttonTintedBlack
hoveredColor: JamiTheme.buttonTintedBlackHovered
pressedColor: JamiTheme.buttonTintedBlackPressed
tertiary: true
toolTipText: JamiStrings.pluginUninstallConfirmation.arg(PluginId)
preferredSize: 32
source: pluginGeneralSettingsView.visible ? JamiResources.expand_less_24dp_svg : JamiResources.expand_more_24dp_svg
text: JamiStrings.uninstall
onClicked: {
pluginGeneralSettingsView.visible = !pluginGeneralSettingsView.visible;
onClicked: viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
"title": JamiStrings.uninstallPlugin,
"infoText": JamiStrings.pluginUninstallConfirmation.arg(PluginName),
"buttonTitles": [JamiStrings.optionOk, JamiStrings.optionCancel],
"buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue, SimpleMessageDialog.ButtonStyle.TintedBlack],
"buttonCallBacks": [function () {
PluginListModel.setVersionStatus(PluginName, PluginStatus.INSTALLABLE);
PluginModel.uninstallPlugin(PluginId);
PluginListModel.removePlugin(index);
// could not call root from here
settings.ListView.view.parent.closed();
}]
})
}
}
}
}
PluginPreferencesListView {
id: pluginGeneralSettingsView
visible: false
Layout.fillWidth: true
pluginId: root.pluginId
}
RowLayout {
Layout.fillWidth: true
visible: pluginAccountSettingsView.count > 0
Text {
Layout.fillWidth: true
Layout.preferredHeight: JamiTheme.preferredFieldHeight
font.pointSize: JamiTheme.headerFontSize
font.kerning: true
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
color: JamiTheme.textColor
text: JamiStrings.accountSettingsMenuTitle
elide: Text.ElideRight
}
PushButton {
Layout.preferredWidth: JamiTheme.preferredFieldHeight
Layout.preferredHeight: JamiTheme.preferredFieldHeight
Layout.alignment: Qt.AlignHCenter
imageColor: JamiTheme.textColor
toolTipText: JamiStrings.tipAccountPluginSettingsDisplay
preferredSize: 32
source: pluginAccountSettingsView.visible ? JamiResources.expand_less_24dp_svg : JamiResources.expand_more_24dp_svg
onClicked: {
pluginAccountSettingsView.visible = !pluginAccountSettingsView.visible;
}
}
}
PluginPreferencesListView {
id: pluginAccountSettingsView
visible: false
Layout.fillWidth: true
accountId: LRCInstance.currentAccountId
pluginId: root.pluginId
}
MaterialButton {
id: uninstallButton
Layout.alignment: Qt.AlignCenter
preferredWidth: JamiTheme.preferredFieldWidth
buttontextHeightMargin: JamiTheme.buttontextHeightMargin
color: JamiTheme.buttonTintedBlack
hoveredColor: JamiTheme.buttonTintedBlackHovered
pressedColor: JamiTheme.buttonTintedBlackPressed
secondary: true
toolTipText: JamiStrings.pluginUninstallConfirmation.arg(pluginName)
iconSource: JamiResources.delete_24dp_svg
text: JamiStrings.uninstall
onClicked: viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
"title": JamiStrings.uninstallPlugin,
"infoText": JamiStrings.pluginUninstallConfirmation.arg(pluginName),
"buttonTitles": [JamiStrings.optionOk, JamiStrings.optionCancel],
"buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue, SimpleMessageDialog.ButtonStyle.TintedBlack],
"buttonCallBacks": [function () {
pluginPreferencesView.visible = false;
PluginModel.uninstallPlugin(pluginId);
PluginListModel.removePlugin(index);
var pluginPath = pluginId.split('/');
PluginListModel.setVersionStatus(pluginPath[pluginPath.length - 1], PluginStatus.INSTALLABLE);
}]
})
}
Rectangle {
Layout.bottomMargin: 10
height: 2
Layout.fillWidth: true
color: "transparent"
border.width: 1
border.color: JamiTheme.separationLine
}
}
}

View file

@ -25,29 +25,84 @@ import "../../commoncomponents"
SettingsPageBase {
id: root
contentFlickableWidth: Math.min(root.width, root.width - 2 * JamiTheme.preferredSettingsMarginSize)
title: JamiStrings.pluginSettingsTitle
flickableContent: ColumnLayout {
id: pluginSettingsColumnLayout
width: contentFlickableWidth
spacing: JamiTheme.settingsBlockSpacing
onWidthChanged: resolvePanes()
flickableContent: RowLayout {
width: parent.width
anchors.left: parent.left
anchors.leftMargin: JamiTheme.preferredSettingsMarginSize
ColumnLayout {
id: generalSettings
Layout.preferredWidth: root.width
spacing: JamiTheme.settingsCategorySpacing
}
PluginListView {
id: pluginListView
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
Layout.maximumWidth: 3 * (JamiTheme.remotePluginWidthDelegate + 20)
Layout.preferredWidth: parent.width
Layout.minimumHeight: 0
Layout.preferredHeight: childrenRect.height
Layout.rightMargin: 80
spacing: JamiTheme.settingsBlockSpacing
// View of installed plugins
PluginListView {
id: pluginList
Layout.fillWidth: true
Layout.rightMargin: 20
Layout.preferredHeight: childrenRect.height
Connections {
target: pluginPreferencesView
function onClosed() {
pluginList.currentIndex = -1;
}
}
}
// View of available plugins in the store
PluginStoreListView {
Layout.alignment: Qt.AlignBottom | Qt.AlignHCenter
Layout.fillWidth: true
}
InstallManuallyView {
Layout.fillWidth: true
spacing: 10
}
}
}
property real previousDetailsWidth: 500
property real previousWidth: 500
// This function governs the visibility of the plugin content and tracks the
// the width of the SplitView and the details panel. This function should be
// called when the width of the SplitView changes, when the SplitView is shown,
// and when the details panel is shown. When called with force=true, it is being
// called from a visibleChanged event, and we should not update the previous widths.
function resolvePanes(force = false) {
// If the details panel is not visible, then show the generalSettings.
if (!pluginPreferencesView.visible) {
pageContainer.visible = true;
return;
}
// Next we compute whether the SplitView is expanding or shrinking.
const isExpanding = width > previousWidth;
// width has a first bad state
const preferencePreferredWidth = pluginPreferencesView.width === 0 ? 500 : pluginPreferencesView.width;
// If the SplitView is not wide enough to show both the generalSettings
// and the details panel, then hide the generalSettings.
if (width < 522 + preferencePreferredWidth && (!isExpanding || force) && pageContainer.visible) {
if (!force)
previousDetailsWidth = pluginPreferencesView.width;
pageContainer.visible = false;
} else if (width >= JamiTheme.mainViewPaneMinWidth + previousDetailsWidth && (isExpanding || force) && !pageContainer.visible) {
pageContainer.visible = true;
}
if (!force)
previousWidth = width;
}
onResizingChanged: if (pageContainer.visible)
pluginPreferencesView.previousWidth = pluginPreferencesView.width
PluginPreferencesView {
id: pluginPreferencesView
SplitView.maximumWidth: root.width
SplitView.minimumWidth: 500
SplitView.preferredWidth: 500
SplitView.fillHeight: true
property int previousWidth: 500
currentIndex: pluginList.currentIndex
visible: pluginList.currentIndex != -1
onVisibleChanged: root.resolvePanes(true)
}
}

View file

@ -25,80 +25,63 @@ import net.jami.Constants 1.1
import "../../commoncomponents"
ColumnLayout {
function installPlugin() {
var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/JamiFileDialog.qml", {
"title": JamiStrings.selectPluginInstall,
"fileMode": JamiFileDialog.OpenFile,
"folder": StandardPaths.writableLocation(StandardPaths.DownloadLocation),
"nameFilters": [JamiStrings.pluginFiles, JamiStrings.allFiles]
});
dlg.fileAccepted.connect(function (file) {
var url = UtilsAdapter.getAbsPath(file.toString());
PluginModel.installPlugin(url, true);
PluginListModel.addPlugin();
});
property bool storeAvailable: true
Component.onCompleted: {
PluginAdapter.getPluginsFromStore();
}
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Label {
Layout.fillWidth: true
Layout.preferredHeight: 25
text: JamiStrings.pluginStoreTitle
font.pointSize: JamiTheme.headerFontSize
font.kerning: true
color: JamiTheme.textColor
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
Connections {
target: PluginAdapter
function onStoreNotAvailable() {
storeAvailable = false;
}
RowLayout {
Layout.alignment: Qt.AlignRight
MaterialButton {
id: installManually
}
Label {
Layout.fillWidth: true
Layout.bottomMargin: 20
text: JamiStrings.pluginStoreTitle
font.pixelSize: JamiTheme.settingsTitlePixelSize
font.kerning: true
color: JamiTheme.textColor
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
}
Loader {
active: storeAvailable
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: active ? item.height : 0
sourceComponent: Flow {
id: pluginStoreList
height: childrenRect.height
spacing: 20
Repeater {
model: PluginStoreListModel
TextMetrics {
id: installManuallyTextSize
font.weight: Font.Black
font.pixelSize: JamiTheme.wizardViewButtonFontPixelSize
font.capitalization: Font.Capitalize
text: JamiStrings.installManually
delegate: PluginAvailableDelagate {
id: pluginItemDelegate
width: JamiTheme.remotePluginWidthDelegate
height: JamiTheme.remotePluginHeightDelegate
pluginName: Name
pluginIcon: IconPath
pluginDescription: Description
pluginAuthor: Author
pluginShortDescription: ""
pluginStatus: Status
}
secondary: true
preferredWidth: installManuallyTextSize.width
text: JamiStrings.installManually
toolTipText: JamiStrings.installManually
fontSize: 15
onClicked: installPlugin()
}
}
}
Flow {
id: pluginStoreList
Loader {
Layout.fillWidth: true
spacing: 20
Layout.preferredHeight: childrenRect.height
clip: true
Repeater {
model: PluginStoreListModel
delegate: PluginAvailableDelagate {
id: pluginItemDelegate
width: 350
height: 400
pluginId: Id
pluginTitle: Title
pluginIcon: IconPath
pluginBackground: Background === '' ? JamiTheme.backgroundColor : Background
pluginDescription: Description
pluginAuthor: Author
pluginShortDescription: ""
pluginStatus: Status
}
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
Layout.preferredHeight: active ? JamiTheme.bigFontSize : 0
active: !storeAvailable
sourceComponent: Text {
font.bold: true
color: JamiTheme.textColor
font.pixelSize: JamiTheme.bigFontSize
horizontalAlignment: Text.AlignHCenter
text: JamiStrings.pluginStoreNotAvailable
}
}
}

View file

@ -23,41 +23,44 @@ import net.jami.Enums 1.1
import net.jami.Models 1.1
import "../../commoncomponents"
Page {
JamiSplitView {
id: root
required property Item flickableContent
property real contentFlickableWidth: Math.min(JamiTheme.maximumWidthSettingsView, root.width - 2 * JamiTheme.preferredSettingsMarginSize)
property real contentFlickableWidth: Math.min(JamiTheme.maximumWidthSettingsView, settingsPage.width - 2 * JamiTheme.preferredSettingsMarginSize)
property alias title: settingsPage.title
property color backgroundColor: JamiTheme.secondaryBackgroundColor
property alias pageContainer: settingsPage
Page {
id: settingsPage
SplitView.maximumWidth: root.width
SplitView.fillWidth: true
SplitView.minimumWidth: 500
Rectangle {
width: parent.width
height: parent.height
color: backgroundColor
}
header: Rectangle {
height: JamiTheme.settingsHeaderpreferredHeight
width: root.preferredWidth
color: backgroundColor
Rectangle {
width: parent.width
height: parent.height
color: backgroundColor
}
SettingsHeader {
id: settingsHeader
title: root.title
anchors.fill: parent
onBackArrowClicked: viewNode.dismiss()
}
}
header: Rectangle {
height: JamiTheme.settingsHeaderpreferredHeight
width: root.preferredWidth
color: backgroundColor
SettingsHeader {
id: settingsHeader
title: root.title
JamiFlickable {
id: flickable
anchors.fill: parent
onBackArrowClicked: viewNode.dismiss()
contentHeight: contentItem.childrenRect.height
contentItem.children: [flickableContent]
topMargin: JamiTheme.preferredSettingsBottomMarginSize
bottomMargin: JamiTheme.preferredSettingsBottomMarginSize
ScrollBar.horizontal.visible: false
}
}
JamiFlickable {
id: flickable
anchors.fill: parent
contentHeight: contentItem.childrenRect.height
contentItem.children: [flickableContent]
topMargin: JamiTheme.preferredSettingsBottomMarginSize
bottomMargin: JamiTheme.preferredSettingsBottomMarginSize
ScrollBar.horizontal.visible: false
}
}

View file

@ -933,3 +933,9 @@ Utils::generateUid()
{
return QUuid::createUuid().toString(QUuid::Id128);
}
QString
Utils::getPlatformString()
{
return "desktop";
}

View file

@ -127,4 +127,5 @@ QString generateUid();
QString humanFileSize(qint64 fileSize);
QString getDebugFilePath();
QString getPlatformString();
} // namespace Utils

View file

@ -40,6 +40,7 @@ struct PluginDetails
{
QString id = "";
QString name = "";
QString description = "";
QString path = "";
QString version = "";
QString iconPath = "";
@ -201,7 +202,6 @@ public:
* @return true if preference was succesfully reset
*/
Q_INVOKABLE bool resetPluginPreferencesValues(const QString& path, const QString& accountId);
Q_SIGNALS:
void chatHandlerStatusUpdated(bool isVisible);
void modelUpdated();

View file

@ -50,6 +50,15 @@ namespace lrc {
using namespace api;
enum pluginInstallResult {
SUCCESS = 0,
PLUGIN_ALREADY_INSTALLED = 100, /* Plugin already installed with the same version */
PLUGIN_OLD_VERSION = 200, /* Plugin already installed with a newer version */
SIGNATURE_VERIFICATION_FAILED = 300, /* Signature verification failed */
CERTIFICATE_VERIFICATION_FAILED = 400,
INVALID_PLUGIN = 500,
};
PluginModel::PluginModel()
: QObject()
{
@ -99,6 +108,7 @@ PluginModel::getPluginDetails(const QString& path)
if (!details.empty()) {
result.id = details["id"];
result.name = details["name"];
result.description = details["description"];
result.path = path;
result.iconPath = details["iconPath"];
result.version = details["version"];
@ -311,5 +321,4 @@ PluginModel::resetPluginPreferencesValues(const QString& path, const QString& ac
Q_EMIT modelUpdated();
return result;
}
} // namespace lrc