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

View File

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

View File

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