Move tabs manager to zustand (#915)

This commit is contained in:
victor-meng
2021-07-08 21:32:22 -07:00
committed by GitHub
parent f4eef1b61b
commit f8ab0a82e0
42 changed files with 609 additions and 663 deletions

View File

@@ -15,6 +15,7 @@ import { fetchPortalNotifications } from "../../Common/PortalNotifications";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
import { UploadDetailsRecord } from "../../Contracts/ViewModels";
import { useTabs } from "../../hooks/useTabs";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../UserContext";
@@ -239,9 +240,11 @@ export default class Collection implements ViewModels.Collection {
this.expandCollection();
}
useCommandBar.getState().setContextButtons([]);
this.container.tabsManager.refreshActiveTab(
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
);
useTabs
.getState()
.refreshActiveTab(
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
);
}
public collapseCollection() {
@@ -288,14 +291,16 @@ export default class Collection implements ViewModels.Collection {
dataExplorerArea: Constants.Areas.ResourceTree,
});
const documentsTabs: DocumentsTab[] = this.container.tabsManager.getTabs(
ViewModels.CollectionTabKind.Documents,
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
) as DocumentsTab[];
const documentsTabs: DocumentsTab[] = useTabs
.getState()
.getTabs(
ViewModels.CollectionTabKind.Documents,
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
) as DocumentsTab[];
let documentsTab: DocumentsTab = documentsTabs && documentsTabs[0];
if (documentsTab) {
this.container.tabsManager.activateTab(documentsTab);
useTabs.getState().activateTab(documentsTab);
} else {
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
databaseName: this.databaseId,
@@ -317,7 +322,7 @@ export default class Collection implements ViewModels.Collection {
onLoadStartKey: startKey,
});
this.container.tabsManager.activateNewTab(documentsTab);
useTabs.getState().activateNewTab(documentsTab);
}
}
@@ -333,14 +338,16 @@ export default class Collection implements ViewModels.Collection {
dataExplorerArea: Constants.Areas.ResourceTree,
});
const conflictsTabs: ConflictsTab[] = this.container.tabsManager.getTabs(
ViewModels.CollectionTabKind.Conflicts,
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
) as ConflictsTab[];
const conflictsTabs: ConflictsTab[] = useTabs
.getState()
.getTabs(
ViewModels.CollectionTabKind.Conflicts,
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
) as ConflictsTab[];
let conflictsTab: ConflictsTab = conflictsTabs && conflictsTabs[0];
if (conflictsTab) {
this.container.tabsManager.activateTab(conflictsTab);
useTabs.getState().activateTab(conflictsTab);
} else {
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
databaseName: this.databaseId,
@@ -362,7 +369,7 @@ export default class Collection implements ViewModels.Collection {
onLoadStartKey: startKey,
});
this.container.tabsManager.activateNewTab(conflictsTab);
useTabs.getState().activateNewTab(conflictsTab);
}
}
@@ -384,14 +391,16 @@ export default class Collection implements ViewModels.Collection {
});
}
const queryTablesTabs: QueryTablesTab[] = this.container.tabsManager.getTabs(
ViewModels.CollectionTabKind.QueryTables,
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
) as QueryTablesTab[];
const queryTablesTabs: QueryTablesTab[] = useTabs
.getState()
.getTabs(
ViewModels.CollectionTabKind.QueryTables,
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
) as QueryTablesTab[];
let queryTablesTab: QueryTablesTab = queryTablesTabs && queryTablesTabs[0];
if (queryTablesTab) {
this.container.tabsManager.activateTab(queryTablesTab);
useTabs.getState().activateTab(queryTablesTab);
} else {
this.documentIds([]);
let title = `Entities`;
@@ -415,7 +424,7 @@ export default class Collection implements ViewModels.Collection {
onLoadStartKey: startKey,
});
this.container.tabsManager.activateNewTab(queryTablesTab);
useTabs.getState().activateNewTab(queryTablesTab);
}
}
@@ -431,14 +440,16 @@ export default class Collection implements ViewModels.Collection {
dataExplorerArea: Constants.Areas.ResourceTree,
});
const graphTabs: GraphTab[] = this.container.tabsManager.getTabs(
ViewModels.CollectionTabKind.Graph,
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
) as GraphTab[];
const graphTabs: GraphTab[] = useTabs
.getState()
.getTabs(
ViewModels.CollectionTabKind.Graph,
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
) as GraphTab[];
let graphTab: GraphTab = graphTabs && graphTabs[0];
if (graphTab) {
this.container.tabsManager.activateTab(graphTab);
useTabs.getState().activateTab(graphTab);
} else {
this.documentIds([]);
const title = "Graph";
@@ -466,7 +477,7 @@ export default class Collection implements ViewModels.Collection {
onLoadStartKey: startKey,
});
this.container.tabsManager.activateNewTab(graphTab);
useTabs.getState().activateNewTab(graphTab);
}
}
@@ -482,14 +493,16 @@ export default class Collection implements ViewModels.Collection {
dataExplorerArea: Constants.Areas.ResourceTree,
});
const mongoDocumentsTabs: MongoDocumentsTab[] = this.container.tabsManager.getTabs(
ViewModels.CollectionTabKind.Documents,
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
) as MongoDocumentsTab[];
const mongoDocumentsTabs: MongoDocumentsTab[] = useTabs
.getState()
.getTabs(
ViewModels.CollectionTabKind.Documents,
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
) as MongoDocumentsTab[];
let mongoDocumentsTab: MongoDocumentsTab = mongoDocumentsTabs && mongoDocumentsTabs[0];
if (mongoDocumentsTab) {
this.container.tabsManager.activateTab(mongoDocumentsTab);
useTabs.getState().activateTab(mongoDocumentsTab);
} else {
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
databaseName: this.databaseId,
@@ -510,7 +523,7 @@ export default class Collection implements ViewModels.Collection {
node: this,
onLoadStartKey: startKey,
});
this.container.tabsManager.activateNewTab(mongoDocumentsTab);
useTabs.getState().activateNewTab(mongoDocumentsTab);
}
};
@@ -525,13 +538,13 @@ export default class Collection implements ViewModels.Collection {
dataExplorerArea: Constants.Areas.ResourceTree,
});
for (const tab of this.container.tabsManager.openedTabs()) {
for (const tab of useTabs.getState().openedTabs) {
if (
tab instanceof SchemaAnalyzerTab &&
tab.collection?.databaseId === this.databaseId &&
tab.collection?.id() === this.id()
) {
return this.container.tabsManager.activateTab(tab);
return useTabs.getState().activateTab(tab);
}
}
@@ -542,7 +555,7 @@ export default class Collection implements ViewModels.Collection {
tabTitle: "Schema",
});
this.documentIds([]);
this.container.tabsManager.activateNewTab(
useTabs.getState().activateNewTab(
new SchemaAnalyzerTab({
account: userContext.databaseAccount,
masterKey: userContext.masterKey || "",
@@ -571,12 +584,9 @@ export default class Collection implements ViewModels.Collection {
});
const tabTitle = !this.offer() ? "Settings" : "Scale & Settings";
const matchingTabs = this.container.tabsManager.getTabs(
ViewModels.CollectionTabKind.CollectionSettingsV2,
(tab) => {
return tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id();
}
);
const matchingTabs = useTabs.getState().getTabs(ViewModels.CollectionTabKind.CollectionSettingsV2, (tab) => {
return tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id();
});
const traceStartData = {
databaseName: this.databaseId,
@@ -608,15 +618,15 @@ export default class Collection implements ViewModels.Collection {
settingsTabOptions.onLoadStartKey = startKey;
settingsTabOptions.tabKind = ViewModels.CollectionTabKind.CollectionSettingsV2;
settingsTabV2 = new CollectionSettingsTabV2(settingsTabOptions);
this.container.tabsManager.activateNewTab(settingsTabV2);
useTabs.getState().activateNewTab(settingsTabV2);
} else {
this.container.tabsManager.activateTab(settingsTabV2);
useTabs.getState().activateTab(settingsTabV2);
}
};
public onNewQueryClick(source: any, event: MouseEvent, queryText?: string) {
const collection: ViewModels.Collection = source.collection || source;
const id = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Query).length + 1;
const id = useTabs.getState().getTabs(ViewModels.CollectionTabKind.Query).length + 1;
const title = "Query " + id;
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
databaseName: this.databaseId,
@@ -626,7 +636,7 @@ export default class Collection implements ViewModels.Collection {
tabTitle: title,
});
this.container.tabsManager.activateNewTab(
useTabs.getState().activateNewTab(
new NewQueryTab(
{
tabKind: ViewModels.CollectionTabKind.Query,
@@ -645,7 +655,7 @@ export default class Collection implements ViewModels.Collection {
public onNewMongoQueryClick(source: any, event: MouseEvent, queryText?: string) {
const collection: ViewModels.Collection = source.collection || source;
const id = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Query).length + 1;
const id = useTabs.getState().getTabs(ViewModels.CollectionTabKind.Query).length + 1;
const title = "Query " + id;
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
@@ -672,11 +682,11 @@ export default class Collection implements ViewModels.Collection {
}
);
this.container.tabsManager.activateNewTab(newMongoQueryTab);
useTabs.getState().activateNewTab(newMongoQueryTab);
}
public onNewGraphClick() {
const id: number = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Graph).length + 1;
const id: number = useTabs.getState().getTabs(ViewModels.CollectionTabKind.Graph).length + 1;
const title: string = "Graph Query " + id;
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
@@ -702,13 +712,11 @@ export default class Collection implements ViewModels.Collection {
onLoadStartKey: startKey,
});
this.container.tabsManager.activateNewTab(graphTab);
useTabs.getState().activateNewTab(graphTab);
}
public onNewMongoShellClick() {
const mongoShellTabs = this.container.tabsManager.getTabs(
ViewModels.CollectionTabKind.MongoShell
) as NewMongoShellTab[];
const mongoShellTabs = useTabs.getState().getTabs(ViewModels.CollectionTabKind.MongoShell) as NewMongoShellTab[];
let index = 1;
if (mongoShellTabs.length > 0) {
@@ -729,7 +737,7 @@ export default class Collection implements ViewModels.Collection {
}
);
this.container.tabsManager.activateNewTab(mongoShellTab);
useTabs.getState().activateNewTab(mongoShellTab);
}
public onNewStoredProcedureClick(source: ViewModels.Collection, event: MouseEvent) {
@@ -787,9 +795,11 @@ export default class Collection implements ViewModels.Collection {
} else {
this.expandStoredProcedures();
}
this.container.tabsManager.refreshActiveTab(
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
);
useTabs
.getState()
.refreshActiveTab(
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
);
}
public expandStoredProcedures() {
@@ -846,9 +856,11 @@ export default class Collection implements ViewModels.Collection {
} else {
this.expandUserDefinedFunctions();
}
this.container.tabsManager.refreshActiveTab(
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
);
useTabs
.getState()
.refreshActiveTab(
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
);
}
public expandUserDefinedFunctions() {
@@ -905,9 +917,11 @@ export default class Collection implements ViewModels.Collection {
} else {
this.expandTriggers();
}
this.container.tabsManager.refreshActiveTab(
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
);
useTabs
.getState()
.refreshActiveTab(
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id()
);
}
public expandTriggers() {

View File

@@ -1,7 +1,7 @@
import * as ko from "knockout";
import { HttpStatusCodes } from "../../Common/Constants";
import * as DataModels from "../../Contracts/DataModels";
import { JunoClient } from "../../Juno/JunoClient";
import { Features } from "../../Platform/Hosted/extractFeatures";
import { updateUserContext, userContext } from "../../UserContext";
import Explorer from "../Explorer";
import Database from "./Database";
@@ -35,7 +35,6 @@ describe("Add Schema", () => {
collection.analyticalStorageTtl = undefined;
const database = new Database(createMockContainer(), collection);
database.container = createMockContainer();
database.container.isSchemaEnabled = ko.computed<boolean>(() => false);
database.junoClient = new JunoClient();
database.junoClient.requestSchema = jest.fn();
@@ -52,7 +51,11 @@ describe("Add Schema", () => {
const database = new Database(createMockContainer(), collection);
database.container = createMockContainer();
database.container.isSchemaEnabled = ko.computed<boolean>(() => true);
updateUserContext({
features: {
enableSchema: true,
} as Features,
});
database.junoClient = new JunoClient();
database.junoClient.requestSchema = jest.fn();

View File

@@ -11,6 +11,7 @@ import { fetchPortalNotifications } from "../../Common/PortalNotifications";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
import { useSidePanel } from "../../hooks/useSidePanel";
import { useTabs } from "../../hooks/useTabs";
import { IJunoResponse, JunoClient } from "../../Juno/JunoClient";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
@@ -67,7 +68,7 @@ export default class Database implements ViewModels.Database {
const pendingNotificationsPromise: Promise<DataModels.Notification> = this.getPendingThroughputSplitNotification();
const tabKind = ViewModels.CollectionTabKind.DatabaseSettingsV2;
const matchingTabs = this.container.tabsManager.getTabs(tabKind, (tab) => tab.node?.id() === this.id());
const matchingTabs = useTabs.getState().getTabs(tabKind, (tab) => tab.node?.id() === this.id());
let settingsTab = matchingTabs?.[0] as DatabaseSettingsTabV2;
if (!settingsTab) {
@@ -91,7 +92,7 @@ export default class Database implements ViewModels.Database {
};
settingsTab = new DatabaseSettingsTabV2(tabOptions);
settingsTab.pendingNotification(pendingNotification);
this.container.tabsManager.activateNewTab(settingsTab);
useTabs.getState().activateNewTab(settingsTab);
},
(error) => {
const errorMessage = getErrorMessage(error);
@@ -116,11 +117,11 @@ export default class Database implements ViewModels.Database {
pendingNotificationsPromise.then(
(pendingNotification: DataModels.Notification) => {
settingsTab.pendingNotification(pendingNotification);
this.container.tabsManager.activateTab(settingsTab);
useTabs.getState().activateTab(settingsTab);
},
() => {
settingsTab.pendingNotification(undefined);
this.container.tabsManager.activateTab(settingsTab);
useTabs.getState().activateTab(settingsTab);
}
);
}
@@ -312,7 +313,7 @@ export default class Database implements ViewModels.Database {
let checkForSchema: NodeJS.Timeout;
interval = interval || 5000;
if (collection.analyticalStorageTtl !== undefined && this.container.isSchemaEnabled()) {
if (collection.analyticalStorageTtl !== undefined && userContext.features.enableSchema) {
collection.requestSchema = () => {
this.junoClient.requestSchema({
id: undefined,

View File

@@ -2,6 +2,7 @@ import * as ko from "knockout";
import * as Constants from "../../Common/Constants";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
import { useTabs } from "../../hooks/useTabs";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../UserContext";
@@ -77,7 +78,7 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
public onNewQueryClick(source: any, event: MouseEvent, queryText?: string) {
const collection: ViewModels.Collection = source.collection || source;
const id = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Query).length + 1;
const id = useTabs.getState().getTabs(ViewModels.CollectionTabKind.Query).length + 1;
const title = "Query " + id;
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
databaseName: this.databaseId,
@@ -87,7 +88,7 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
tabTitle: title,
});
this.container.tabsManager.activateNewTab(
useTabs.getState().activateNewTab(
new NewQueryTab(
{
tabKind: ViewModels.CollectionTabKind.Query,
@@ -115,16 +116,18 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
dataExplorerArea: Constants.Areas.ResourceTree,
});
const documentsTabs: DocumentsTab[] = this.container.tabsManager.getTabs(
ViewModels.CollectionTabKind.Documents,
(tab: TabsBase) =>
tab.collection?.id() === this.id() &&
(tab.collection as ViewModels.CollectionBase).databaseId === this.databaseId
) as DocumentsTab[];
const documentsTabs: DocumentsTab[] = useTabs
.getState()
.getTabs(
ViewModels.CollectionTabKind.Documents,
(tab: TabsBase) =>
tab.collection?.id() === this.id() &&
(tab.collection as ViewModels.CollectionBase).databaseId === this.databaseId
) as DocumentsTab[];
let documentsTab: DocumentsTab = documentsTabs && documentsTabs[0];
if (documentsTab) {
this.container.tabsManager.activateTab(documentsTab);
useTabs.getState().activateTab(documentsTab);
} else {
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
databaseName: this.databaseId,
@@ -146,7 +149,7 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
onLoadStartKey: startKey,
});
this.container.tabsManager.activateNewTab(documentsTab);
useTabs.getState().activateNewTab(documentsTab);
}
}

View File

@@ -1,28 +1,34 @@
import * as ko from "knockout";
import * as ViewModels from "../../Contracts/ViewModels";
import { useTabs } from "../../hooks/useTabs";
import TabsBase from "../Tabs/TabsBase";
import { useSelectedNode } from "../useSelectedNode";
describe("useSelectedNode.getState()", () => {
describe("useSelectedNode", () => {
const mockTab = {
tabKind: ViewModels.CollectionTabKind.Documents,
} as TabsBase;
// TODO isDataNodeSelected needs a better design and refactor, but for now, we protect some of the code paths
describe("isDataNodeSelected", () => {
afterEach(() => useSelectedNode.getState().setSelectedNode(undefined));
afterEach(() => {
useSelectedNode.getState().setSelectedNode(undefined);
useTabs.setState({ activeTab: undefined });
});
it("it should not select if no selected node", () => {
const isDataNodeSelected = useSelectedNode.getState().isDataNodeSelected(mockTab, "foo", "bar", undefined);
useTabs.setState({ activeTab: mockTab });
const isDataNodeSelected = useSelectedNode.getState().isDataNodeSelected("foo", "bar", undefined);
expect(isDataNodeSelected).toBeFalsy();
});
it("it should not select incorrect subnodekinds", () => {
useTabs.setState({ activeTab: mockTab });
useSelectedNode.getState().setSelectedNode({
nodeKind: "nodeKind",
rid: "rid",
id: ko.observable<string>("id"),
});
const isDataNodeSelected = useSelectedNode.getState().isDataNodeSelected(mockTab, "foo", "bar", undefined);
const isDataNodeSelected = useSelectedNode.getState().isDataNodeSelected("foo", "bar", undefined);
expect(isDataNodeSelected).toBeFalsy();
});
@@ -32,11 +38,12 @@ describe("useSelectedNode.getState()", () => {
rid: "rid",
id: ko.observable<string>("id"),
});
const isDataNodeSelected = useSelectedNode.getState().isDataNodeSelected(undefined, "foo", "bar", undefined);
const isDataNodeSelected = useSelectedNode.getState().isDataNodeSelected("foo", "bar", undefined);
expect(isDataNodeSelected).toBeFalsy();
});
it("should select if correct database node regardless of subnodekinds", () => {
useTabs.setState({ activeTab: mockTab });
const subNodeKind = ViewModels.CollectionTabKind.Documents;
useSelectedNode.getState().setSelectedNode({
nodeKind: "Database",
@@ -46,7 +53,7 @@ describe("useSelectedNode.getState()", () => {
} as ViewModels.TreeNode);
const isDataNodeSelected = useSelectedNode
.getState()
.isDataNodeSelected(mockTab, "dbid", undefined, [ViewModels.CollectionTabKind.Documents]);
.isDataNodeSelected("dbid", undefined, [ViewModels.CollectionTabKind.Documents]);
expect(isDataNodeSelected).toBeTruthy();
});
@@ -55,6 +62,7 @@ describe("useSelectedNode.getState()", () => {
let activeTab = {
tabKind: subNodeKind,
} as TabsBase;
useTabs.setState({ activeTab });
useSelectedNode.getState().setSelectedNode({
nodeKind: "Collection",
rid: "collrid",
@@ -62,15 +70,14 @@ describe("useSelectedNode.getState()", () => {
id: ko.observable<string>("collid"),
selectedSubnodeKind: ko.observable<ViewModels.CollectionTabKind>(subNodeKind),
} as ViewModels.TreeNode);
let isDataNodeSelected = useSelectedNode
.getState()
.isDataNodeSelected(activeTab, "dbid", "collid", [subNodeKind]);
let isDataNodeSelected = useSelectedNode.getState().isDataNodeSelected("dbid", "collid", [subNodeKind]);
expect(isDataNodeSelected).toBeTruthy();
subNodeKind = ViewModels.CollectionTabKind.Graph;
activeTab = {
tabKind: subNodeKind,
} as TabsBase;
useTabs.setState({ activeTab });
useSelectedNode.getState().setSelectedNode({
nodeKind: "Collection",
rid: "collrid",
@@ -78,7 +85,7 @@ describe("useSelectedNode.getState()", () => {
id: ko.observable<string>("collid"),
selectedSubnodeKind: ko.observable<ViewModels.CollectionTabKind>(subNodeKind),
} as ViewModels.TreeNode);
isDataNodeSelected = useSelectedNode.getState().isDataNodeSelected(activeTab, "dbid", "collid", [subNodeKind]);
isDataNodeSelected = useSelectedNode.getState().isDataNodeSelected("dbid", "collid", [subNodeKind]);
expect(isDataNodeSelected).toBeTruthy();
});
@@ -93,9 +100,10 @@ describe("useSelectedNode.getState()", () => {
const activeTab = {
tabKind: ViewModels.CollectionTabKind.Documents,
} as TabsBase;
useTabs.setState({ activeTab });
const isDataNodeSelected = useSelectedNode
.getState()
.isDataNodeSelected(activeTab, "dbid", "collid", [ViewModels.CollectionTabKind.Settings]);
.isDataNodeSelected("dbid", "collid", [ViewModels.CollectionTabKind.Settings]);
expect(isDataNodeSelected).toBeFalsy();
});
});

View File

@@ -16,6 +16,7 @@ import { Areas } from "../../Common/Constants";
import { isPublicInternetAccessAllowed } from "../../Common/DatabaseAccountUtility";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
import { useTabs } from "../../hooks/useTabs";
import { IPinnedRepo } from "../../Juno/JunoClient";
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
import { Action, ActionModifiers, Source } from "../../Shared/Telemetry/TelemetryConstants";
@@ -57,7 +58,10 @@ export class ResourceTreeAdapter implements ReactAdapter {
this.parameters = ko.observable(Date.now());
useSelectedNode.subscribe(() => this.triggerRender());
this.container.tabsManager.activeTab.subscribe((newValue: TabsBase) => this.triggerRender());
useTabs.subscribe(
() => this.triggerRender(),
(state) => state.activeTab
);
useNotebook.subscribe(
() => this.triggerRender(),
(state) => state.isNotebookEnabled
@@ -188,8 +192,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
isExpanded: false,
className: "databaseHeader",
children: [],
isSelected: () =>
useSelectedNode.getState().isDataNodeSelected(this.container.tabsManager.activeTab(), database.id()),
isSelected: () => useSelectedNode.getState().isDataNodeSelected(database.id()),
contextMenu: ResourceTreeContextMenuButtonFactory.createDatabaseContextMenu(this.container, database.id()),
onClick: async (isExpanded) => {
// Rewritten version of expandCollapseDatabase():
@@ -204,7 +207,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
databaseNode.isLoading = false;
useSelectedNode.getState().setSelectedNode(database);
useCommandBar.getState().setContextButtons([]);
this.container.tabsManager.refreshActiveTab((tab: TabsBase) => tab.collection?.databaseId === database.id());
useTabs.getState().refreshActiveTab((tab: TabsBase) => tab.collection?.databaseId === database.id());
},
onContextMenuOpen: () => useSelectedNode.getState().setSelectedNode(database),
};
@@ -215,9 +218,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
isSelected: () =>
useSelectedNode
.getState()
.isDataNodeSelected(this.container.tabsManager.activeTab(), database.id(), undefined, [
ViewModels.CollectionTabKind.DatabaseSettings,
]),
.isDataNodeSelected(database.id(), undefined, [ViewModels.CollectionTabKind.DatabaseSettings]),
onClick: database.onSettingsClick.bind(database),
});
}
@@ -265,7 +266,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
isSelected: () =>
useSelectedNode
.getState()
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id(), [
.isDataNodeSelected(collection.databaseId, collection.id(), [
ViewModels.CollectionTabKind.Documents,
ViewModels.CollectionTabKind.Graph,
]),
@@ -283,9 +284,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
isSelected: () =>
useSelectedNode
.getState()
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id(), [
ViewModels.CollectionTabKind.SchemaAnalyzer,
]),
.isDataNodeSelected(collection.databaseId, collection.id(), [ViewModels.CollectionTabKind.SchemaAnalyzer]),
});
}
@@ -296,9 +295,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
isSelected: () =>
useSelectedNode
.getState()
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id(), [
ViewModels.CollectionTabKind.Settings,
]),
.isDataNodeSelected(collection.databaseId, collection.id(), [ViewModels.CollectionTabKind.Settings]),
});
}
@@ -326,9 +323,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
isSelected: () =>
useSelectedNode
.getState()
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id(), [
ViewModels.CollectionTabKind.Conflicts,
]),
.isDataNodeSelected(collection.databaseId, collection.id(), [ViewModels.CollectionTabKind.Conflicts]),
});
}
@@ -343,10 +338,12 @@ export class ResourceTreeAdapter implements ReactAdapter {
// Rewritten version of expandCollapseCollection
useSelectedNode.getState().setSelectedNode(collection);
useCommandBar.getState().setContextButtons([]);
this.container.tabsManager.refreshActiveTab(
(tab: TabsBase) =>
tab.collection?.id() === collection.id() && tab.collection.databaseId === collection.databaseId
);
useTabs
.getState()
.refreshActiveTab(
(tab: TabsBase) =>
tab.collection?.id() === collection.id() && tab.collection.databaseId === collection.databaseId
);
},
onExpanded: () => {
if (ResourceTreeAdapter.showScriptNodes(this.container)) {
@@ -355,10 +352,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
collection.loadTriggers();
}
},
isSelected: () =>
useSelectedNode
.getState()
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id()),
isSelected: () => useSelectedNode.getState().isDataNodeSelected(collection.databaseId, collection.id()),
onContextMenuOpen: () => useSelectedNode.getState().setSelectedNode(collection),
};
}
@@ -372,17 +366,19 @@ export class ResourceTreeAdapter implements ReactAdapter {
isSelected: () =>
useSelectedNode
.getState()
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id(), [
.isDataNodeSelected(collection.databaseId, collection.id(), [
ViewModels.CollectionTabKind.StoredProcedures,
]),
contextMenu: ResourceTreeContextMenuButtonFactory.createStoreProcedureContextMenuItems(this.container, sp),
})),
onClick: () => {
collection.selectedSubnodeKind(ViewModels.CollectionTabKind.StoredProcedures);
this.container.tabsManager.refreshActiveTab(
(tab: TabsBase) =>
tab.collection?.id() === collection.id() && tab.collection.databaseId === collection.databaseId
);
useTabs
.getState()
.refreshActiveTab(
(tab: TabsBase) =>
tab.collection?.id() === collection.id() && tab.collection.databaseId === collection.databaseId
);
},
};
}
@@ -396,7 +392,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
isSelected: () =>
useSelectedNode
.getState()
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id(), [
.isDataNodeSelected(collection.databaseId, collection.id(), [
ViewModels.CollectionTabKind.UserDefinedFunctions,
]),
contextMenu: ResourceTreeContextMenuButtonFactory.createUserDefinedFunctionContextMenuItems(
@@ -406,10 +402,12 @@ export class ResourceTreeAdapter implements ReactAdapter {
})),
onClick: () => {
collection.selectedSubnodeKind(ViewModels.CollectionTabKind.UserDefinedFunctions);
this.container.tabsManager.refreshActiveTab(
(tab: TabsBase) =>
tab.collection?.id() === collection.id() && tab.collection.databaseId === collection.databaseId
);
useTabs
.getState()
.refreshActiveTab(
(tab: TabsBase) =>
tab.collection?.id() === collection.id() && tab.collection.databaseId === collection.databaseId
);
},
};
}
@@ -423,17 +421,17 @@ export class ResourceTreeAdapter implements ReactAdapter {
isSelected: () =>
useSelectedNode
.getState()
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id(), [
ViewModels.CollectionTabKind.Triggers,
]),
.isDataNodeSelected(collection.databaseId, collection.id(), [ViewModels.CollectionTabKind.Triggers]),
contextMenu: ResourceTreeContextMenuButtonFactory.createTriggerContextMenuItems(this.container, trigger),
})),
onClick: () => {
collection.selectedSubnodeKind(ViewModels.CollectionTabKind.Triggers);
this.container.tabsManager.refreshActiveTab(
(tab: TabsBase) =>
tab.collection?.id() === collection.id() && tab.collection.databaseId === collection.databaseId
);
useTabs
.getState()
.refreshActiveTab(
(tab: TabsBase) =>
tab.collection?.id() === collection.id() && tab.collection.databaseId === collection.databaseId
);
},
};
}
@@ -452,9 +450,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
children: this.getSchemaNodes(collection.schema.fields),
onClick: () => {
collection.selectedSubnodeKind(ViewModels.CollectionTabKind.Schema);
this.container.tabsManager.refreshActiveTab(
(tab: TabsBase) => tab.collection && tab.collection.rid === collection.rid
);
useTabs.getState().refreshActiveTab((tab: TabsBase) => tab.collection && tab.collection.rid === collection.rid);
},
};
}
@@ -584,7 +580,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
className: "notebookHeader galleryHeader",
onClick: () => this.container.openGallery(),
isSelected: () => {
const activeTab = this.container.tabsManager.activeTab();
const activeTab = useTabs.getState().activeTab;
return activeTab && activeTab.tabKind === ViewModels.CollectionTabKind.Gallery;
},
};
@@ -678,7 +674,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
className: "notebookHeader",
onClick: () => onFileClick(item),
isSelected: () => {
const activeTab = this.container.tabsManager.activeTab();
const activeTab = useTabs.getState().activeTab;
return (
activeTab &&
activeTab.tabKind === ViewModels.CollectionTabKind.NotebookV2 &&
@@ -833,7 +829,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
}
},
isSelected: () => {
const activeTab = this.container.tabsManager.activeTab();
const activeTab = useTabs.getState().activeTab;
return (
activeTab &&
activeTab.tabKind === ViewModels.CollectionTabKind.NotebookV2 &&

View File

@@ -3,6 +3,7 @@ import * as React from "react";
import CollectionIcon from "../../../images/tree-collection.svg";
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
import * as ViewModels from "../../Contracts/ViewModels";
import { useTabs } from "../../hooks/useTabs";
import { userContext } from "../../UserContext";
import { TreeComponent, TreeNode } from "../Controls/TreeComponent/TreeComponent";
import Explorer from "../Explorer";
@@ -24,7 +25,10 @@ export class ResourceTreeAdapterForResourceToken implements ReactAdapter {
(state) => state.resourceTokenCollection
);
useSelectedNode.subscribe(() => this.triggerRender());
this.container.tabsManager && this.container.tabsManager.activeTab.subscribe(() => this.triggerRender());
useTabs.subscribe(
() => this.triggerRender(),
(state) => state.activeTab
);
this.triggerRender();
}
@@ -55,9 +59,7 @@ export class ResourceTreeAdapterForResourceToken implements ReactAdapter {
isSelected: () =>
useSelectedNode
.getState()
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id(), [
ViewModels.CollectionTabKind.Documents,
]),
.isDataNodeSelected(collection.databaseId, collection.id(), [ViewModels.CollectionTabKind.Documents]),
});
const collectionNode: TreeNode = {
@@ -70,14 +72,13 @@ export class ResourceTreeAdapterForResourceToken implements ReactAdapter {
// Rewritten version of expandCollapseCollection
useSelectedNode.getState().setSelectedNode(collection);
useCommandBar.getState().setContextButtons([]);
this.container.tabsManager.refreshActiveTab(
(tab) => tab.collection?.id() === collection.id() && tab.collection.databaseId === collection.databaseId
);
},
isSelected: () =>
useSelectedNode
useTabs
.getState()
.isDataNodeSelected(this.container.tabsManager.activeTab(), collection.databaseId, collection.id()),
.refreshActiveTab(
(tab) => tab.collection?.id() === collection.id() && tab.collection.databaseId === collection.databaseId
);
},
isSelected: () => useSelectedNode.getState().isDataNodeSelected(collection.databaseId, collection.id()),
};
return {

View File

@@ -4,6 +4,7 @@ import * as Constants from "../../Common/Constants";
import { deleteStoredProcedure } from "../../Common/dataAccess/deleteStoredProcedure";
import { executeStoredProcedure } from "../../Common/dataAccess/executeStoredProcedure";
import * as ViewModels from "../../Contracts/ViewModels";
import { useTabs } from "../../hooks/useTabs";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../UserContext";
@@ -62,7 +63,7 @@ export default class StoredProcedure {
}
public static create(source: ViewModels.Collection, event: MouseEvent) {
const id = source.container.tabsManager.getTabs(ViewModels.CollectionTabKind.StoredProcedures).length + 1;
const id = useTabs.getState().getTabs(ViewModels.CollectionTabKind.StoredProcedures).length + 1;
const storedProcedure = <StoredProcedureDefinition>{
id: "",
body: sampleStoredProcedureBody,
@@ -84,7 +85,7 @@ export default class StoredProcedure {
}
);
source.container.tabsManager.activateNewTab(storedProcedureTab);
useTabs.getState().activateNewTab(storedProcedureTab);
}
public select() {
@@ -99,14 +100,16 @@ export default class StoredProcedure {
public open = () => {
this.select();
const storedProcedureTabs: NewStoredProcedureTab[] = this.container.tabsManager.getTabs(
ViewModels.CollectionTabKind.StoredProcedures,
(tab: TabsBase) => tab.node && tab.node.rid === this.rid
) as NewStoredProcedureTab[];
const storedProcedureTabs: NewStoredProcedureTab[] = useTabs
.getState()
.getTabs(
ViewModels.CollectionTabKind.StoredProcedures,
(tab: TabsBase) => tab.node && tab.node.rid === this.rid
) as NewStoredProcedureTab[];
let storedProcedureTab: NewStoredProcedureTab = storedProcedureTabs && storedProcedureTabs[0];
if (storedProcedureTab) {
this.container.tabsManager.activateTab(storedProcedureTab);
useTabs.getState().activateTab(storedProcedureTab);
} else {
const storedProcedureData = <StoredProcedureDefinition>{
_rid: this.rid,
@@ -131,7 +134,7 @@ export default class StoredProcedure {
}
);
this.container.tabsManager.activateNewTab(storedProcedureTab);
useTabs.getState().activateNewTab(storedProcedureTab);
}
};
public delete() {
@@ -141,7 +144,7 @@ export default class StoredProcedure {
deleteStoredProcedure(this.collection.databaseId, this.collection.id(), this.id()).then(
() => {
this.container.tabsManager.closeTabsByComparator((tab: TabsBase) => tab.node && tab.node.rid === this.rid);
useTabs.getState().closeTabsByComparator((tab: TabsBase) => tab.node && tab.node.rid === this.rid);
this.collection.children.remove(this);
},
(reason) => {}
@@ -149,10 +152,12 @@ export default class StoredProcedure {
}
public execute(params: string[], partitionKeyValue?: string): void {
const sprocTabs: NewStoredProcedureTab[] = this.container.tabsManager.getTabs(
ViewModels.CollectionTabKind.StoredProcedures,
(tab: TabsBase) => tab.node && tab.node.rid === this.rid
) as NewStoredProcedureTab[];
const sprocTabs: NewStoredProcedureTab[] = useTabs
.getState()
.getTabs(
ViewModels.CollectionTabKind.StoredProcedures,
(tab: TabsBase) => tab.node && tab.node.rid === this.rid
) as NewStoredProcedureTab[];
const sprocTab: NewStoredProcedureTab = sprocTabs && sprocTabs.length > 0 && sprocTabs[0];
sprocTab.isExecuting(true);
this.container &&

View File

@@ -3,6 +3,7 @@ import * as ko from "knockout";
import * as Constants from "../../Common/Constants";
import { deleteTrigger } from "../../Common/dataAccess/deleteTrigger";
import * as ViewModels from "../../Contracts/ViewModels";
import { useTabs } from "../../hooks/useTabs";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import Explorer from "../Explorer";
@@ -42,7 +43,7 @@ export default class Trigger {
}
public static create(source: ViewModels.Collection, event: MouseEvent) {
const id = source.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Triggers).length + 1;
const id = useTabs.getState().getTabs(ViewModels.CollectionTabKind.Triggers).length + 1;
const trigger = <StoredProcedureDefinition>{
id: "",
body: "function trigger(){}",
@@ -60,20 +61,19 @@ export default class Trigger {
node: source,
});
source.container.tabsManager.activateNewTab(triggerTab);
useTabs.getState().activateNewTab(triggerTab);
}
public open = () => {
this.select();
const triggerTabs: TriggerTab[] = this.container.tabsManager.getTabs(
ViewModels.CollectionTabKind.Triggers,
(tab) => tab.node && tab.node.rid === this.rid
) as TriggerTab[];
const triggerTabs: TriggerTab[] = useTabs
.getState()
.getTabs(ViewModels.CollectionTabKind.Triggers, (tab) => tab.node && tab.node.rid === this.rid) as TriggerTab[];
let triggerTab: TriggerTab = triggerTabs && triggerTabs[0];
if (triggerTab) {
this.container.tabsManager.activateTab(triggerTab);
useTabs.getState().activateTab(triggerTab);
} else {
const triggerData = <StoredProcedureDefinition>{
_rid: this.rid,
@@ -94,7 +94,7 @@ export default class Trigger {
node: this,
});
this.container.tabsManager.activateNewTab(triggerTab);
useTabs.getState().activateNewTab(triggerTab);
}
};
@@ -105,7 +105,7 @@ export default class Trigger {
deleteTrigger(this.collection.databaseId, this.collection.id(), this.id()).then(
() => {
this.container.tabsManager.closeTabsByComparator((tab) => tab.node && tab.node.rid === this.rid);
useTabs.getState().closeTabsByComparator((tab) => tab.node && tab.node.rid === this.rid);
this.collection.children.remove(this);
},
(reason) => {}

View File

@@ -3,6 +3,7 @@ import * as ko from "knockout";
import * as Constants from "../../Common/Constants";
import { deleteUserDefinedFunction } from "../../Common/dataAccess/deleteUserDefinedFunction";
import * as ViewModels from "../../Contracts/ViewModels";
import { useTabs } from "../../hooks/useTabs";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import Explorer from "../Explorer";
@@ -30,7 +31,7 @@ export default class UserDefinedFunction {
}
public static create(source: ViewModels.Collection, event: MouseEvent) {
const id = source.container.tabsManager.getTabs(ViewModels.CollectionTabKind.UserDefinedFunctions).length + 1;
const id = useTabs.getState().getTabs(ViewModels.CollectionTabKind.UserDefinedFunctions).length + 1;
const userDefinedFunction = {
id: "",
body: "function userDefinedFunction(){}",
@@ -46,20 +47,22 @@ export default class UserDefinedFunction {
node: source,
});
source.container.tabsManager.activateNewTab(userDefinedFunctionTab);
useTabs.getState().activateNewTab(userDefinedFunctionTab);
}
public open = () => {
this.select();
const userDefinedFunctionTabs: UserDefinedFunctionTab[] = this.container.tabsManager.getTabs(
ViewModels.CollectionTabKind.UserDefinedFunctions,
(tab) => tab.node?.rid === this.rid
) as UserDefinedFunctionTab[];
const userDefinedFunctionTabs: UserDefinedFunctionTab[] = useTabs
.getState()
.getTabs(
ViewModels.CollectionTabKind.UserDefinedFunctions,
(tab) => tab.node?.rid === this.rid
) as UserDefinedFunctionTab[];
let userDefinedFunctionTab: UserDefinedFunctionTab = userDefinedFunctionTabs && userDefinedFunctionTabs[0];
if (userDefinedFunctionTab) {
this.container.tabsManager.activateTab(userDefinedFunctionTab);
useTabs.getState().activateTab(userDefinedFunctionTab);
} else {
const userDefinedFunctionData = {
_rid: this.rid,
@@ -78,7 +81,7 @@ export default class UserDefinedFunction {
node: this,
});
this.container.tabsManager.activateNewTab(userDefinedFunctionTab);
useTabs.getState().activateNewTab(userDefinedFunctionTab);
}
};
@@ -98,7 +101,7 @@ export default class UserDefinedFunction {
deleteUserDefinedFunction(this.collection.databaseId, this.collection.id(), this.id()).then(
() => {
this.container.tabsManager.closeTabsByComparator((tab) => tab.node && tab.node.rid === this.rid);
useTabs.getState().closeTabsByComparator((tab) => tab.node && tab.node.rid === this.rid);
this.collection.children.remove(this);
},
(reason) => {}