diff --git a/assets/screenshot.png b/assets/screenshot.png index 4009912..b53f136 100644 Binary files a/assets/screenshot.png and b/assets/screenshot.png differ diff --git a/snx-vpn-indicator@diegodario88.github.io/extension.js b/snx-vpn-indicator@diegodario88.github.io/extension.js index 1bf1916..31e09a4 100644 --- a/snx-vpn-indicator@diegodario88.github.io/extension.js +++ b/snx-vpn-indicator@diegodario88.github.io/extension.js @@ -18,15 +18,14 @@ * If this extension breaks your desktop you get to keep all of the pieces... */ -const { NM } = imports.gi; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const Util = Me.imports.util; -const Indicator = Me.imports.indicator; - -class Extension { - constructor() { +import NM from 'gi://NM'; +import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js'; +import { SnxIndicator } from './indicator.js'; +import { CONSTANTS, NMDeviceStateReason } from './util.js'; + +export default class SnxVPNExtension extends Extension { + constructor(metadata) { + super(metadata); this._indicator = null; this._networkManagerClient = null; } @@ -41,7 +40,7 @@ class Extension { enable() { this._networkManagerClient = NM.Client.new(null); const hasSNX = this.isTunsnxDevicePresent(); - this._indicator = new Indicator.SnxIndicator(hasSNX); + this._indicator = new SnxIndicator(hasSNX, this.path); this._networkManagerClient.connect( 'any-device-added', @@ -83,7 +82,7 @@ class Extension { return this._networkManagerClient .get_devices() .map((device) => device.get_description().trim()) - .some((description) => description === Util.CONSTANTS['SNX_DEVICE_NAME']); + .some((description) => description === CONSTANTS['SNX_DEVICE_NAME']); } /** @@ -94,7 +93,7 @@ class Extension { */ handleOnAnyDeviceAdded(_, device) { const description = device.get_description(); - const shouldAvoid = description !== Util.CONSTANTS['SNX_DEVICE_NAME']; + const shouldAvoid = description !== CONSTANTS['SNX_DEVICE_NAME']; if (shouldAvoid) { return; @@ -111,30 +110,14 @@ class Extension { */ handleOnAnyDeviceRemoved(_, device) { const description = device.get_description(); - const shouldAvoid = description !== Util.CONSTANTS['SNX_DEVICE_NAME']; + const shouldAvoid = description !== CONSTANTS['SNX_DEVICE_NAME']; if (shouldAvoid) { return; } const stateReasonCode = device.get_state_reason(); - const reason = Util.NMDeviceStateReason[stateReasonCode]; + const reason = NMDeviceStateReason[stateReasonCode]; this._indicator.hide(reason); } } - -/** - * This function is called once when your extension is loaded, not enabled. This - * is a good time to setup translations or anything else you only do once. - * - * You MUST NOT make any changes to GNOME Shell, connect any signals or add any - * MainLoop sources here. - * - * @param {ExtensionMeta} meta - An extension meta object, described below. - * @returns {Object} an object with enable() and disable() methods - */ -function init(meta) { - log(`initializing ${meta.metadata.name} version ${meta.metadata.version}`); - ExtensionUtils.initTranslations(); - return new Extension(); -} diff --git a/snx-vpn-indicator@diegodario88.github.io/indicator.js b/snx-vpn-indicator@diegodario88.github.io/indicator.js index 28e6eb6..489954c 100644 --- a/snx-vpn-indicator@diegodario88.github.io/indicator.js +++ b/snx-vpn-indicator@diegodario88.github.io/indicator.js @@ -1,44 +1,28 @@ -const { GObject } = imports.gi; -const QuickSettingsMenu = imports.ui.main.panel.statusArea.quickSettings; -const QuickSettings = imports.ui.quickSettings; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const Util = Me.imports.util; -const Toggle = Me.imports.toggle; - -var SnxIndicator = GObject.registerClass( - class SnxIndicator extends QuickSettings.SystemIndicator { - _init(hasTunsnxDevice = false) { +import GObject from 'gi://GObject'; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import { + QuickSettingsMenu, + SystemIndicator +} from 'resource:///org/gnome/shell/ui/quickSettings.js'; +import { SnxToggle } from './toggle.js'; +import { CONSTANTS } from './util.js'; + +export const SnxIndicator = GObject.registerClass( + class SnxIndicator extends SystemIndicator { + _init(hasTunsnxDevice = false, cwd) { super._init(); this._indicator = this._addIndicator(); - this._indicator.icon_name = Util.CONSTANTS['ENABLED_VPN_ICON']; + this._indicator.icon_name = CONSTANTS['ENABLED_VPN_ICON']; this._indicator.visible = hasTunsnxDevice; - this._snxToggle = new Toggle.SnxToggle(hasTunsnxDevice); + this._snxToggle = new SnxToggle(hasTunsnxDevice, cwd); this.quickSettingsItems.push(this._snxToggle); this.connect('destroy', () => { this.quickSettingsItems.forEach((item) => item.destroy()); }); - QuickSettingsMenu._indicators.insert_child_at_index(this, 0); - this.addQuickSettingsItems(); - } - - addQuickSettingsItems() { - QuickSettingsMenu._addItems(this.quickSettingsItems); - - if (Util.getGnomeShellVersion() < 44) { - return; - } - - for (const item of this.quickSettingsItems) { - QuickSettingsMenu.menu._grid.set_child_below_sibling( - item, - QuickSettingsMenu._backgroundApps.quickSettingsItems[0] - ); - } + Main.panel.statusArea.quickSettings.addExternalIndicator(this); } /** @@ -48,14 +32,14 @@ var SnxIndicator = GObject.registerClass( log(`[SnxIndicator] hide: ${reason}`); this._indicator.visible = false; this._snxToggle.checked = false; - this._snxToggle.icon_name = Util.CONSTANTS['DISABLED_VPN_ICON']; + this._snxToggle.icon_name = CONSTANTS['DISABLED_VPN_ICON']; this._snxToggle._removeSessionParameters(); } show() { this._indicator.visible = true; this._snxToggle.checked = true; - this._snxToggle.icon_name = Util.CONSTANTS['ENABLED_VPN_ICON']; + this._snxToggle.icon_name = CONSTANTS['ENABLED_VPN_ICON']; } } ); diff --git a/snx-vpn-indicator@diegodario88.github.io/metadata.json b/snx-vpn-indicator@diegodario88.github.io/metadata.json index 21852ef..1e18450 100644 --- a/snx-vpn-indicator@diegodario88.github.io/metadata.json +++ b/snx-vpn-indicator@diegodario88.github.io/metadata.json @@ -1,10 +1,7 @@ { "uuid": "snx-vpn-indicator@diegodario88.github.io", - "version": 4, - "shell-version": [ - "43", - "44" - ], + "version": 5, + "shell-version": [ "45" ], "url": "https://github.com/diegodario88/snx-vpn-indicator", "name": "SNX VPN Indicator", "description": "This extension adds VPN functionality to the quickSettings by integrating the SSL Network Extender (SNX CLI) client", diff --git a/snx-vpn-indicator@diegodario88.github.io/toggle.js b/snx-vpn-indicator@diegodario88.github.io/toggle.js index 193b83e..8e60959 100644 --- a/snx-vpn-indicator@diegodario88.github.io/toggle.js +++ b/snx-vpn-indicator@diegodario88.github.io/toggle.js @@ -1,43 +1,48 @@ -const Main = imports.ui.main; -const { Gio, GObject } = imports.gi; -const QuickSettings = imports.ui.quickSettings; -const PopupMenu = imports.ui.popupMenu; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const Util = Me.imports.util; - -var SnxToggle = GObject.registerClass( - class SnxToggle extends QuickSettings.QuickMenuToggle { - _init(hasTunsnxDevice = false) { +import GObject from 'gi://GObject'; +import Gio from 'gi://Gio'; +import { + PopupMenuSection, + PopupSeparatorMenuItem, + PopupSwitchMenuItem, + PopupMenuItem +} from 'resource:///org/gnome/shell/ui/popupMenu.js'; +import { QuickMenuToggle } from 'resource:///org/gnome/shell/ui/quickSettings.js'; +import { + CONSTANTS, + VPN_NOTIFY, + execCommunicate, + parseSessionParameters +} from './util.js'; + +export const SnxToggle = GObject.registerClass( + class SnxToggle extends QuickMenuToggle { + _init(hasTunsnxDevice = false, cwd) { const config = { toggleMode: true, hasMenu: true, checked: hasTunsnxDevice }; - if (Util.getGnomeShellVersion() > 43) { - config.title = Util.CONSTANTS['SNX_LABEL']; - } else { - config.label = Util.CONSTANTS['SNX_LABEL']; - } + config.title = CONSTANTS['SNX_LABEL']; super._init(config); + this.cwd = cwd; this.icon_name = hasTunsnxDevice - ? Util.CONSTANTS['ENABLED_VPN_ICON'] - : Util.CONSTANTS['DISABLED_VPN_ICON']; + ? CONSTANTS['ENABLED_VPN_ICON'] + : CONSTANTS['DISABLED_VPN_ICON']; this.previousCancellable = null; - this._mainItemsSection = new PopupMenu.PopupMenuSection(); - this._separator = new PopupMenu.PopupSeparatorMenuItem('Connector'); + this._mainItemsSection = new PopupMenuSection(); + this._separator = new PopupSeparatorMenuItem('Connector'); - this._popupSwitchMenuItem = new PopupMenu.PopupSwitchMenuItem( - Util.CONSTANTS['SNX_LABEL_EXTENDED'], + this._popupSwitchMenuItem = new PopupSwitchMenuItem( + CONSTANTS['SNX_LABEL_EXTENDED'], this.checked ); this._mainItemsSection.addMenuItem(this._popupSwitchMenuItem); - this.menu.setHeader(Util.CONSTANTS['ENABLED_VPN_ICON'], _('VPN')); + this.menu.setHeader(CONSTANTS['ENABLED_VPN_ICON'], _('VPN')); this.menu.addMenuItem(this._mainItemsSection); this.menu.addMenuItem(this._separator); @@ -67,13 +72,11 @@ var SnxToggle = GObject.registerClass( */ _addSessionParameters(loginResponse) { this._removeSessionParameters(); - const sessionParams = Util.parseSessionParameters(loginResponse); + const sessionParams = parseSessionParameters(loginResponse); sessionParams.forEach((session) => this.menu.addMenuItem( - new PopupMenu.PopupMenuItem( - `${session.label.trim()} : ${session.value.trim()}` - ) + new PopupMenuItem(`${session.label.trim()} : ${session.value.trim()}`) ) ); @@ -83,7 +86,7 @@ var SnxToggle = GObject.registerClass( _removeSessionParameters() { const items = this.menu._getMenuItems(); items.forEach((item) => { - if (item instanceof PopupMenu.PopupMenuItem) { + if (item instanceof PopupMenuItem) { item.destroy(); } }); @@ -98,7 +101,7 @@ var SnxToggle = GObject.registerClass( */ async _handleCheckedAction(cancellable) { try { - const passwordPromptOutput = await Util.execCommunicate( + const passwordPromptOutput = await execCommunicate( [ 'zenity', '--password', @@ -116,8 +119,8 @@ var SnxToggle = GObject.registerClass( }); } - const stdout = await Util.execCommunicate( - [`${Me.dir.get_path()}/bridge-snx-cli.sh`, passwordPromptOutput], + const stdout = await execCommunicate( + [`${this.cwd}/bridge-snx-cli.sh`, passwordPromptOutput], null ); @@ -136,21 +139,18 @@ var SnxToggle = GObject.registerClass( this._addSessionParameters(loginResponse); - Util.VPN_NOTIFY( + VPN_NOTIFY( _('Successfully connected to VPN'), - Util.CONSTANTS['ENABLED_VPN_ICON'] + CONSTANTS['ENABLED_VPN_ICON'] ); } catch (error) { logError(error); if (error.code !== 14) { - Util.VPN_NOTIFY( - _(error.message), - Util.CONSTANTS['NO_ROUTE_VPN_ICON'] - ); + VPN_NOTIFY(_(error.message), CONSTANTS['NO_ROUTE_VPN_ICON']); } this.checked = false; - this.icon_name = Util.CONSTANTS['DISABLED_VPN_ICON']; + this.icon_name = CONSTANTS['DISABLED_VPN_ICON']; } } @@ -161,16 +161,16 @@ var SnxToggle = GObject.registerClass( */ async _handleUncheckedAction(cancellable) { try { - const output = await Util.execCommunicate( + const output = await execCommunicate( ['/usr/bin/snx', '-d'], null, cancellable ); - Util.VPN_NOTIFY(_(output), Util.CONSTANTS['DISCONNECTED_VPN_ICON']); + VPN_NOTIFY(_(output), CONSTANTS['DISCONNECTED_VPN_ICON']); } catch (error) { logError(error); - Util.VPN_NOTIFY(_(error.message), Util.CONSTANTS['NO_ROUTE_VPN_ICON']); + VPN_NOTIFY(_(error.message), CONSTANTS['NO_ROUTE_VPN_ICON']); } } @@ -180,7 +180,7 @@ var SnxToggle = GObject.registerClass( } const cancellable = new Gio.Cancellable(); - this.icon_name = Util.CONSTANTS['ACQUIRING_VPN_ICON']; + this.icon_name = CONSTANTS['ACQUIRING_VPN_ICON']; if (this.checked) { this._handleCheckedAction(cancellable); diff --git a/snx-vpn-indicator@diegodario88.github.io/util.js b/snx-vpn-indicator@diegodario88.github.io/util.js index 856ee0e..370352a 100644 --- a/snx-vpn-indicator@diegodario88.github.io/util.js +++ b/snx-vpn-indicator@diegodario88.github.io/util.js @@ -4,9 +4,13 @@ * @property {string} value - 192.123.124.1 */ -const { Gio, GLib } = imports.gi; -const Config = imports.misc.config; -const [major, minor] = Config.PACKAGE_VERSION.split('.').map((s) => Number(s)); +import GLib from 'gi://GLib'; +import Gio from 'gi://Gio'; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import { + Notification, + Source +} from 'resource:///org/gnome/shell/ui/messageTray.js'; /** * Execute a command asynchronously and return the output from `stdout` on @@ -20,7 +24,7 @@ const [major, minor] = Config.PACKAGE_VERSION.split('.').map((s) => Number(s)); * @param {Gio.Cancellable} [cancellable] - optional cancellable object * @returns {Promise} - The process output */ -async function execCommunicate(argv, input = null, cancellable = null) { +export async function execCommunicate(argv, input = null, cancellable = null) { let cancelId = 0; let flags = Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_PIPE; @@ -70,7 +74,7 @@ async function execCommunicate(argv, input = null, cancellable = null) { * @param {string} formattedString - The formatted string to extract parameters from. * @returns {SessionParameters[]} An array containing the extracted session parameters. */ -function parseSessionParameters(formattedString) { +export function parseSessionParameters(formattedString) { return formattedString .split(/\r?\n|\r/g) .splice(4, 4) @@ -84,15 +88,12 @@ function parseSessionParameters(formattedString) { * @param {string} body * @param {string} icon */ -function VPN_NOTIFY(body, icon) { - const source = new imports.ui.messageTray.Source( - _(CONSTANTS['SNX_LABEL']), - icon - ); +export function VPN_NOTIFY(body, icon) { + const source = new Source(_(CONSTANTS['SNX_LABEL']), icon); - imports.ui.main.messageTray.add(source); + Main.messageTray.add(source); - const notification = new imports.ui.messageTray.Notification( + const notification = new Notification( source, _(CONSTANTS['SNX_LABEL_EXTENDED']), body @@ -102,15 +103,7 @@ function VPN_NOTIFY(body, icon) { source.showNotification(notification); } -/** - * - * @returns {number} major version - */ -function getGnomeShellVersion() { - return major; -} - -var CONSTANTS = { +export const CONSTANTS = { SNX_DEVICE_NAME: 'tunsnx', SNX_LABEL: 'SNX VPN', SNX_LABEL_EXTENDED: 'SSL Network Extender', @@ -122,7 +115,7 @@ var CONSTANTS = { HOME_DIR: GLib.get_home_dir() }; -var NMDeviceStateReason = { +export const NMDeviceStateReason = { 0: 'No reason given', 1: 'Unknown error', 2: 'Device is now managed',