1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Enable installed extensions by default (#1572)

Signed-off-by: Panu Horsmalahti <phorsmalahti@mirantis.com>
This commit is contained in:
Panu Horsmalahti 2020-12-02 11:37:23 +02:00 committed by GitHub
parent f3f9f08c0a
commit 7798dce61d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 56 additions and 39 deletions

View File

@ -9,7 +9,7 @@ jest.mock(
() => ({ () => ({
ipcRenderer: { ipcRenderer: {
invoke: jest.fn(async (channel: string) => { invoke: jest.fn(async (channel: string) => {
if (channel === "extensions:loaded") { if (channel === "extensions:main") {
return [ return [
[ [
manifestPath, manifestPath,
@ -44,7 +44,7 @@ jest.mock(
}), }),
on: jest.fn( on: jest.fn(
(channel: string, listener: (event: any, ...args: any[]) => void) => { (channel: string, listener: (event: any, ...args: any[]) => void) => {
if (channel === "extensions:loaded") { if (channel === "extensions:main") {
// First initialize with extensions 1 and 2 // First initialize with extensions 1 and 2
// and then broadcast event to remove extensioin 2 and add extension number 3 // and then broadcast event to remove extensioin 2 and add extension number 3
setTimeout(() => { setTimeout(() => {

View File

@ -1,5 +1,6 @@
import { app, ipcRenderer, remote } from "electron"; import { app, ipcRenderer, remote } from "electron";
import { EventEmitter } from "events"; import { EventEmitter } from "events";
import { isEqual } from "lodash";
import { action, computed, observable, reaction, toJS, when } from "mobx"; import { action, computed, observable, reaction, toJS, when } from "mobx";
import path from "path"; import path from "path";
import { getHostedCluster } from "../common/cluster-store"; import { getHostedCluster } from "../common/cluster-store";
@ -25,7 +26,12 @@ const logModule = "[EXTENSIONS-LOADER]";
export class ExtensionLoader { export class ExtensionLoader {
protected extensions = observable.map<LensExtensionId, InstalledExtension>(); protected extensions = observable.map<LensExtensionId, InstalledExtension>();
protected instances = observable.map<LensExtensionId, LensExtension>(); protected instances = observable.map<LensExtensionId, LensExtension>();
protected readonly requestExtensionsChannel = "extensions:loaded";
// IPC channel to broadcast changes to extensions from main
protected static readonly extensionsMainChannel = "extensions:main";
// IPC channel to broadcast changes to extensions from renderer
protected static readonly extensionsRendererChannel = "extensions:renderer";
// emits event "remove" of type LensExtension when the extension is removed // emits event "remove" of type LensExtension when the extension is removed
private events = new EventEmitter(); private events = new EventEmitter();
@ -95,28 +101,27 @@ export class ExtensionLoader {
this.loadOnMain(); this.loadOnMain();
this.broadcastExtensions(); this.broadcastExtensions();
reaction(() => this.extensions.toJS(), () => { reaction(() => this.toJSON(), () => {
this.broadcastExtensions(); this.broadcastExtensions();
}); });
handleRequest(this.requestExtensionsChannel, () => { handleRequest(ExtensionLoader.extensionsMainChannel, () => {
return Array.from(this.toJSON()); return Array.from(this.toJSON());
}); });
subscribeToBroadcast(ExtensionLoader.extensionsRendererChannel, (_event, extensions: [LensExtensionId, InstalledExtension][]) => {
this.syncExtensions(extensions);
});
} }
protected async initRenderer() { protected async initRenderer() {
const extensionListHandler = (extensions: [LensExtensionId, InstalledExtension][]) => { const extensionListHandler = (extensions: [LensExtensionId, InstalledExtension][]) => {
this.isLoaded = true; this.isLoaded = true;
this.syncExtensions(extensions);
const receivedExtensionIds = extensions.map(([lensExtensionId]) => lensExtensionId); const receivedExtensionIds = extensions.map(([lensExtensionId]) => lensExtensionId);
// Add new extensions // Remove deleted extensions in renderer side only
extensions.forEach(([extId, ext]) => {
if (!this.extensions.has(extId)) {
this.extensions.set(extId, ext);
}
});
// Remove deleted extensions
this.extensions.forEach((_, lensExtensionId) => { this.extensions.forEach((_, lensExtensionId) => {
if (!receivedExtensionIds.includes(lensExtensionId)) { if (!receivedExtensionIds.includes(lensExtensionId)) {
this.removeExtension(lensExtensionId); this.removeExtension(lensExtensionId);
@ -124,14 +129,26 @@ export class ExtensionLoader {
}); });
}; };
requestMain(this.requestExtensionsChannel).then(extensionListHandler); reaction(() => this.toJSON(), () => {
subscribeToBroadcast(this.requestExtensionsChannel, (event, extensions: [LensExtensionId, InstalledExtension][]) => { this.broadcastExtensions(false);
});
requestMain(ExtensionLoader.extensionsMainChannel).then(extensionListHandler);
subscribeToBroadcast(ExtensionLoader.extensionsMainChannel, (_event, extensions: [LensExtensionId, InstalledExtension][]) => {
extensionListHandler(extensions); extensionListHandler(extensions);
}); });
} }
syncExtensions(extensions: [LensExtensionId, InstalledExtension][]) {
extensions.forEach(([lensExtensionId, extension]) => {
if (!isEqual(this.extensions.get(lensExtensionId), extension)) {
this.extensions.set(lensExtensionId, extension);
}
});
}
loadOnMain() { loadOnMain() {
logger.info(`${logModule}: load on main`); logger.debug(`${logModule}: load on main`);
this.autoInitExtensions(async (extension: LensMainExtension) => { this.autoInitExtensions(async (extension: LensMainExtension) => {
// Each .add returns a function to remove the item // Each .add returns a function to remove the item
const removeItems = [ const removeItems = [
@ -151,7 +168,7 @@ export class ExtensionLoader {
} }
loadOnClusterManagerRenderer() { loadOnClusterManagerRenderer() {
logger.info(`${logModule}: load on main renderer (cluster manager)`); logger.debug(`${logModule}: load on main renderer (cluster manager)`);
this.autoInitExtensions(async (extension: LensRendererExtension) => { this.autoInitExtensions(async (extension: LensRendererExtension) => {
const removeItems = [ const removeItems = [
registries.globalPageRegistry.add(extension.globalPages, extension), registries.globalPageRegistry.add(extension.globalPages, extension),
@ -174,7 +191,7 @@ export class ExtensionLoader {
} }
loadOnClusterRenderer() { loadOnClusterRenderer() {
logger.info(`${logModule}: load on cluster renderer (dashboard)`); logger.debug(`${logModule}: load on cluster renderer (dashboard)`);
const cluster = getHostedCluster(); const cluster = getHostedCluster();
this.autoInitExtensions(async (extension: LensRendererExtension) => { this.autoInitExtensions(async (extension: LensRendererExtension) => {
@ -204,26 +221,26 @@ export class ExtensionLoader {
protected autoInitExtensions(register: (ext: LensExtension) => Promise<Function[]>) { protected autoInitExtensions(register: (ext: LensExtension) => Promise<Function[]>) {
return reaction(() => this.toJSON(), installedExtensions => { return reaction(() => this.toJSON(), installedExtensions => {
for (const [extId, ext] of installedExtensions) { for (const [extId, extension] of installedExtensions) {
const alreadyInit = this.instances.has(extId); const alreadyInit = this.instances.has(extId);
if (ext.isEnabled && !alreadyInit) { if (extension.isEnabled && !alreadyInit) {
try { try {
const LensExtensionClass = this.requireExtension(ext); const LensExtensionClass = this.requireExtension(extension);
if (!LensExtensionClass) { if (!LensExtensionClass) {
continue; continue;
} }
const instance = new LensExtensionClass(ext); const instance = new LensExtensionClass(extension);
instance.whenEnabled(() => register(instance)); instance.whenEnabled(() => register(instance));
instance.enable(); instance.enable();
this.instances.set(extId, instance); this.instances.set(extId, instance);
} catch (err) { } catch (err) {
logger.error(`${logModule}: activation extension error`, { ext, err }); logger.error(`${logModule}: activation extension error`, { ext: extension, err });
} }
} else if (!ext.isEnabled && alreadyInit) { } else if (!extension.isEnabled && alreadyInit) {
this.removeInstance(extId); this.removeInstance(extId);
} }
} }
@ -262,8 +279,8 @@ export class ExtensionLoader {
}); });
} }
broadcastExtensions() { broadcastExtensions(main = true) {
broadcastMessage(this.requestExtensionsChannel, Array.from(this.toJSON())); broadcastMessage(main ? ExtensionLoader.extensionsMainChannel : ExtensionLoader.extensionsRendererChannel, Array.from(this.toJSON()));
} }
} }

View File

@ -45,17 +45,6 @@ export class ExtensionsStore extends BaseStore<LensExtensionsStoreModel> {
await extensionLoader.whenLoaded; await extensionLoader.whenLoaded;
await this.whenLoaded; await this.whenLoaded;
// apply state on changes from store
reaction(() => this.state.toJS(), extensionsState => {
extensionsState.forEach((state, extId) => {
const ext = extensionLoader.getExtension(extId);
if (ext && !ext.isBundled) {
ext.isEnabled = state.enabled;
}
});
});
// save state on change `extension.isEnabled` // save state on change `extension.isEnabled`
reaction(() => this.getState(extensionLoader), extensionsState => { reaction(() => this.getState(extensionLoader), extensionsState => {
this.state.merge(extensionsState); this.state.merge(extensionsState);
@ -65,7 +54,9 @@ export class ExtensionsStore extends BaseStore<LensExtensionsStoreModel> {
isEnabled(extId: LensExtensionId) { isEnabled(extId: LensExtensionId) {
const state = this.state.get(extId); const state = this.state.get(extId);
return state && state.enabled; // by default false // By default false, so that copied extensions are disabled by default.
// If user installs the extension from the UI, the Extensions component will specifically enable it.
return Boolean(state?.enabled);
} }
@action @action

View File

@ -91,11 +91,20 @@ export class Extensions extends React.Component {
}); });
this.addedInstalling.forEach(({ id, displayName }) => { this.addedInstalling.forEach(({ id, displayName }) => {
const extension = this.extensions.find(extension => extension.id === id);
if (!extension) {
throw new Error("Extension not found");
}
Notifications.ok( Notifications.ok(
<p>Extension <b>{displayName}</b> successfully installed!</p> <p>Extension <b>{displayName}</b> successfully installed!</p>
); );
this.extensionState.delete(id); this.extensionState.delete(id);
this.installPath = ""; this.installPath = "";
// Enable installed extensions by default.
extension.isEnabled = true;
}); });
}) })
); );