import { html, render, keyed } from "/js/lib/lit-html.js"; import { pageEffect, bindPageTitle } from "/js/router.js"; import { headerTemplate } from "/js/templates/header.template.js"; import "/js/components/app-icon.js"; import { confirmModal } from "/js/modals/confirm.modal.js"; import { showToast } from "/js/toasts.js"; import { Signal, SignalSet, ReactiveStore } from "/js/signals.js"; import { PermissionsDeclinedError } from "/js/plugins/pluginService.js"; import { linkToPluginSettings, linkToCommunityPlugin } from "/js/navigation.js"; import "/js/components/toggle-switch.js"; export default async function installedPluginsView({ root, router, context: { auth, pluginService }, }) { await auth.requireAuth(); const state = new ReactiveStore("installedPluginsView"); state.$uninstallingIds = new SignalSet(); state.$enablingIds = new SignalSet(); state.$disablingIds = new SignalSet(); state.$updatingIds = new SignalSet(); state.$reloading = new Signal.State(false); state.$checkingForUpdates = new Signal.State(false); state.$updatingAll = new Signal.State(false); async function uninstallPlugin(plugin) { const confirmed = await confirmModal( `"${plugin.name}" will be uninstalled and its settings will be deleted.`, { title: "Uninstall plugin?", confirmButtonStyle: "danger", confirmButtonText: "Uninstall", }, ); if (!confirmed) return; state.$uninstallingIds.add(plugin.id); try { await pluginService.uninstallPlugin(plugin.id); showToast(`Uninstalled ${plugin.name}`); } finally { state.$uninstallingIds.delete(plugin.id); } } async function reloadPlugins() { if (state.$reloading.get()) return; state.$reloading.set(true); try { await pluginService.reloadPlugins(); showToast("Reloaded plugins"); } catch (e) { console.error(e); showToast("Failed to reload plugins", { style: "error" }); } finally { state.$reloading.set(false); } } async function checkForUpdates() { if (state.$checkingForUpdates.get()) return; state.$checkingForUpdates.set(true); try { const updates = await pluginService.checkForUpdates(); if (updates.size === 0) { showToast("All plugins are up to date", { style: "success" }); } else { showToast( `${updates.size} update${updates.size === 1 ? "" : "s"} available`, ); } } catch (e) { showToast("Failed to check for updates", { style: "error" }); } finally { state.$checkingForUpdates.set(false); } } async function updatePlugin(plugin) { state.$updatingIds.add(plugin.id); try { const result = await pluginService.updatePlugin(plugin.id); if (result.updated) { showToast(`Updated ${plugin.name} to v${result.version}`, { style: "success", }); } } catch (e) { if (e instanceof PermissionsDeclinedError) { // User declined the permission prompt; nothing to report. } else { console.error(e); showToast(`Failed to update ${plugin.name}`, { style: "error", }); } } finally { state.$updatingIds.delete(plugin.id); } } async function updateAllPlugins() { if (state.$updatingAll.get()) return; state.$updatingAll.set(true); try { const { updated, failed } = await pluginService.updateAllPlugins(); if (failed.length > 0) { showToast(`Updated ${updated.length}, failed ${failed.length}`, { style: "error", }); } else { showToast( `Updated ${updated.length} plugin${updated.length === 1 ? "" : "s"}`, { style: "success" }, ); } } finally { state.$updatingAll.set(false); } } async function togglePlugin(plugin) { const pendingIds = plugin.enabled ? state.$disablingIds : state.$enablingIds; pendingIds.add(plugin.id); try { if (plugin.enabled) { await pluginService.disablePlugin(plugin.id); showToast(`Disabled ${plugin.name}`); } else { try { await pluginService.enablePlugin(plugin.id); showToast(`Enabled ${plugin.name}`, { style: "success" }); } catch (e) { const message = e.message ?? String(e); showToast(`Error when loading ${plugin.name} - ${message}`, { style: "error", }); } } } finally { pendingIds.delete(plugin.id); } } bindPageTitle(root, () => "Plugins"); pageEffect(root, () => { const reloading = state.$reloading.get(); const checkingForUpdates = state.$checkingForUpdates.get(); const updatingAll = state.$updatingAll.get(); const pluginsInfo = pluginService.$pluginsInfo.get(); const availableUpdates = pluginService.$availableUpdates.get(); const hasAvailableUpdates = availableUpdates !== null && availableUpdates.size > 0; render( html`
${headerTemplate({ title: "Plugins", })}
Browse community plugins Discover plugins built by the community ${!pluginsInfo ? html`

Loading…

` : pluginsInfo.length === 0 ? html`
No plugins installed

Browse the community registry to find and install plugins.

` : html`

Installed plugins

`}
`, root, ); }); }