mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Making extension components observable
Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
parent
51231ae2df
commit
993c3746b1
@ -61,7 +61,7 @@ export class ExtensionLoader extends Singleton {
|
||||
whenLoaded = when(() => this.isLoaded);
|
||||
|
||||
@computed get userExtensions(): Map<LensExtensionId, InstalledExtension> {
|
||||
const extensions = this.extensions.toJS();
|
||||
const extensions = this.toJSON();
|
||||
|
||||
extensions.forEach((ext, extId) => {
|
||||
if (ext.isBundled) {
|
||||
@ -86,6 +86,10 @@ export class ExtensionLoader extends Singleton {
|
||||
return extensions;
|
||||
}
|
||||
|
||||
@computed get userExtensionList() {
|
||||
return Array.from(this.userExtensions.values());
|
||||
}
|
||||
|
||||
getExtensionByName(name: string): LensExtension | null {
|
||||
for (const [, val] of this.instances) {
|
||||
if (val.name === name) {
|
||||
|
||||
@ -24,7 +24,7 @@ import "./extensions.scss";
|
||||
import { remote, shell } from "electron";
|
||||
import fse from "fs-extra";
|
||||
import _ from "lodash";
|
||||
import { computed, observable, reaction, when } from "mobx";
|
||||
import { observable, reaction, when } from "mobx";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import os from "os";
|
||||
import path from "path";
|
||||
@ -33,7 +33,6 @@ import { SemVer } from "semver";
|
||||
import URLParse from "url-parse";
|
||||
|
||||
import {
|
||||
autobind,
|
||||
Disposer,
|
||||
disposer,
|
||||
downloadFile,
|
||||
@ -55,11 +54,9 @@ import {
|
||||
import logger from "../../../main/logger";
|
||||
import { Button } from "../button";
|
||||
import { ConfirmDialog } from "../confirm-dialog";
|
||||
import { Icon } from "../icon";
|
||||
import { DropFileInput, InputValidators } from "../input";
|
||||
import { PageLayout } from "../layout/page-layout";
|
||||
import { Notifications } from "../notifications";
|
||||
import { Spinner } from "../spinner/spinner";
|
||||
import { ExtensionInstallationState, ExtensionInstallationStateStore } from "./extension-install.store";
|
||||
import { Install } from "./install";
|
||||
import { InstalledExtensions } from "./installed-extensions";
|
||||
@ -106,6 +103,22 @@ interface InstallRequestValidated {
|
||||
tempFile: string; // temp system path to packed extension for unpacking
|
||||
}
|
||||
|
||||
function setExtensionEnabled(id: LensExtensionId, isEnabled: boolean): void {
|
||||
const extension = ExtensionLoader.getInstance().getExtension(id);
|
||||
|
||||
if (extension) {
|
||||
extension.isEnabled = isEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
function enableExtension(id: LensExtensionId) {
|
||||
setExtensionEnabled(id, true);
|
||||
}
|
||||
|
||||
function disableExtension(id: LensExtensionId) {
|
||||
setExtensionEnabled(id, false);
|
||||
}
|
||||
|
||||
async function uninstallExtension(extensionId: LensExtensionId): Promise<boolean> {
|
||||
const loader = ExtensionLoader.getInstance();
|
||||
const { manifest } = loader.getExtension(extensionId);
|
||||
@ -482,19 +495,8 @@ async function installFromSelectFileDialog() {
|
||||
|
||||
@observer
|
||||
export class Extensions extends React.Component {
|
||||
@observable search = "";
|
||||
@observable installPath = "";
|
||||
|
||||
@computed get searchedForExtensions() {
|
||||
const searchText = this.search.toLowerCase();
|
||||
|
||||
return Array.from(ExtensionLoader.getInstance().userExtensions.values())
|
||||
.filter(({ manifest: { name, description }}) => (
|
||||
name.toLowerCase().includes(searchText)
|
||||
|| description?.toLowerCase().includes(searchText)
|
||||
));
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// TODO: change this after upgrading to mobx6 as that versions' reactions have this functionality
|
||||
let prevSize = ExtensionLoader.getInstance().userExtensions.size;
|
||||
@ -513,83 +515,8 @@ export class Extensions extends React.Component {
|
||||
]);
|
||||
}
|
||||
|
||||
renderNoExtensionsHelpText() {
|
||||
if (this.search) {
|
||||
return <p>No search results found</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<p>
|
||||
There are no installed extensions.
|
||||
See list of <a href="https://github.com/lensapp/lens-extensions/blob/main/README.md" target="_blank" rel="noreferrer">available extensions</a>.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
renderNoExtensions() {
|
||||
return (
|
||||
<div className="no-extensions flex box gaps justify-center">
|
||||
<Icon material="info" />
|
||||
<div>
|
||||
{this.renderNoExtensionsHelpText()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@autobind()
|
||||
renderExtension(extension: InstalledExtension) {
|
||||
const { id, isEnabled, manifest } = extension;
|
||||
const { name, description, version } = manifest;
|
||||
const isUninstalling = ExtensionInstallationStateStore.isExtensionUninstalling(id);
|
||||
|
||||
return (
|
||||
<div key={id} className="extension flex gaps align-center">
|
||||
<div className="box grow">
|
||||
<h5>{name}</h5>
|
||||
<h6>{version}</h6>
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
<div className="actions">
|
||||
{
|
||||
isEnabled
|
||||
? <Button accent disabled={isUninstalling} onClick={() => extension.isEnabled = false}>Disable</Button>
|
||||
: <Button plain active disabled={isUninstalling} onClick={() => extension.isEnabled = true}>Enable</Button>
|
||||
}
|
||||
<Button
|
||||
plain
|
||||
active
|
||||
disabled={isUninstalling}
|
||||
waiting={isUninstalling}
|
||||
onClick={() => confirmUninstallExtension(extension)}
|
||||
>
|
||||
Uninstall
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderExtensions() {
|
||||
if (!ExtensionDiscovery.getInstance().isLoaded) {
|
||||
return <div className="spinner-wrapper"><Spinner /></div>;
|
||||
}
|
||||
|
||||
const { searchedForExtensions } = this;
|
||||
|
||||
if (!searchedForExtensions.length) {
|
||||
return this.renderNoExtensions();
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{...searchedForExtensions.map(this.renderExtension)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const extensions = Array.from(ExtensionLoader.getInstance().userExtensions.values());
|
||||
const extensions = ExtensionLoader.getInstance().userExtensionList;
|
||||
|
||||
return (
|
||||
<DropFileInput onDropFiles={installOnDrop}>
|
||||
@ -611,6 +538,8 @@ export class Extensions extends React.Component {
|
||||
|
||||
<InstalledExtensions
|
||||
extensions={extensions}
|
||||
enable={enableExtension}
|
||||
disable={disableExtension}
|
||||
uninstall={confirmUninstallExtension}
|
||||
/>
|
||||
</section>
|
||||
|
||||
@ -7,6 +7,7 @@ import { Input, InputValidator, InputValidators } from "../input";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { TooltipPosition } from "../tooltip";
|
||||
import { ExtensionInstallationStateStore } from "./extension-install.store";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
interface Props {
|
||||
installPath: string;
|
||||
@ -29,7 +30,7 @@ const installInputValidator: InputValidator = {
|
||||
),
|
||||
};
|
||||
|
||||
export function Install(props: Props) {
|
||||
export const Install = observer((props: Props) => {
|
||||
const { installPath, supportedFormats, onChange, installFromInput, installFromSelectFileDialog } = props;
|
||||
|
||||
return (
|
||||
@ -63,7 +64,7 @@ export function Install(props: Props) {
|
||||
primary
|
||||
label="Install"
|
||||
className="w-80 h-full"
|
||||
// disabled={ExtensionInstallationStateStore.anyPreInstallingOrInstalling || !installInputValidator.validate(installPath)}
|
||||
disabled={ExtensionInstallationStateStore.anyPreInstallingOrInstalling}
|
||||
waiting={ExtensionInstallationStateStore.anyPreInstallingOrInstalling}
|
||||
onClick={installFromInput}
|
||||
/>
|
||||
@ -74,4 +75,5 @@ export function Install(props: Props) {
|
||||
</small>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -18,4 +18,8 @@
|
||||
.noItemsIcon {
|
||||
@apply opacity-10;
|
||||
--size: 180px;
|
||||
}
|
||||
|
||||
.frozenRow {
|
||||
@apply opacity-30 pointer-events-none;
|
||||
}
|
||||
@ -7,9 +7,13 @@ import { MenuActions, MenuItem } from "../menu";
|
||||
import { Spinner } from "../spinner";
|
||||
import { ExtensionInstallationStateStore } from "./extension-install.store";
|
||||
import { cssNames } from "../../utils";
|
||||
import { observer } from "mobx-react";
|
||||
import type { LensExtensionId } from "../../../extensions/lens-extension";
|
||||
|
||||
interface Props {
|
||||
extensions: InstalledExtension[];
|
||||
enable: (id: LensExtensionId) => void;
|
||||
disable: (id: LensExtensionId) => void;
|
||||
uninstall: (extension: InstalledExtension) => void;
|
||||
}
|
||||
|
||||
@ -17,7 +21,7 @@ function getStatus(isEnabled: boolean) {
|
||||
return isEnabled ? "Enabled" : "Disabled";
|
||||
}
|
||||
|
||||
export function InstalledExtensions({ extensions, uninstall }: Props) {
|
||||
export const InstalledExtensions = observer(({ extensions, uninstall, enable, disable }: Props) => {
|
||||
if (!ExtensionDiscovery.getInstance().isLoaded) {
|
||||
return <div><Spinner center /></div>;
|
||||
}
|
||||
@ -43,7 +47,7 @@ export function InstalledExtensions({ extensions, uninstall }: Props) {
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: "Extension",
|
||||
Header: "Name",
|
||||
accessor: "extension",
|
||||
width: 200
|
||||
},
|
||||
@ -74,7 +78,7 @@ export function InstalledExtensions({ extensions, uninstall }: Props) {
|
||||
|
||||
return {
|
||||
extension: (
|
||||
<div className="flex items-start">
|
||||
<div className={cssNames("flex items-start", { [styles.frozenRow]: isUninstalling })}>
|
||||
<div>
|
||||
<div className={styles.extensionName}>{name}</div>
|
||||
<div className={styles.extensionDescription}>{description}</div>
|
||||
@ -92,7 +96,7 @@ export function InstalledExtensions({ extensions, uninstall }: Props) {
|
||||
{isEnabled ? (
|
||||
<MenuItem
|
||||
disabled={isUninstalling}
|
||||
onClick={() => extension.isEnabled = false}
|
||||
onClick={() => disable(id)}
|
||||
>
|
||||
<Icon material="unpublished"/>
|
||||
<span className="title">Disable</span>
|
||||
@ -100,7 +104,7 @@ export function InstalledExtensions({ extensions, uninstall }: Props) {
|
||||
) : (
|
||||
<MenuItem
|
||||
disabled={isUninstalling}
|
||||
onClick={() => extension.isEnabled = true}
|
||||
onClick={() => enable(id)}
|
||||
>
|
||||
<Icon material="check_circle"/>
|
||||
<span className="title">Enable</span>
|
||||
@ -131,4 +135,4 @@ export function InstalledExtensions({ extensions, uninstall }: Props) {
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user