mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Only create TerminalStore after ThemeStore (#3359)
- Use Proxy for backwards compatability - Fix a bunch of unit tests that needed all the Singleton's created - Add comments on the order required for the store migrations Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
9e363b8d5f
commit
c335b0f054
@ -63,8 +63,7 @@
|
|||||||
},
|
},
|
||||||
"moduleNameMapper": {
|
"moduleNameMapper": {
|
||||||
"\\.(css|scss)$": "<rootDir>/__mocks__/styleMock.ts",
|
"\\.(css|scss)$": "<rootDir>/__mocks__/styleMock.ts",
|
||||||
"\\.(svg)$": "<rootDir>/__mocks__/imageMock.ts",
|
"\\.(svg)$": "<rootDir>/__mocks__/imageMock.ts"
|
||||||
"^@lingui/macro$": "<rootDir>/__mocks__/@linguiMacro.ts"
|
|
||||||
},
|
},
|
||||||
"modulePathIgnorePatterns": [
|
"modulePathIgnorePatterns": [
|
||||||
"<rootDir>/dist",
|
"<rootDir>/dist",
|
||||||
@ -218,7 +217,7 @@
|
|||||||
"mobx": "^6.3.0",
|
"mobx": "^6.3.0",
|
||||||
"mobx-observable-history": "^2.0.1",
|
"mobx-observable-history": "^2.0.1",
|
||||||
"mobx-react": "^7.1.0",
|
"mobx-react": "^7.1.0",
|
||||||
"mock-fs": "^4.12.0",
|
"mock-fs": "4.14",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"moment-timezone": "^0.5.33",
|
"moment-timezone": "^0.5.33",
|
||||||
"node-pty": "^0.9.0",
|
"node-pty": "^0.9.0",
|
||||||
@ -279,7 +278,7 @@
|
|||||||
"@types/marked": "^2.0.3",
|
"@types/marked": "^2.0.3",
|
||||||
"@types/md5-file": "^4.0.2",
|
"@types/md5-file": "^4.0.2",
|
||||||
"@types/mini-css-extract-plugin": "^0.9.1",
|
"@types/mini-css-extract-plugin": "^0.9.1",
|
||||||
"@types/mock-fs": "^4.10.0",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/module-alias": "^2.0.0",
|
"@types/module-alias": "^2.0.0",
|
||||||
"@types/node": "12.20",
|
"@types/node": "12.20",
|
||||||
"@types/npm": "^2.0.31",
|
"@types/npm": "^2.0.31",
|
||||||
@ -339,7 +338,7 @@
|
|||||||
"html-webpack-plugin": "^4.5.2",
|
"html-webpack-plugin": "^4.5.2",
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"include-media": "^1.4.9",
|
"include-media": "^1.4.9",
|
||||||
"jest": "^26.0.1",
|
"jest": "26.6.3",
|
||||||
"jest-canvas-mock": "^2.3.0",
|
"jest-canvas-mock": "^2.3.0",
|
||||||
"jest-fetch-mock": "^3.0.3",
|
"jest-fetch-mock": "^3.0.3",
|
||||||
"jest-mock-extended": "^1.0.16",
|
"jest-mock-extended": "^1.0.16",
|
||||||
|
|||||||
@ -22,14 +22,24 @@
|
|||||||
import { ClusterPageRegistry, getExtensionPageUrl, GlobalPageRegistry, PageParams } from "../page-registry";
|
import { ClusterPageRegistry, getExtensionPageUrl, GlobalPageRegistry, PageParams } from "../page-registry";
|
||||||
import { LensExtension } from "../../lens-extension";
|
import { LensExtension } from "../../lens-extension";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import fse from "fs-extra";
|
||||||
import { Console } from "console";
|
import { Console } from "console";
|
||||||
import { stdout, stderr } from "process";
|
import { stdout, stderr } from "process";
|
||||||
|
import { ThemeStore } from "../../../renderer/theme.store";
|
||||||
|
import { TerminalStore } from "../../renderer-api/components";
|
||||||
|
import { UserStore } from "../../../common/user-store";
|
||||||
|
|
||||||
|
jest.mock("electron", () => ({
|
||||||
|
app: {
|
||||||
|
getPath: () => "tmp",
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
console = new Console(stdout, stderr);
|
console = new Console(stdout, stderr);
|
||||||
|
|
||||||
let ext: LensExtension = null;
|
let ext: LensExtension = null;
|
||||||
|
|
||||||
describe("getPageUrl", () => {
|
describe("page registry tests", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
ext = new LensExtension({
|
ext = new LensExtension({
|
||||||
manifest: {
|
manifest: {
|
||||||
@ -43,6 +53,9 @@ describe("getPageUrl", () => {
|
|||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
isCompatible: true
|
isCompatible: true
|
||||||
});
|
});
|
||||||
|
UserStore.createInstance();
|
||||||
|
ThemeStore.createInstance();
|
||||||
|
TerminalStore.createInstance();
|
||||||
ClusterPageRegistry.createInstance();
|
ClusterPageRegistry.createInstance();
|
||||||
GlobalPageRegistry.createInstance().add({
|
GlobalPageRegistry.createInstance().add({
|
||||||
id: "page-with-params",
|
id: "page-with-params",
|
||||||
@ -54,70 +67,6 @@ describe("getPageUrl", () => {
|
|||||||
test2: "" // no default value, just declaration
|
test2: "" // no default value, just declaration
|
||||||
},
|
},
|
||||||
}, ext);
|
}, ext);
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
GlobalPageRegistry.resetInstance();
|
|
||||||
ClusterPageRegistry.resetInstance();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns a page url for extension", () => {
|
|
||||||
expect(getExtensionPageUrl({ extensionId: ext.name })).toBe("/extension/foo-bar");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("allows to pass base url as parameter", () => {
|
|
||||||
expect(getExtensionPageUrl({ extensionId: ext.name, pageId: "/test" })).toBe("/extension/foo-bar/test");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("removes @ and replace `/` to `--`", () => {
|
|
||||||
expect(getExtensionPageUrl({ extensionId: "@foo/bar" })).toBe("/extension/foo--bar");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("adds / prefix", () => {
|
|
||||||
expect(getExtensionPageUrl({ extensionId: ext.name, pageId: "test" })).toBe("/extension/foo-bar/test");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("normalize possible multi-slashes in page.id", () => {
|
|
||||||
expect(getExtensionPageUrl({ extensionId: ext.name, pageId: "//test/" })).toBe("/extension/foo-bar/test");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("gets page url with custom params", () => {
|
|
||||||
const params: PageParams = { test1: "one", test2: "2" };
|
|
||||||
const searchParams = new URLSearchParams(params);
|
|
||||||
const pageUrl = getExtensionPageUrl({
|
|
||||||
extensionId: ext.name,
|
|
||||||
pageId: "page-with-params",
|
|
||||||
params,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(pageUrl).toBe(`/extension/foo-bar/page-with-params?${searchParams}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("gets page url with default custom params", () => {
|
|
||||||
const defaultPageUrl = getExtensionPageUrl({
|
|
||||||
extensionId: ext.name,
|
|
||||||
pageId: "page-with-params",
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(defaultPageUrl).toBe(`/extension/foo-bar/page-with-params?test1=test1-default`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("globalPageRegistry", () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
ext = new LensExtension({
|
|
||||||
manifest: {
|
|
||||||
name: "@acme/foo-bar",
|
|
||||||
version: "0.1.1"
|
|
||||||
},
|
|
||||||
id: "/this/is/fake/package.json",
|
|
||||||
absolutePath: "/absolute/fake/",
|
|
||||||
manifestPath: "/this/is/fake/package.json",
|
|
||||||
isBundled: false,
|
|
||||||
isEnabled: true,
|
|
||||||
isCompatible: true
|
|
||||||
});
|
|
||||||
ClusterPageRegistry.createInstance();
|
|
||||||
GlobalPageRegistry.createInstance().add([
|
GlobalPageRegistry.createInstance().add([
|
||||||
{
|
{
|
||||||
id: "test-page",
|
id: "test-page",
|
||||||
@ -142,33 +91,82 @@ describe("globalPageRegistry", () => {
|
|||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
GlobalPageRegistry.resetInstance();
|
GlobalPageRegistry.resetInstance();
|
||||||
ClusterPageRegistry.resetInstance();
|
ClusterPageRegistry.resetInstance();
|
||||||
|
TerminalStore.resetInstance();
|
||||||
|
ThemeStore.resetInstance();
|
||||||
|
UserStore.resetInstance();
|
||||||
|
fse.remove("tmp");
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("getByPageTarget", () => {
|
describe("getPageUrl", () => {
|
||||||
it("matching to first registered page without id", () => {
|
it("returns a page url for extension", () => {
|
||||||
const page = GlobalPageRegistry.getInstance().getByPageTarget({ extensionId: ext.name });
|
expect(getExtensionPageUrl({ extensionId: ext.name })).toBe("/extension/foo-bar");
|
||||||
|
|
||||||
expect(page.id).toEqual(undefined);
|
|
||||||
expect(page.extensionId).toEqual(ext.name);
|
|
||||||
expect(page.url).toEqual(getExtensionPageUrl({ extensionId: ext.name }));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns matching page", () => {
|
it("allows to pass base url as parameter", () => {
|
||||||
const page = GlobalPageRegistry.getInstance().getByPageTarget({
|
expect(getExtensionPageUrl({ extensionId: ext.name, pageId: "/test" })).toBe("/extension/foo-bar/test");
|
||||||
pageId: "test-page",
|
|
||||||
extensionId: ext.name
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(page.id).toEqual("test-page");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns null if target not found", () => {
|
it("removes @ and replace `/` to `--`", () => {
|
||||||
const page = GlobalPageRegistry.getInstance().getByPageTarget({
|
expect(getExtensionPageUrl({ extensionId: "@foo/bar" })).toBe("/extension/foo--bar");
|
||||||
pageId: "wrong-page",
|
});
|
||||||
extensionId: ext.name
|
|
||||||
|
it("adds / prefix", () => {
|
||||||
|
expect(getExtensionPageUrl({ extensionId: ext.name, pageId: "test" })).toBe("/extension/foo-bar/test");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("normalize possible multi-slashes in page.id", () => {
|
||||||
|
expect(getExtensionPageUrl({ extensionId: ext.name, pageId: "//test/" })).toBe("/extension/foo-bar/test");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("gets page url with custom params", () => {
|
||||||
|
const params: PageParams = { test1: "one", test2: "2" };
|
||||||
|
const searchParams = new URLSearchParams(params);
|
||||||
|
const pageUrl = getExtensionPageUrl({
|
||||||
|
extensionId: ext.name,
|
||||||
|
pageId: "page-with-params",
|
||||||
|
params,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(page).toBeNull();
|
expect(pageUrl).toBe(`/extension/foo-bar/page-with-params?${searchParams}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("gets page url with default custom params", () => {
|
||||||
|
const defaultPageUrl = getExtensionPageUrl({
|
||||||
|
extensionId: ext.name,
|
||||||
|
pageId: "page-with-params",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(defaultPageUrl).toBe(`/extension/foo-bar/page-with-params?test1=test1-default`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("globalPageRegistry", () => {
|
||||||
|
describe("getByPageTarget", () => {
|
||||||
|
it("matching to first registered page without id", () => {
|
||||||
|
const page = GlobalPageRegistry.getInstance().getByPageTarget({ extensionId: ext.name });
|
||||||
|
|
||||||
|
expect(page.id).toEqual(undefined);
|
||||||
|
expect(page.extensionId).toEqual(ext.name);
|
||||||
|
expect(page.url).toEqual(getExtensionPageUrl({ extensionId: ext.name }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns matching page", () => {
|
||||||
|
const page = GlobalPageRegistry.getInstance().getByPageTarget({
|
||||||
|
pageId: "test-page",
|
||||||
|
extensionId: ext.name
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(page.id).toEqual("test-page");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns null if target not found", () => {
|
||||||
|
const page = GlobalPageRegistry.getInstance().getByPageTarget({
|
||||||
|
pageId: "wrong-page",
|
||||||
|
extensionId: ext.name
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(page).toBeNull();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -67,5 +67,5 @@ export * from "../../renderer/components/+events/kube-event-details";
|
|||||||
|
|
||||||
// specific exports
|
// specific exports
|
||||||
export * from "../../renderer/components/status-brick";
|
export * from "../../renderer/components/status-brick";
|
||||||
export { terminalStore, createTerminalTab } from "../../renderer/components/dock/terminal.store";
|
export { terminalStore, createTerminalTab, TerminalStore } from "../../renderer/components/dock/terminal.store";
|
||||||
export { logTabStore } from "../../renderer/components/dock/log-tab.store";
|
export { logTabStore } from "../../renderer/components/dock/log-tab.store";
|
||||||
|
|||||||
@ -62,7 +62,7 @@ import { FilesystemProvisionerStore } from "./extension-filesystem";
|
|||||||
import { SentryInit } from "../common/sentry";
|
import { SentryInit } from "../common/sentry";
|
||||||
|
|
||||||
// This has to be called before start using winton-based logger
|
// This has to be called before start using winton-based logger
|
||||||
// For example, before any logger.log
|
// For example, before any logger.log
|
||||||
SentryInit();
|
SentryInit();
|
||||||
|
|
||||||
const workingDir = path.join(app.getPath("appData"), appName);
|
const workingDir = path.join(app.getPath("appData"), appName);
|
||||||
@ -145,8 +145,12 @@ app.on("ready", async () => {
|
|||||||
|
|
||||||
UserStore.createInstance().startMainReactions();
|
UserStore.createInstance().startMainReactions();
|
||||||
|
|
||||||
|
// ClusterStore depends on: UserStore
|
||||||
ClusterStore.createInstance().provideInitialFromMain();
|
ClusterStore.createInstance().provideInitialFromMain();
|
||||||
|
|
||||||
|
// HotbarStore depends on: ClusterStore
|
||||||
HotbarStore.createInstance();
|
HotbarStore.createInstance();
|
||||||
|
|
||||||
ExtensionsStore.createInstance();
|
ExtensionsStore.createInstance();
|
||||||
FilesystemProvisionerStore.createInstance();
|
FilesystemProvisionerStore.createInstance();
|
||||||
WeblinkStore.createInstance();
|
WeblinkStore.createInstance();
|
||||||
|
|||||||
@ -48,6 +48,7 @@ import { ExtensionsStore } from "../extensions/extensions-store";
|
|||||||
import { FilesystemProvisionerStore } from "../main/extension-filesystem";
|
import { FilesystemProvisionerStore } from "../main/extension-filesystem";
|
||||||
import { ThemeStore } from "./theme.store";
|
import { ThemeStore } from "./theme.store";
|
||||||
import { SentryInit } from "../common/sentry";
|
import { SentryInit } from "../common/sentry";
|
||||||
|
import { TerminalStore } from "./components/dock/terminal.store";
|
||||||
|
|
||||||
configurePackages();
|
configurePackages();
|
||||||
|
|
||||||
@ -89,18 +90,28 @@ export async function bootstrap(App: AppComponent) {
|
|||||||
|
|
||||||
SentryInit();
|
SentryInit();
|
||||||
|
|
||||||
await ClusterStore.createInstance().loadInitialOnRenderer();
|
// ClusterStore depends on: UserStore
|
||||||
|
const cs = ClusterStore.createInstance();
|
||||||
|
|
||||||
|
await cs.loadInitialOnRenderer();
|
||||||
|
|
||||||
|
// HotbarStore depends on: ClusterStore
|
||||||
HotbarStore.createInstance();
|
HotbarStore.createInstance();
|
||||||
ExtensionsStore.createInstance();
|
ExtensionsStore.createInstance();
|
||||||
FilesystemProvisionerStore.createInstance();
|
FilesystemProvisionerStore.createInstance();
|
||||||
|
|
||||||
|
// ThemeStore depends on: UserStore
|
||||||
ThemeStore.createInstance();
|
ThemeStore.createInstance();
|
||||||
|
|
||||||
|
// TerminalStore depends on: ThemeStore
|
||||||
|
TerminalStore.createInstance();
|
||||||
WeblinkStore.createInstance();
|
WeblinkStore.createInstance();
|
||||||
|
|
||||||
ExtensionInstallationStateStore.bindIpcListeners();
|
ExtensionInstallationStateStore.bindIpcListeners();
|
||||||
HelmRepoManager.createInstance(); // initialize the manager
|
HelmRepoManager.createInstance(); // initialize the manager
|
||||||
|
|
||||||
// Register additional store listeners
|
// Register additional store listeners
|
||||||
ClusterStore.getInstance().registerIpcListener();
|
cs.registerIpcListener();
|
||||||
|
|
||||||
// init app's dependencies if any
|
// init app's dependencies if any
|
||||||
if (App.init) {
|
if (App.init) {
|
||||||
|
|||||||
@ -22,14 +22,18 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { fireEvent, render } from "@testing-library/react";
|
import { fireEvent, render } from "@testing-library/react";
|
||||||
import "@testing-library/jest-dom/extend-expect";
|
import "@testing-library/jest-dom/extend-expect";
|
||||||
|
import fse from "fs-extra";
|
||||||
|
|
||||||
import { DockTabs } from "../dock-tabs";
|
import { DockTabs } from "../dock-tabs";
|
||||||
import { dockStore, DockTab, TabKind } from "../dock.store";
|
import { dockStore, DockTab, TabKind } from "../dock.store";
|
||||||
import { noop } from "../../../utils";
|
import { noop } from "../../../utils";
|
||||||
|
import { ThemeStore } from "../../../theme.store";
|
||||||
|
import { TerminalStore } from "../terminal.store";
|
||||||
|
import { UserStore } from "../../../../common/user-store";
|
||||||
|
|
||||||
jest.mock("electron", () => ({
|
jest.mock("electron", () => ({
|
||||||
app: {
|
app: {
|
||||||
getPath: () => "/foo",
|
getPath: () => "tmp",
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -55,10 +59,20 @@ const getTabKinds = () => dockStore.tabs.map(tab => tab.kind);
|
|||||||
|
|
||||||
describe("<DockTabs />", () => {
|
describe("<DockTabs />", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
UserStore.createInstance();
|
||||||
|
ThemeStore.createInstance();
|
||||||
|
TerminalStore.createInstance();
|
||||||
await dockStore.whenReady;
|
await dockStore.whenReady;
|
||||||
dockStore.tabs = initialTabs;
|
dockStore.tabs = initialTabs;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
ThemeStore.resetInstance();
|
||||||
|
TerminalStore.resetInstance();
|
||||||
|
UserStore.resetInstance();
|
||||||
|
fse.remove("tmp");
|
||||||
|
});
|
||||||
|
|
||||||
it("renders w/o errors", () => {
|
it("renders w/o errors", () => {
|
||||||
const { container } = renderTabs();
|
const { container } = renderTabs();
|
||||||
|
|
||||||
|
|||||||
@ -20,14 +20,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { podsStore } from "../../+workloads-pods/pods.store";
|
import { podsStore } from "../../+workloads-pods/pods.store";
|
||||||
|
import { UserStore } from "../../../../common/user-store";
|
||||||
import { Pod } from "../../../api/endpoints";
|
import { Pod } from "../../../api/endpoints";
|
||||||
|
import { ThemeStore } from "../../../theme.store";
|
||||||
import { dockStore } from "../dock.store";
|
import { dockStore } from "../dock.store";
|
||||||
import { logTabStore } from "../log-tab.store";
|
import { logTabStore } from "../log-tab.store";
|
||||||
|
import { TerminalStore } from "../terminal.store";
|
||||||
import { deploymentPod1, deploymentPod2, deploymentPod3, dockerPod } from "./pod.mock";
|
import { deploymentPod1, deploymentPod2, deploymentPod3, dockerPod } from "./pod.mock";
|
||||||
|
import fse from "fs-extra";
|
||||||
|
|
||||||
jest.mock("electron", () => ({
|
jest.mock("electron", () => ({
|
||||||
app: {
|
app: {
|
||||||
getPath: () => "/foo",
|
getPath: () => "tmp",
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -36,9 +40,19 @@ podsStore.items.push(new Pod(deploymentPod1));
|
|||||||
podsStore.items.push(new Pod(deploymentPod2));
|
podsStore.items.push(new Pod(deploymentPod2));
|
||||||
|
|
||||||
describe("log tab store", () => {
|
describe("log tab store", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
UserStore.createInstance();
|
||||||
|
ThemeStore.createInstance();
|
||||||
|
TerminalStore.createInstance();
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
logTabStore.reset();
|
logTabStore.reset();
|
||||||
dockStore.reset();
|
dockStore.reset();
|
||||||
|
UserStore.resetInstance();
|
||||||
|
ThemeStore.resetInstance();
|
||||||
|
TerminalStore.resetInstance();
|
||||||
|
fse.remove("tmp");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("creates log tab without sibling pods", () => {
|
it("creates log tab without sibling pods", () => {
|
||||||
|
|||||||
@ -136,7 +136,12 @@ export class DockStore implements DockStorageState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get selectedTabId(): TabId | undefined {
|
get selectedTabId(): TabId | undefined {
|
||||||
return this.storage.get().selectedTabId || this.tabs[0]?.id;
|
return this.storage.get().selectedTabId
|
||||||
|
|| (
|
||||||
|
this.tabs.length > 0
|
||||||
|
? this.tabs[0]?.id
|
||||||
|
: undefined
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
set selectedTabId(tabId: TabId) {
|
set selectedTabId(tabId: TabId) {
|
||||||
@ -277,9 +282,9 @@ export class DockStore implements DockStorageState {
|
|||||||
|
|
||||||
if (newTab?.kind === TabKind.TERMINAL) {
|
if (newTab?.kind === TabKind.TERMINAL) {
|
||||||
// close the dock when selected sibling inactive terminal tab
|
// close the dock when selected sibling inactive terminal tab
|
||||||
const { terminalStore } = await import("./terminal.store");
|
const { TerminalStore } = await import("./terminal.store");
|
||||||
|
|
||||||
if (!terminalStore.isConnected(newTab.id)) this.close();
|
if (!TerminalStore.getInstance(false)?.isConnected(newTab.id)) this.close();
|
||||||
}
|
}
|
||||||
this.selectTab(newTab.id);
|
this.selectTab(newTab.id);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -108,8 +108,10 @@ export class LogTabStore extends DockTabStore<LogTabData> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateTabsData() {
|
private async updateTabsData() {
|
||||||
this.data.forEach((tabData, tabId) => {
|
const promises: Promise<void>[] = [];
|
||||||
|
|
||||||
|
for (const [tabId, tabData] of this.data) {
|
||||||
const pod = new Pod(tabData.selectedPod);
|
const pod = new Pod(tabData.selectedPod);
|
||||||
const pods = podsStore.getPodsByOwnerId(pod.getOwnerRefs()[0]?.uid);
|
const pods = podsStore.getPodsByOwnerId(pod.getOwnerRefs()[0]?.uid);
|
||||||
const isSelectedPodInList = pods.find(item => item.getId() == pod.getId());
|
const isSelectedPodInList = pods.find(item => item.getId() == pod.getId());
|
||||||
@ -126,14 +128,16 @@ export class LogTabStore extends DockTabStore<LogTabData> {
|
|||||||
|
|
||||||
this.renameTab(tabId);
|
this.renameTab(tabId);
|
||||||
} else {
|
} else {
|
||||||
this.closeTab(tabId);
|
promises.push(this.closeTab(tabId));
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
private closeTab(tabId: string) {
|
private async closeTab(tabId: string) {
|
||||||
this.clearData(tabId);
|
this.clearData(tabId);
|
||||||
dockStore.closeTab(tabId);
|
await dockStore.closeTab(tabId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { autorun, observable } from "mobx";
|
import { autorun, observable } from "mobx";
|
||||||
import { autoBind } from "../../utils";
|
import { autoBind, Singleton } from "../../utils";
|
||||||
import { Terminal } from "./terminal";
|
import { Terminal } from "./terminal";
|
||||||
import { TerminalApi } from "../../api/terminal-api";
|
import { TerminalApi } from "../../api/terminal-api";
|
||||||
import { dockStore, DockTab, DockTabCreateSpecific, TabId, TabKind } from "./dock.store";
|
import { dockStore, DockTab, DockTabCreateSpecific, TabId, TabKind } from "./dock.store";
|
||||||
@ -38,11 +38,12 @@ export function createTerminalTab(tabParams: DockTabCreateSpecific = {}) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TerminalStore {
|
export class TerminalStore extends Singleton {
|
||||||
protected terminals = new Map<TabId, Terminal>();
|
protected terminals = new Map<TabId, Terminal>();
|
||||||
protected connections = observable.map<TabId, TerminalApi>();
|
protected connections = observable.map<TabId, TerminalApi>();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
super();
|
||||||
autoBind(this);
|
autoBind(this);
|
||||||
|
|
||||||
// connect active tab
|
// connect active tab
|
||||||
@ -129,4 +130,24 @@ export class TerminalStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const terminalStore = new TerminalStore();
|
/**
|
||||||
|
* @deprecated use `TerminalStore.getInstance()` instead
|
||||||
|
*/
|
||||||
|
export const terminalStore = new Proxy({}, {
|
||||||
|
get(target, p) {
|
||||||
|
if (p === "$$typeof") {
|
||||||
|
return "TerminalStore";
|
||||||
|
}
|
||||||
|
|
||||||
|
const ts = TerminalStore.getInstance();
|
||||||
|
const res = (ts as any)?.[p];
|
||||||
|
|
||||||
|
if (typeof res === "function") {
|
||||||
|
return function(...args: any[]) {
|
||||||
|
return res.apply(ts, args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
}) as TerminalStore;
|
||||||
|
|||||||
@ -84,7 +84,7 @@ export interface TabProps<D = any> extends DOMAttributes<HTMLElement> {
|
|||||||
export class Tab extends React.PureComponent<TabProps> {
|
export class Tab extends React.PureComponent<TabProps> {
|
||||||
static contextType = TabsContext;
|
static contextType = TabsContext;
|
||||||
declare context: TabsContextValue;
|
declare context: TabsContextValue;
|
||||||
public elem: HTMLElement;
|
public ref = React.createRef<HTMLDivElement>();
|
||||||
|
|
||||||
get isActive() {
|
get isActive() {
|
||||||
const { active, value } = this.props;
|
const { active, value } = this.props;
|
||||||
@ -93,11 +93,11 @@ export class Tab extends React.PureComponent<TabProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
focus() {
|
focus() {
|
||||||
this.elem.focus();
|
this.ref.current?.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollIntoView() {
|
scrollIntoView() {
|
||||||
this.elem.scrollIntoView({
|
this.ref.current?.scrollIntoView?.({
|
||||||
behavior: "smooth",
|
behavior: "smooth",
|
||||||
inline: "center",
|
inline: "center",
|
||||||
});
|
});
|
||||||
@ -106,30 +106,28 @@ export class Tab extends React.PureComponent<TabProps> {
|
|||||||
@boundMethod
|
@boundMethod
|
||||||
onClick(evt: React.MouseEvent<HTMLElement>) {
|
onClick(evt: React.MouseEvent<HTMLElement>) {
|
||||||
const { value, active, disabled, onClick } = this.props;
|
const { value, active, disabled, onClick } = this.props;
|
||||||
const { onChange } = this.context;
|
|
||||||
|
|
||||||
if (disabled || active) return;
|
if (disabled || active) {
|
||||||
if (onClick) onClick(evt);
|
return;
|
||||||
if (onChange) onChange(value);
|
}
|
||||||
|
|
||||||
|
onClick?.(evt);
|
||||||
|
this.context.onChange?.(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
onFocus(evt: React.FocusEvent<HTMLElement>) {
|
onFocus(evt: React.FocusEvent<HTMLElement>) {
|
||||||
const { onFocus } = this.props;
|
this.props.onFocus?.(evt);
|
||||||
|
|
||||||
if (onFocus) onFocus(evt);
|
|
||||||
this.scrollIntoView();
|
this.scrollIntoView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
onKeyDown(evt: React.KeyboardEvent<HTMLElement>) {
|
onKeyDown(evt: React.KeyboardEvent<HTMLElement>) {
|
||||||
const ENTER_KEY = evt.keyCode === 13;
|
if (evt.key === " " || evt.key === "Enter") {
|
||||||
const SPACE_KEY = evt.keyCode === 32;
|
this.ref.current?.click();
|
||||||
|
}
|
||||||
|
|
||||||
if (SPACE_KEY || ENTER_KEY) this.elem.click();
|
this.props?.onKeyDown(evt);
|
||||||
const { onKeyDown } = this.props;
|
|
||||||
|
|
||||||
if (onKeyDown) onKeyDown(evt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -138,11 +136,6 @@ export class Tab extends React.PureComponent<TabProps> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
|
||||||
protected bindRef(elem: HTMLElement) {
|
|
||||||
this.elem = elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { active, disabled, icon, label, value, ...elemProps } = this.props;
|
const { active, disabled, icon, label, value, ...elemProps } = this.props;
|
||||||
let { className } = this.props;
|
let { className } = this.props;
|
||||||
@ -160,7 +153,7 @@ export class Tab extends React.PureComponent<TabProps> {
|
|||||||
onClick={this.onClick}
|
onClick={this.onClick}
|
||||||
onFocus={this.onFocus}
|
onFocus={this.onFocus}
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
ref={this.bindRef}
|
ref={this.ref}
|
||||||
>
|
>
|
||||||
{typeof icon === "string" ? <Icon small material={icon}/> : icon}
|
{typeof icon === "string" ? <Icon small material={icon}/> : icon}
|
||||||
<div className="label">
|
<div className="label">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user