mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
843 lines
24 KiB
QML
843 lines
24 KiB
QML
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Layouts
|
|
import qs.Commons
|
|
import qs.Services.Noctalia
|
|
import qs.Services.UI
|
|
import qs.Widgets
|
|
|
|
ColumnLayout {
|
|
id: root
|
|
|
|
// Track which plugins are currently updating
|
|
property var updatingPlugins: ({})
|
|
property int installedPluginsRefreshCounter: 0
|
|
|
|
function stripAuthorEmail(author) {
|
|
if (!author)
|
|
return "";
|
|
var lastBracket = author.lastIndexOf("<");
|
|
if (lastBracket >= 0) {
|
|
return author.substring(0, lastBracket).trim();
|
|
}
|
|
return author;
|
|
}
|
|
|
|
// Check for updates when tab becomes visible
|
|
onVisibleChanged: {
|
|
if (visible && PluginService.pluginsFullyLoaded) {
|
|
PluginService.checkForUpdates();
|
|
}
|
|
}
|
|
|
|
// ------------------------------
|
|
// Installed Plugins
|
|
// ------------------------------
|
|
NHeader {
|
|
label: I18n.tr("settings.plugins.installed.label")
|
|
description: I18n.tr("settings.plugins.installed.description")
|
|
}
|
|
|
|
// Update All button
|
|
NButton {
|
|
property int updateCount: Object.keys(PluginService.pluginUpdates).length
|
|
property bool isUpdating: false
|
|
|
|
text: I18n.tr("settings.plugins.update-all", {
|
|
"count": updateCount
|
|
})
|
|
icon: "download"
|
|
visible: updateCount >= 2
|
|
enabled: !isUpdating
|
|
backgroundColor: Color.mPrimary
|
|
textColor: Color.mOnPrimary
|
|
Layout.fillWidth: true
|
|
onClicked: {
|
|
isUpdating = true;
|
|
var pluginIds = Object.keys(PluginService.pluginUpdates);
|
|
var currentIndex = 0;
|
|
|
|
function updateNext() {
|
|
if (currentIndex >= pluginIds.length) {
|
|
isUpdating = false;
|
|
ToastService.showNotice(I18n.tr("settings.plugins.update-all-success"));
|
|
return;
|
|
}
|
|
|
|
var pluginId = pluginIds[currentIndex];
|
|
currentIndex++;
|
|
|
|
PluginService.updatePlugin(pluginId, function (success, error) {
|
|
if (!success) {
|
|
Logger.w("PluginsTab", "Failed to update", pluginId + ":", error);
|
|
}
|
|
Qt.callLater(updateNext);
|
|
});
|
|
}
|
|
|
|
updateNext();
|
|
}
|
|
}
|
|
|
|
// ------------------------------
|
|
// Installed plugins
|
|
// ------------------------------
|
|
ColumnLayout {
|
|
spacing: Style.marginM
|
|
Layout.fillWidth: true
|
|
|
|
Repeater {
|
|
id: installedPluginsRepeater
|
|
|
|
model: {
|
|
// Force refresh when counter changes
|
|
var _ = root.installedPluginsRefreshCounter;
|
|
|
|
var allIds = PluginRegistry.getAllInstalledPluginIds();
|
|
var plugins = [];
|
|
for (var i = 0; i < allIds.length; i++) {
|
|
var manifest = PluginRegistry.getPluginManifest(allIds[i]);
|
|
if (manifest) {
|
|
// Create a copy of manifest and include update info and enabled state
|
|
var pluginData = JSON.parse(JSON.stringify(manifest));
|
|
pluginData._updateInfo = PluginService.pluginUpdates[allIds[i]];
|
|
pluginData._enabled = PluginRegistry.isPluginEnabled(allIds[i]);
|
|
plugins.push(pluginData);
|
|
}
|
|
}
|
|
return plugins;
|
|
}
|
|
|
|
delegate: NBox {
|
|
Layout.fillWidth: true
|
|
Layout.leftMargin: Style.borderS
|
|
Layout.rightMargin: Style.borderS
|
|
implicitHeight: Math.round(rowLayout.implicitHeight) + Style.marginL * 2
|
|
color: Color.mSurface
|
|
|
|
RowLayout {
|
|
id: rowLayout
|
|
anchors.fill: parent
|
|
anchors.margins: Style.marginL
|
|
spacing: Style.marginM
|
|
|
|
NIcon {
|
|
icon: "plugin"
|
|
pointSize: Style.fontSizeXL
|
|
color: PluginService.hasPluginError(modelData.id) ? Color.mError : Color.mOnSurface
|
|
}
|
|
|
|
ColumnLayout {
|
|
spacing: 2
|
|
Layout.fillWidth: true
|
|
|
|
NText {
|
|
text: modelData.name
|
|
font.weight: Font.Medium
|
|
color: Color.mOnSurface
|
|
elide: Text.ElideRight
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
NText {
|
|
text: modelData.description
|
|
font.pointSize: Style.fontSizeXS
|
|
color: Color.mOnSurfaceVariant
|
|
wrapMode: Text.WordWrap
|
|
maximumLineCount: 2
|
|
elide: Text.ElideRight
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
RowLayout {
|
|
spacing: Style.marginS
|
|
|
|
NText {
|
|
text: modelData._updateInfo ? I18n.tr("settings.plugins.update-version", {
|
|
"current": modelData.version,
|
|
"new": modelData._updateInfo.availableVersion
|
|
}) : "v" + modelData.version
|
|
font.pointSize: Style.fontSizeXXS
|
|
color: modelData._updateInfo ? Color.mPrimary : Color.mOnSurfaceVariant
|
|
font.weight: modelData._updateInfo ? Font.Medium : Font.Normal
|
|
}
|
|
|
|
NText {
|
|
text: "•"
|
|
font.pointSize: Style.fontSizeXXS
|
|
color: Color.mOnSurfaceVariant
|
|
}
|
|
|
|
NText {
|
|
text: stripAuthorEmail(modelData.author)
|
|
font.pointSize: Style.fontSizeXXS
|
|
color: Color.mOnSurfaceVariant
|
|
}
|
|
}
|
|
|
|
// Error indicator
|
|
RowLayout {
|
|
spacing: Style.marginS
|
|
visible: PluginService.hasPluginError(modelData.id)
|
|
|
|
NIcon {
|
|
icon: "alert-triangle"
|
|
pointSize: Style.fontSizeS
|
|
color: Color.mError
|
|
}
|
|
|
|
NText {
|
|
property var errorInfo: PluginService.getPluginError(modelData.id)
|
|
text: errorInfo ? errorInfo.error : ""
|
|
font.pointSize: Style.fontSizeXXS
|
|
color: Color.mError
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
elide: Text.ElideRight
|
|
maximumLineCount: 3
|
|
}
|
|
}
|
|
}
|
|
|
|
NIconButton {
|
|
icon: "settings"
|
|
tooltipText: I18n.tr("settings.plugins.settings.tooltip")
|
|
baseSize: Style.baseWidgetSize * 0.7
|
|
visible: modelData.entryPoints?.settings !== undefined
|
|
onClicked: {
|
|
pluginSettingsDialog.openPluginSettings(modelData);
|
|
}
|
|
}
|
|
|
|
NButton {
|
|
id: updateButton
|
|
property string pluginId: modelData.id
|
|
property bool isUpdating: root.updatingPlugins[pluginId] === true
|
|
|
|
text: isUpdating ? I18n.tr("settings.plugins.updating", {
|
|
"plugin": modelData.name
|
|
}) : I18n.tr("settings.plugins.update")
|
|
icon: isUpdating ? "" : "download"
|
|
visible: modelData._updateInfo !== undefined
|
|
enabled: !isUpdating
|
|
backgroundColor: Color.mPrimary
|
|
textColor: Color.mOnPrimary
|
|
onClicked: {
|
|
var pid = pluginId;
|
|
var pname = modelData.name;
|
|
var pversion = modelData._updateInfo?.availableVersion || "";
|
|
var rootRef = root;
|
|
var updates = Object.assign({}, rootRef.updatingPlugins);
|
|
updates[pid] = true;
|
|
rootRef.updatingPlugins = updates;
|
|
|
|
PluginService.updatePlugin(pid, function (success, error) {
|
|
var updates2 = Object.assign({}, rootRef.updatingPlugins);
|
|
updates2[pid] = false;
|
|
rootRef.updatingPlugins = updates2;
|
|
|
|
if (success) {
|
|
ToastService.showNotice(I18n.tr("settings.plugins.update-success", {
|
|
"plugin": pname,
|
|
"version": pversion
|
|
}));
|
|
} else {
|
|
ToastService.showError(I18n.tr("settings.plugins.update-error", {
|
|
"plugin": pname,
|
|
"error": error || "Unknown error"
|
|
}));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
NToggle {
|
|
checked: modelData._enabled
|
|
baseSize: Style.baseWidgetSize * 0.7
|
|
onToggled: function (checked) {
|
|
if (checked) {
|
|
PluginService.enablePlugin(modelData.id);
|
|
} else {
|
|
PluginService.disablePlugin(modelData.id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NLabel {
|
|
visible: PluginRegistry.getAllInstalledPluginIds().length === 0
|
|
label: I18n.tr("settings.plugins.installed.no-plugins-label")
|
|
description: I18n.tr("settings.plugins.installed.no-plugins-description")
|
|
Layout.fillWidth: true
|
|
}
|
|
}
|
|
|
|
NDivider {
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: Style.marginL
|
|
Layout.bottomMargin: Style.marginL
|
|
}
|
|
|
|
// ------------------------------
|
|
// Available Plugins (Sources + Filter + List)
|
|
// ------------------------------
|
|
NHeader {
|
|
label: I18n.tr("settings.plugins.available.label")
|
|
description: I18n.tr("settings.plugins.available.description")
|
|
}
|
|
|
|
// Sources
|
|
NCollapsible {
|
|
Layout.fillWidth: true
|
|
label: I18n.tr("settings.plugins.sources.label")
|
|
description: I18n.tr("settings.plugins.sources.description")
|
|
expanded: false
|
|
|
|
ColumnLayout {
|
|
spacing: Style.marginM
|
|
Layout.fillWidth: true
|
|
|
|
// List of plugin sources
|
|
Repeater {
|
|
id: pluginSourcesRepeater
|
|
model: PluginRegistry.pluginSources || []
|
|
|
|
delegate: RowLayout {
|
|
spacing: Style.marginM
|
|
Layout.fillWidth: true
|
|
|
|
NIcon {
|
|
icon: "brand-github"
|
|
pointSize: Style.fontSizeM
|
|
}
|
|
|
|
ColumnLayout {
|
|
spacing: Style.marginS
|
|
Layout.fillWidth: true
|
|
|
|
NText {
|
|
text: modelData.name
|
|
font.weight: Font.Medium
|
|
color: Color.mOnSurface
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
NText {
|
|
text: modelData.url
|
|
font.pointSize: Style.fontSizeS
|
|
color: Color.mOnSurfaceVariant
|
|
Layout.fillWidth: true
|
|
}
|
|
}
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
NIconButton {
|
|
icon: "trash"
|
|
tooltipText: I18n.tr("settings.plugins.sources.remove.tooltip")
|
|
visible: index !== 0 // Cannot remove official source
|
|
baseSize: Style.baseWidgetSize * 0.7
|
|
onClicked: {
|
|
PluginRegistry.removePluginSource(modelData.url);
|
|
}
|
|
}
|
|
|
|
// Enable/Disable a source
|
|
NToggle {
|
|
checked: modelData.enabled !== false // Default to true if not set
|
|
baseSize: Style.baseWidgetSize * 0.7
|
|
onToggled: function (checked) {
|
|
PluginRegistry.setSourceEnabled(modelData.url, checked);
|
|
PluginService.refreshAvailablePlugins();
|
|
ToastService.showNotice(I18n.tr("settings.plugins.refresh.refreshing"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NDivider {
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
// Add custom repository
|
|
NButton {
|
|
text: I18n.tr("settings.plugins.sources.add-custom")
|
|
icon: "plus"
|
|
onClicked: {
|
|
addSourceDialog.open();
|
|
}
|
|
Layout.fillWidth: true
|
|
}
|
|
}
|
|
}
|
|
|
|
// Filter controls
|
|
RowLayout {
|
|
spacing: Style.marginM
|
|
Layout.fillWidth: true
|
|
Layout.bottomMargin: Style.marginM
|
|
|
|
NTabBar {
|
|
id: filterTabBar
|
|
Layout.fillWidth: true
|
|
spacing: Style.marginM
|
|
currentIndex: 0
|
|
onCurrentIndexChanged: {
|
|
if (currentIndex === 0)
|
|
pluginFilter = "all";
|
|
else if (currentIndex === 1)
|
|
pluginFilter = "downloaded";
|
|
else if (currentIndex === 2)
|
|
pluginFilter = "notDownloaded";
|
|
}
|
|
|
|
NTabButton {
|
|
Layout.fillWidth: true
|
|
text: I18n.tr("settings.plugins.filter.all")
|
|
tabIndex: 0
|
|
checked: pluginFilter === "all"
|
|
}
|
|
|
|
NTabButton {
|
|
Layout.fillWidth: true
|
|
text: I18n.tr("settings.plugins.filter.downloaded")
|
|
tabIndex: 1
|
|
checked: pluginFilter === "downloaded"
|
|
}
|
|
|
|
NTabButton {
|
|
Layout.fillWidth: true
|
|
text: I18n.tr("settings.plugins.filter.not-downloaded")
|
|
tabIndex: 2
|
|
checked: pluginFilter === "notDownloaded"
|
|
}
|
|
}
|
|
|
|
NIconButton {
|
|
icon: "refresh"
|
|
tooltipText: I18n.tr("settings.plugins.refresh.tooltip")
|
|
baseSize: Style.baseWidgetSize * 0.9
|
|
onClicked: {
|
|
PluginService.refreshAvailablePlugins();
|
|
checkUpdatesTimer.restart();
|
|
ToastService.showNotice(I18n.tr("settings.plugins.refresh.refreshing"));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Timer to check for updates after refresh starts
|
|
Timer {
|
|
id: checkUpdatesTimer
|
|
interval: 100
|
|
onTriggered: {
|
|
PluginService.checkForUpdates();
|
|
}
|
|
}
|
|
|
|
// Timer to recheck updates after available plugins are updated
|
|
Timer {
|
|
id: recheckUpdatesTimer
|
|
interval: 50
|
|
onTriggered: {
|
|
PluginService.checkForUpdates();
|
|
}
|
|
}
|
|
|
|
property string pluginFilter: "all"
|
|
|
|
// Available plugins list
|
|
ColumnLayout {
|
|
spacing: Style.marginM
|
|
Layout.fillWidth: true
|
|
|
|
Repeater {
|
|
id: availablePluginsRepeater
|
|
|
|
model: {
|
|
var all = PluginService.availablePlugins || [];
|
|
var filtered = [];
|
|
|
|
for (var i = 0; i < all.length; i++) {
|
|
var plugin = all[i];
|
|
var downloaded = plugin.downloaded || false;
|
|
|
|
if (pluginFilter === "all") {
|
|
filtered.push(plugin);
|
|
} else if (pluginFilter === "downloaded" && downloaded) {
|
|
filtered.push(plugin);
|
|
} else if (pluginFilter === "notDownloaded" && !downloaded) {
|
|
filtered.push(plugin);
|
|
}
|
|
}
|
|
|
|
return filtered;
|
|
}
|
|
|
|
delegate: NBox {
|
|
Layout.fillWidth: true
|
|
Layout.leftMargin: Style.borderS
|
|
Layout.rightMargin: Style.borderS
|
|
implicitHeight: Math.round(contentRow.implicitHeight + Style.marginL * 2)
|
|
color: Color.mSurface
|
|
|
|
RowLayout {
|
|
id: contentRow
|
|
anchors.fill: parent
|
|
anchors.margins: Style.marginL
|
|
spacing: Style.marginM
|
|
|
|
NIcon {
|
|
icon: "plugin"
|
|
pointSize: Style.fontSizeXL
|
|
color: Color.mOnSurface
|
|
}
|
|
|
|
ColumnLayout {
|
|
spacing: 2
|
|
Layout.fillWidth: true
|
|
|
|
NText {
|
|
text: modelData.name
|
|
font.weight: Font.Medium
|
|
color: Color.mOnSurface
|
|
elide: Text.ElideRight
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
NText {
|
|
text: modelData.description
|
|
font.pointSize: Style.fontSizeXS
|
|
color: Color.mOnSurfaceVariant
|
|
wrapMode: Text.WordWrap
|
|
maximumLineCount: 2
|
|
elide: Text.ElideRight
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
RowLayout {
|
|
spacing: Style.marginS
|
|
|
|
NText {
|
|
text: "v" + modelData.version
|
|
font.pointSize: Style.fontSizeXXS
|
|
color: Color.mOnSurfaceVariant
|
|
}
|
|
|
|
NText {
|
|
text: "•"
|
|
font.pointSize: Style.fontSizeXXS
|
|
color: Color.mOnSurfaceVariant
|
|
}
|
|
|
|
NText {
|
|
text: stripAuthorEmail(modelData.author)
|
|
font.pointSize: Style.fontSizeXXS
|
|
color: Color.mOnSurfaceVariant
|
|
}
|
|
|
|
NText {
|
|
text: "•"
|
|
font.pointSize: Style.fontSizeXXS
|
|
color: Color.mOnSurfaceVariant
|
|
}
|
|
|
|
NText {
|
|
text: modelData.source?.name || "Unknown"
|
|
font.pointSize: Style.fontSizeXS
|
|
color: Color.mOnSurfaceVariant
|
|
}
|
|
}
|
|
}
|
|
|
|
// Downloaded indicator
|
|
NIcon {
|
|
icon: "circle-check"
|
|
pointSize: Style.fontSizeXL
|
|
color: Color.mPrimary
|
|
visible: modelData.downloaded === true
|
|
}
|
|
|
|
// Install/Uninstall button
|
|
NIconButton {
|
|
icon: modelData.downloaded ? "trash" : "download"
|
|
baseSize: Style.baseWidgetSize * 0.7
|
|
tooltipText: modelData.downloaded ? I18n.tr("settings.plugins.uninstall") : I18n.tr("settings.plugins.install")
|
|
onClicked: {
|
|
if (modelData.downloaded) {
|
|
uninstallDialog.pluginToUninstall = modelData;
|
|
uninstallDialog.open();
|
|
} else {
|
|
installPlugin(modelData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NLabel {
|
|
visible: availablePluginsRepeater.count === 0
|
|
label: I18n.tr("settings.plugins.available.no-plugins-label")
|
|
description: I18n.tr("settings.plugins.available.no-plugins-description")
|
|
Layout.fillWidth: true
|
|
}
|
|
}
|
|
|
|
NDivider {
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: Style.marginL
|
|
Layout.bottomMargin: Style.marginL
|
|
}
|
|
|
|
// ------------------------------
|
|
// Dialogs
|
|
// ------------------------------
|
|
|
|
// Add source dialog
|
|
Popup {
|
|
id: addSourceDialog
|
|
parent: Overlay.overlay
|
|
modal: true
|
|
dim: false
|
|
anchors.centerIn: parent
|
|
width: 500
|
|
padding: Style.marginL
|
|
|
|
background: Rectangle {
|
|
color: Color.mSurface
|
|
radius: Style.radiusS
|
|
border.color: Color.mPrimary
|
|
border.width: Style.borderM
|
|
}
|
|
|
|
contentItem: ColumnLayout {
|
|
width: parent.width
|
|
spacing: Style.marginL
|
|
|
|
NHeader {
|
|
label: I18n.tr("settings.plugins.sources.add-dialog.title")
|
|
description: I18n.tr("settings.plugins.sources.add-dialog.description")
|
|
}
|
|
|
|
NTextInput {
|
|
id: sourceNameInput
|
|
label: I18n.tr("settings.plugins.sources.add-dialog.name")
|
|
placeholderText: I18n.tr("settings.plugins.sources.add-dialog.name-placeholder")
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
NTextInput {
|
|
id: sourceUrlInput
|
|
label: I18n.tr("settings.plugins.sources.add-dialog.url")
|
|
placeholderText: "https://github.com/user/repo"
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
RowLayout {
|
|
spacing: Style.marginM
|
|
Layout.fillWidth: true
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
NButton {
|
|
text: I18n.tr("common.cancel")
|
|
onClicked: addSourceDialog.close()
|
|
}
|
|
|
|
NButton {
|
|
text: I18n.tr("common.add")
|
|
backgroundColor: Color.mPrimary
|
|
textColor: Color.mOnPrimary
|
|
enabled: sourceNameInput.text.length > 0 && sourceUrlInput.text.length > 0
|
|
onClicked: {
|
|
if (PluginRegistry.addPluginSource(sourceNameInput.text, sourceUrlInput.text)) {
|
|
ToastService.showNotice(I18n.tr("settings.plugins.sources.add-dialog.success"));
|
|
PluginService.refreshAvailablePlugins();
|
|
addSourceDialog.close();
|
|
sourceNameInput.text = "";
|
|
sourceUrlInput.text = "";
|
|
} else {
|
|
ToastService.showNotice(I18n.tr("settings.plugins.sources.add-dialog.error"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Uninstall confirmation dialog
|
|
Popup {
|
|
id: uninstallDialog
|
|
parent: Overlay.overlay
|
|
modal: true
|
|
dim: false
|
|
anchors.centerIn: parent
|
|
width: 400 * Style.uiScaleRatio
|
|
padding: Style.marginL
|
|
|
|
property var pluginToUninstall: null
|
|
|
|
background: Rectangle {
|
|
color: Color.mSurface
|
|
radius: Style.radiusS
|
|
border.color: Color.mPrimary
|
|
border.width: Style.borderM
|
|
}
|
|
|
|
contentItem: ColumnLayout {
|
|
width: parent.width
|
|
spacing: Style.marginL
|
|
|
|
NHeader {
|
|
label: I18n.tr("settings.plugins.uninstall-dialog.title")
|
|
description: I18n.tr("settings.plugins.uninstall-dialog.description", {
|
|
"plugin": uninstallDialog.pluginToUninstall?.name || ""
|
|
})
|
|
}
|
|
|
|
RowLayout {
|
|
spacing: Style.marginM
|
|
Layout.fillWidth: true
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
NButton {
|
|
text: I18n.tr("common.cancel")
|
|
onClicked: uninstallDialog.close()
|
|
}
|
|
|
|
NButton {
|
|
text: I18n.tr("settings.plugins.uninstall")
|
|
backgroundColor: Color.mPrimary
|
|
textColor: Color.mOnPrimary
|
|
onClicked: {
|
|
if (uninstallDialog.pluginToUninstall) {
|
|
root.uninstallPlugin(uninstallDialog.pluginToUninstall.id);
|
|
uninstallDialog.close();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Plugin settings popup
|
|
NPluginSettingsPopup {
|
|
id: pluginSettingsDialog
|
|
parent: Overlay.overlay
|
|
showToastOnSave: true
|
|
}
|
|
|
|
// ------------------------------
|
|
// Functions
|
|
// ------------------------------
|
|
|
|
function installPlugin(pluginMetadata) {
|
|
ToastService.showNotice(I18n.tr("settings.plugins.installing", {
|
|
"plugin": pluginMetadata.name
|
|
}));
|
|
|
|
PluginService.installPlugin(pluginMetadata, function (success, error) {
|
|
if (success) {
|
|
ToastService.showNotice(I18n.tr("settings.plugins.install-success", {
|
|
"plugin": pluginMetadata.name
|
|
}));
|
|
// Auto-enable the plugin after installation
|
|
PluginService.enablePlugin(pluginMetadata.id);
|
|
} else {
|
|
ToastService.showNotice(I18n.tr("settings.plugins.install-error", {
|
|
"error": error || "Unknown error"
|
|
}));
|
|
}
|
|
});
|
|
}
|
|
|
|
function uninstallPlugin(pluginId) {
|
|
var manifest = PluginRegistry.getPluginManifest(pluginId);
|
|
var pluginName = manifest?.name || pluginId;
|
|
|
|
ToastService.showNotice(I18n.tr("settings.plugins.uninstalling", {
|
|
"plugin": pluginName
|
|
}));
|
|
|
|
PluginService.uninstallPlugin(pluginId, function (success, error) {
|
|
if (success) {
|
|
ToastService.showNotice(I18n.tr("settings.plugins.uninstall-success", {
|
|
"plugin": pluginName
|
|
}));
|
|
} else {
|
|
ToastService.showNotice(I18n.tr("settings.plugins.uninstall-error", {
|
|
"error": error || "Unknown error"
|
|
}));
|
|
}
|
|
});
|
|
}
|
|
|
|
// Listen to plugin registry changes
|
|
Connections {
|
|
target: PluginRegistry
|
|
|
|
function onPluginsChanged() {
|
|
// Force model refresh for installed plugins by incrementing counter
|
|
root.installedPluginsRefreshCounter++;
|
|
|
|
// Force model refresh for plugin sources
|
|
pluginSourcesRepeater.model = undefined;
|
|
Qt.callLater(function () {
|
|
pluginSourcesRepeater.model = Qt.binding(function () {
|
|
return PluginRegistry.pluginSources || [];
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
// Listen to plugin service signals
|
|
Connections {
|
|
target: PluginService
|
|
|
|
function onAvailablePluginsUpdated() {
|
|
// Force model refresh for available plugins
|
|
availablePluginsRepeater.model = undefined;
|
|
Qt.callLater(function () {
|
|
availablePluginsRepeater.model = Qt.binding(function () {
|
|
var all = PluginService.availablePlugins || [];
|
|
var filtered = [];
|
|
|
|
for (var i = 0; i < all.length; i++) {
|
|
var plugin = all[i];
|
|
var downloaded = plugin.downloaded || false;
|
|
|
|
if (root.pluginFilter === "all") {
|
|
filtered.push(plugin);
|
|
} else if (root.pluginFilter === "downloaded" && downloaded) {
|
|
filtered.push(plugin);
|
|
} else if (root.pluginFilter === "notDownloaded" && !downloaded) {
|
|
filtered.push(plugin);
|
|
}
|
|
}
|
|
|
|
return filtered;
|
|
});
|
|
});
|
|
|
|
// Manually trigger update check after a small delay to ensure all registries are loaded
|
|
Qt.callLater(function () {
|
|
PluginService.checkForUpdates();
|
|
});
|
|
}
|
|
|
|
function onPluginUpdatesChanged() {
|
|
// Increment counter to force installed plugins model refresh
|
|
root.installedPluginsRefreshCounter++;
|
|
}
|
|
}
|
|
}
|