/* extension.js * * Copyright (C) 2025 Alexander Vanhee * * 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 2 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 . * * SPDX-License-Identifier: GPL-3.0-or-later */ import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js'; import * as Main from 'resource:///org/gnome/shell/ui/main.js'; import * as AppMenu from 'resource:///org/gnome/shell/ui/appMenu.js'; import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import Shell from 'gi://Shell'; import { showRemoveDialog } from './removeDialog.js'; export default class BazaarIntegration extends Extension { enable() { this._originalUpdateDetailsVisibility = AppMenu.AppMenu.prototype._updateDetailsVisibility; this._originalSetApp = AppMenu.AppMenu.prototype.setApp; const extension = this; AppMenu.AppMenu.prototype._updateDetailsVisibility = function() { const hasBazaar = this._appSystem.lookup_app('io.github.kolunmi.Bazaar.desktop') !== null; const hasGnomeSoftware = this._appSystem.lookup_app('org.gnome.Software.desktop') !== null; const isFlatpak = this._app ? extension._isFlatpakApp(this._app) : false; if (isFlatpak) { this._detailsItem.visible = hasBazaar; } else { this._detailsItem.visible = hasGnomeSoftware; } if (this._removeItem) { this._removeItem.visible = hasBazaar && isFlatpak; } }; AppMenu.AppMenu.prototype.setApp = function(app) { extension._originalSetApp.call(this, app); if (!this._bazaarHandlerPatched) { const items = this._getMenuItems(); const detailsIndex = items.indexOf(this._detailsItem); this._detailsItem.destroy(); this._detailsItem = new PopupMenu.PopupMenuItem(_('App Details')); this._detailsItem.connect('activate', () => { const isFlatpak = extension._isFlatpakApp(this._app); if (isFlatpak) { extension._openInBazaar(this._app); } else { extension._openInGnomeSoftware(this._app); } }); if (detailsIndex !== -1) { this.addMenuItem(this._detailsItem, detailsIndex); } else { this.addMenuItem(this._detailsItem); } this._bazaarHandlerPatched = true; } if (!this._removeItem) { this._removeItem = new PopupMenu.PopupMenuItem('Uninstall'); this._removeItem.connect('activate', () => { showRemoveDialog(this._app); }); const items = this._getMenuItems(); const detailsIndex = items.indexOf(this._detailsItem); if (detailsIndex !== -1) { this.addMenuItem(this._removeItem, detailsIndex + 1); } else { this.addMenuItem(this._removeItem); } } this._updateDetailsVisibility(); }; } disable() { if (this._originalUpdateDetailsVisibility) { AppMenu.AppMenu.prototype._updateDetailsVisibility = this._originalUpdateDetailsVisibility; this._originalUpdateDetailsVisibility = null; } if (this._originalSetApp) { AppMenu.AppMenu.prototype.setApp = this._originalSetApp; this._originalSetApp = null; } } _isFlatpakApp(app) { if (!app) return false; const appInfo = app.get_app_info(); if (!appInfo) return false; const filename = appInfo.get_filename(); if (!filename) return false; // Check if the Desktop file is in a Flatpak directory const isFlatpak = filename.includes('/flatpak/exports/share/applications/') || filename.includes('/var/lib/flatpak/') || filename.startsWith(GLib.get_home_dir() + '/.local/share/flatpak/'); console.log(`Bazaar Integration: Is Flatpak (by path): ${isFlatpak}`); return isFlatpak; } async _openInBazaar(app) { if (!app) return; const appId = app.get_id(); if (!appId) return; const cleanAppId = appId.replace(/\.desktop$/, ''); const appstreamUri = `appstream:${cleanAppId}`; const bazaarApp = Shell.AppSystem.get_default().lookup_app('io.github.kolunmi.Bazaar.desktop'); if (!bazaarApp) return; const appInfo = bazaarApp.get_app_info(); appInfo.launch_uris([appstreamUri], null); Main.overview.hide(); } async _openInGnomeSoftware(app) { if (!app) return; const id = app.get_id(); if (!id) return; const args = GLib.Variant.new('(ss)', [id, '']); const bus = await Gio.DBus.get(Gio.BusType.SESSION, null); bus.call( 'org.gnome.Software', '/org/gnome/Software', 'org.gtk.Actions', 'Activate', new GLib.Variant('(sava{sv})', ['details', [args], null]), null, 0, -1, null); Main.overview.hide(); } }