mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2024-12-11 06:47:01 +00:00
Fix notebook updates issues
This commit is contained in:
parent
4480a7250d
commit
e48a6a10cb
@ -7,6 +7,7 @@
|
|||||||
.main {
|
.main {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
border-right: 1px solid @BaseMedium;
|
||||||
}
|
}
|
||||||
|
|
||||||
.resourceTreeScroll {
|
.resourceTreeScroll {
|
||||||
|
@ -45,7 +45,7 @@ import { ExecuteSprocParamsPane } from "./Panes/ExecuteSprocParamsPane";
|
|||||||
import { ExplorerMetrics } from "../Common/Constants";
|
import { ExplorerMetrics } from "../Common/Constants";
|
||||||
import { ExplorerSettings } from "../Shared/ExplorerSettings";
|
import { ExplorerSettings } from "../Shared/ExplorerSettings";
|
||||||
import { FileSystemUtil } from "./Notebook/FileSystemUtil";
|
import { FileSystemUtil } from "./Notebook/FileSystemUtil";
|
||||||
import { IGalleryItem } from "../Juno/JunoClient";
|
import { IGalleryItem, IPinnedRepo } from "../Juno/JunoClient";
|
||||||
import { LoadQueryPane } from "./Panes/LoadQueryPane";
|
import { LoadQueryPane } from "./Panes/LoadQueryPane";
|
||||||
import * as Logger from "../Common/Logger";
|
import * as Logger from "../Common/Logger";
|
||||||
import { sendMessage, sendCachedDataMessage } from "../Common/MessageHandler";
|
import { sendMessage, sendCachedDataMessage } from "../Common/MessageHandler";
|
||||||
@ -103,6 +103,8 @@ export interface ExplorerParams {
|
|||||||
openDialog: (props: DialogProps) => void;
|
openDialog: (props: DialogProps) => void;
|
||||||
|
|
||||||
onRefreshNotebookList: () => void;
|
onRefreshNotebookList: () => void;
|
||||||
|
initializeGitHubRepos: (pinnedRepos: IPinnedRepo[]) => void;
|
||||||
|
getMyNotebooksContentRoot: () => NotebookContentItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Explorer {
|
export default class Explorer {
|
||||||
@ -249,7 +251,7 @@ export default class Explorer {
|
|||||||
|
|
||||||
private static readonly MaxNbDatabasesToAutoExpand = 5;
|
private static readonly MaxNbDatabasesToAutoExpand = 5;
|
||||||
|
|
||||||
constructor(private params?: ExplorerParams) {
|
constructor(public params?: ExplorerParams) {
|
||||||
this.setIsNotificationConsoleExpanded = params?.setIsNotificationConsoleExpanded;
|
this.setIsNotificationConsoleExpanded = params?.setIsNotificationConsoleExpanded;
|
||||||
this.setNotificationConsoleData = params?.setNotificationConsoleData;
|
this.setNotificationConsoleData = params?.setNotificationConsoleData;
|
||||||
this.setInProgressConsoleDataIdToBeDeleted = params?.setInProgressConsoleDataIdToBeDeleted;
|
this.setInProgressConsoleDataIdToBeDeleted = params?.setInProgressConsoleDataIdToBeDeleted;
|
||||||
@ -1720,7 +1722,7 @@ export default class Explorer {
|
|||||||
|
|
||||||
const promise = this.notebookManager?.notebookContentClient.uploadFileAsync(name, content, parent);
|
const promise = this.notebookManager?.notebookContentClient.uploadFileAsync(name, content, parent);
|
||||||
promise
|
promise
|
||||||
.then(() => this.resourceTree.triggerRender())
|
.then(() => this.params.onRefreshNotebookList())
|
||||||
.catch((reason: any) => this.showOkModalDialog("Unable to upload file", reason));
|
.catch((reason: any) => this.showOkModalDialog("Unable to upload file", reason));
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
@ -1728,7 +1730,7 @@ export default class Explorer {
|
|||||||
public async importAndOpen(path: string): Promise<boolean> {
|
public async importAndOpen(path: string): Promise<boolean> {
|
||||||
const name = NotebookUtil.getName(path);
|
const name = NotebookUtil.getName(path);
|
||||||
const item = NotebookUtil.createNotebookContentItem(name, path, "file");
|
const item = NotebookUtil.createNotebookContentItem(name, path, "file");
|
||||||
const parent = this.resourceTree.myNotebooksContentRoot;
|
const parent = this.params.getMyNotebooksContentRoot();
|
||||||
|
|
||||||
if (parent && parent.children && this.isNotebookEnabled() && this.notebookManager?.notebookClient) {
|
if (parent && parent.children && this.isNotebookEnabled() && this.notebookManager?.notebookClient) {
|
||||||
const existingItem = _.find(parent.children, (node) => node.name === name);
|
const existingItem = _.find(parent.children, (node) => node.name === name);
|
||||||
@ -1745,7 +1747,8 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async importAndOpenContent(name: string, content: string): Promise<boolean> {
|
public async importAndOpenContent(name: string, content: string): Promise<boolean> {
|
||||||
const parent = this.resourceTree.myNotebooksContentRoot;
|
// const parent = this.params.getMyNotebooksContentRoot();
|
||||||
|
const parent = this.params.getMyNotebooksContentRoot();
|
||||||
|
|
||||||
if (parent && parent.children && this.isNotebookEnabled() && this.notebookManager?.notebookClient) {
|
if (parent && parent.children && this.isNotebookEnabled() && this.notebookManager?.notebookClient) {
|
||||||
if (this.notebookToImport && this.notebookToImport.name === name && this.notebookToImport.content === content) {
|
if (this.notebookToImport && this.notebookToImport.name === name && this.notebookToImport.content === content) {
|
||||||
@ -1930,7 +1933,6 @@ export default class Explorer {
|
|||||||
|
|
||||||
return newNotebookFile;
|
return newNotebookFile;
|
||||||
});
|
});
|
||||||
result.then(() => this.resourceTree.triggerRender());
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1951,7 +1953,6 @@ export default class Explorer {
|
|||||||
defaultInput: "",
|
defaultInput: "",
|
||||||
onSubmit: (input: string) => this.notebookManager?.notebookContentClient.createDirectory(parent, input),
|
onSubmit: (input: string) => this.notebookManager?.notebookContentClient.createDirectory(parent, input),
|
||||||
});
|
});
|
||||||
result.then(() => this.resourceTree.triggerRender());
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2113,8 +2114,6 @@ export default class Explorer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("=======> refreshNotebookList");
|
|
||||||
// await this.resourceTree.initialize();
|
|
||||||
this.params?.onRefreshNotebookList();
|
this.params?.onRefreshNotebookList();
|
||||||
|
|
||||||
this.notebookManager?.refreshPinnedRepos();
|
this.notebookManager?.refreshPinnedRepos();
|
||||||
@ -2179,7 +2178,7 @@ export default class Explorer {
|
|||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
parent = parent || this.resourceTree.myNotebooksContentRoot;
|
parent = parent || this.params.getMyNotebooksContentRoot();
|
||||||
|
|
||||||
const notificationProgressId = NotificationConsoleUtils.logConsoleMessage(
|
const notificationProgressId = NotificationConsoleUtils.logConsoleMessage(
|
||||||
ConsoleDataType.InProgress,
|
ConsoleDataType.InProgress,
|
||||||
@ -2203,7 +2202,7 @@ export default class Explorer {
|
|||||||
);
|
);
|
||||||
return this.openNotebook(newFile);
|
return this.openNotebook(newFile);
|
||||||
})
|
})
|
||||||
.then(() => this.resourceTree.triggerRender())
|
.then(() => this.params.onRefreshNotebookList())
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
const errorMessage = `Failed to create a new notebook: ${getErrorMessage(error)}`;
|
const errorMessage = `Failed to create a new notebook: ${getErrorMessage(error)}`;
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, errorMessage);
|
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, errorMessage);
|
||||||
@ -2221,7 +2220,7 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onUploadToNotebookServerClicked(parent?: NotebookContentItem): void {
|
public onUploadToNotebookServerClicked(parent?: NotebookContentItem): void {
|
||||||
parent = parent || this.resourceTree.myNotebooksContentRoot;
|
parent = parent || this.params.getMyNotebooksContentRoot();
|
||||||
|
|
||||||
this.uploadFilePane.openWithOptions({
|
this.uploadFilePane.openWithOptions({
|
||||||
paneTitle: "Upload file to notebook server",
|
paneTitle: "Upload file to notebook server",
|
||||||
|
@ -29,7 +29,6 @@ import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
|||||||
export interface NotebookManagerOptions {
|
export interface NotebookManagerOptions {
|
||||||
container: Explorer;
|
container: Explorer;
|
||||||
notebookBasePath: ko.Observable<string>;
|
notebookBasePath: ko.Observable<string>;
|
||||||
// resourceTree: ResourceTreeAdapter;
|
|
||||||
refreshCommandBarButtons: () => void;
|
refreshCommandBarButtons: () => void;
|
||||||
refreshNotebookList: () => void;
|
refreshNotebookList: () => void;
|
||||||
}
|
}
|
||||||
@ -106,8 +105,8 @@ export default class NotebookManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.junoClient.subscribeToPinnedRepos((pinnedRepos) => {
|
this.junoClient.subscribeToPinnedRepos((pinnedRepos) => {
|
||||||
this.params.resourceTree.initializeGitHubRepos(pinnedRepos);
|
// TODO Move this out of NotebookManager?
|
||||||
this.params.resourceTree.triggerRender();
|
this.params.container.params.initializeGitHubRepos(pinnedRepos);
|
||||||
});
|
});
|
||||||
this.refreshPinnedRepos();
|
this.refreshPinnedRepos();
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,9 @@ import FileIcon from "../../../images/notebook/file-cosmos.svg";
|
|||||||
import PublishIcon from "../../../images/notebook/publish_content.svg";
|
import PublishIcon from "../../../images/notebook/publish_content.svg";
|
||||||
import { ArrayHashMap } from "../../Common/ArrayHashMap";
|
import { ArrayHashMap } from "../../Common/ArrayHashMap";
|
||||||
import { NotebookUtil } from "../Notebook/NotebookUtil";
|
import { NotebookUtil } from "../Notebook/NotebookUtil";
|
||||||
import { IPinnedRepo } from "../../Juno/JunoClient";
|
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { Action, ActionModifiers, Source } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers, Source } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import { Areas, Notebook } from "../../Common/Constants";
|
import { Areas } from "../../Common/Constants";
|
||||||
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
||||||
import GalleryIcon from "../../../images/GalleryIcon.svg";
|
import GalleryIcon from "../../../images/GalleryIcon.svg";
|
||||||
import { Callout, Text, Link, DirectionalHint, Stack, ICalloutProps, ILinkProps } from "office-ui-fabric-react";
|
import { Callout, Text, Link, DirectionalHint, Stack, ICalloutProps, ILinkProps } from "office-ui-fabric-react";
|
||||||
@ -32,6 +31,7 @@ import Trigger from "./Trigger";
|
|||||||
import TabsBase from "../Tabs/TabsBase";
|
import TabsBase from "../Tabs/TabsBase";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
|
import { DataTitle, NotebooksTitle, PseudoDirPath } from "../../hooks/useNotebooks";
|
||||||
|
|
||||||
export interface ResourceTreeProps {
|
export interface ResourceTreeProps {
|
||||||
// TODO remove eventually
|
// TODO remove eventually
|
||||||
@ -39,19 +39,12 @@ export interface ResourceTreeProps {
|
|||||||
|
|
||||||
lastRefreshedTime: number;
|
lastRefreshedTime: number;
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ResourceTreeState {
|
|
||||||
galleryContentRoot: NotebookContentItem;
|
galleryContentRoot: NotebookContentItem;
|
||||||
myNotebooksContentRoot: NotebookContentItem;
|
myNotebooksContentRoot: NotebookContentItem;
|
||||||
gitHubNotebooksContentRoot: NotebookContentItem;
|
gitHubNotebooksContentRoot: NotebookContentItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ResourceTree extends React.Component<ResourceTreeProps, ResourceTreeState> {
|
export class ResourceTree extends React.Component<ResourceTreeProps> {
|
||||||
private static readonly DataTitle = "DATA";
|
|
||||||
private static readonly NotebooksTitle = "NOTEBOOKS";
|
|
||||||
private static readonly PseudoDirPath = "PseudoDir";
|
|
||||||
|
|
||||||
private koSubsDatabaseIdMap: ArrayHashMap<ko.Subscription>; // database id -> ko subs
|
private koSubsDatabaseIdMap: ArrayHashMap<ko.Subscription>; // database id -> ko subs
|
||||||
private koSubsCollectionIdMap: ArrayHashMap<ko.Subscription>; // collection id -> ko subs
|
private koSubsCollectionIdMap: ArrayHashMap<ko.Subscription>; // collection id -> ko subs
|
||||||
private databaseCollectionIdMap: ArrayHashMap<string>; // database id -> collection ids
|
private databaseCollectionIdMap: ArrayHashMap<string>; // database id -> collection ids
|
||||||
@ -63,7 +56,7 @@ export class ResourceTree extends React.Component<ResourceTreeProps, ResourceTre
|
|||||||
this.state = {
|
this.state = {
|
||||||
galleryContentRoot: undefined,
|
galleryContentRoot: undefined,
|
||||||
myNotebooksContentRoot: undefined,
|
myNotebooksContentRoot: undefined,
|
||||||
gitHubNotebooksContentRoot: undefined
|
gitHubNotebooksContentRoot: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.container = props.explorer;
|
this.container = props.explorer;
|
||||||
@ -85,31 +78,6 @@ export class ResourceTree extends React.Component<ResourceTreeProps, ResourceTre
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.container.nonSystemDatabases().forEach((database: ViewModels.Database) => this.watchDatabase(database));
|
this.container.nonSystemDatabases().forEach((database: ViewModels.Database) => this.watchDatabase(database));
|
||||||
this.triggerRender();
|
|
||||||
}
|
|
||||||
|
|
||||||
private traceMyNotebookTreeInfo() {
|
|
||||||
const myNotebooksTree = this.state.myNotebooksContentRoot;
|
|
||||||
if (myNotebooksTree.children) {
|
|
||||||
// Count 1st generation children (tree is lazy-loaded)
|
|
||||||
const nodeCounts = { files: 0, notebooks: 0, directories: 0 };
|
|
||||||
myNotebooksTree.children.forEach((treeNode) => {
|
|
||||||
switch ((treeNode as NotebookContentItem).type) {
|
|
||||||
case NotebookContentItemType.File:
|
|
||||||
nodeCounts.files++;
|
|
||||||
break;
|
|
||||||
case NotebookContentItemType.Directory:
|
|
||||||
nodeCounts.directories++;
|
|
||||||
break;
|
|
||||||
case NotebookContentItemType.Notebook:
|
|
||||||
nodeCounts.notebooks++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
TelemetryProcessor.trace(Action.RefreshResourceTreeMyNotebooks, ActionModifiers.Mark, { ...nodeCounts });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
@ -120,15 +88,15 @@ export class ResourceTree extends React.Component<ResourceTreeProps, ResourceTre
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AccordionComponent>
|
<AccordionComponent>
|
||||||
<AccordionItemComponent title={ResourceTree.DataTitle} isExpanded={!this.state.gitHubNotebooksContentRoot}>
|
<AccordionItemComponent title={DataTitle} isExpanded={!this.props.gitHubNotebooksContentRoot}>
|
||||||
<TreeComponent className="dataResourceTree" rootNode={dataRootNode} />
|
<TreeComponent className="dataResourceTree" rootNode={dataRootNode} />
|
||||||
</AccordionItemComponent>
|
</AccordionItemComponent>
|
||||||
<AccordionItemComponent title={ResourceTree.NotebooksTitle}>
|
<AccordionItemComponent title={NotebooksTitle}>
|
||||||
<TreeComponent className="notebookResourceTree" rootNode={notebooksRootNode} />
|
<TreeComponent className="notebookResourceTree" rootNode={notebooksRootNode} />
|
||||||
</AccordionItemComponent>
|
</AccordionItemComponent>
|
||||||
</AccordionComponent>
|
</AccordionComponent>
|
||||||
|
|
||||||
{this.state.galleryContentRoot && this.buildGalleryCallout()}
|
{this.props.galleryContentRoot && this.buildGalleryCallout()}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -136,90 +104,6 @@ export class ResourceTree extends React.Component<ResourceTreeProps, ResourceTre
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: ResourceTreeProps): void {
|
|
||||||
if (this.props.lastRefreshedTime === undefined || prevProps.lastRefreshedTime === this.props.lastRefreshedTime) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async initialize(): Promise<void[]> {
|
|
||||||
const refreshTasks: Promise<void>[] = [];
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
galleryContentRoot: {
|
|
||||||
name: "Gallery",
|
|
||||||
path: "Gallery",
|
|
||||||
type: NotebookContentItemType.File,
|
|
||||||
},
|
|
||||||
|
|
||||||
myNotebooksContentRoot: {
|
|
||||||
name: Notebook.MyNotebooksTitle,
|
|
||||||
path: this.container.getNotebookBasePath(),
|
|
||||||
type: NotebookContentItemType.Directory,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("====> componentDidUpdate");
|
|
||||||
// Only if notebook server is available we can refresh
|
|
||||||
if (this.container.notebookServerInfo().notebookServerEndpoint) {
|
|
||||||
refreshTasks.push(
|
|
||||||
this.container.refreshContentItem(this.state.myNotebooksContentRoot).then(root => {
|
|
||||||
this.setState({ myNotebooksContentRoot: root });
|
|
||||||
// this.triggerRender();
|
|
||||||
this.traceMyNotebookTreeInfo();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.container.notebookManager?.gitHubOAuthService.isLoggedIn()) {
|
|
||||||
this.setState({
|
|
||||||
gitHubNotebooksContentRoot: {
|
|
||||||
name: Notebook.GitHubReposTitle,
|
|
||||||
path: ResourceTree.PseudoDirPath,
|
|
||||||
type: NotebookContentItemType.Directory,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
gitHubNotebooksContentRoot: undefined
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.all(refreshTasks);
|
|
||||||
}
|
|
||||||
|
|
||||||
public initializeGitHubRepos(pinnedRepos: IPinnedRepo[]): void {
|
|
||||||
if (this.state.gitHubNotebooksContentRoot) {
|
|
||||||
const { gitHubNotebooksContentRoot } = this.state;
|
|
||||||
gitHubNotebooksContentRoot.children = [];
|
|
||||||
this.setState({ gitHubNotebooksContentRoot });
|
|
||||||
|
|
||||||
pinnedRepos?.forEach((pinnedRepo) => {
|
|
||||||
const repoFullName = GitHubUtils.toRepoFullName(pinnedRepo.owner, pinnedRepo.name);
|
|
||||||
const repoTreeItem: NotebookContentItem = {
|
|
||||||
name: repoFullName,
|
|
||||||
path: ResourceTree.PseudoDirPath,
|
|
||||||
type: NotebookContentItemType.Directory,
|
|
||||||
children: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
pinnedRepo.branches.forEach((branch) => {
|
|
||||||
repoTreeItem.children.push({
|
|
||||||
name: branch.name,
|
|
||||||
path: GitHubUtils.toContentUri(pinnedRepo.owner, pinnedRepo.name, branch.name, ""),
|
|
||||||
type: NotebookContentItemType.Directory,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.state.gitHubNotebooksContentRoot.children.push(repoTreeItem);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.triggerRender();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildDataTree(): TreeNode {
|
private buildDataTree(): TreeNode {
|
||||||
const databaseTreeNodes: TreeNode[] = this.container.nonSystemDatabases().map((database: ViewModels.Database) => {
|
const databaseTreeNodes: TreeNode[] = this.container.nonSystemDatabases().map((database: ViewModels.Database) => {
|
||||||
const databaseNode: TreeNode = {
|
const databaseNode: TreeNode = {
|
||||||
@ -526,15 +410,15 @@ export class ResourceTree extends React.Component<ResourceTreeProps, ResourceTre
|
|||||||
children: [],
|
children: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.state.galleryContentRoot) {
|
if (this.props.galleryContentRoot) {
|
||||||
notebooksTree.children.push(this.buildGalleryNotebooksTree());
|
notebooksTree.children.push(this.buildGalleryNotebooksTree());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.myNotebooksContentRoot) {
|
if (this.props.myNotebooksContentRoot) {
|
||||||
notebooksTree.children.push(this.buildMyNotebooksTree());
|
notebooksTree.children.push(this.buildMyNotebooksTree());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.gitHubNotebooksContentRoot) {
|
if (this.props.gitHubNotebooksContentRoot) {
|
||||||
// collapse all other notebook nodes
|
// collapse all other notebook nodes
|
||||||
notebooksTree.children.forEach((node) => (node.isExpanded = false));
|
notebooksTree.children.forEach((node) => (node.isExpanded = false));
|
||||||
notebooksTree.children.push(this.buildGitHubNotebooksTree());
|
notebooksTree.children.push(this.buildGitHubNotebooksTree());
|
||||||
@ -604,7 +488,7 @@ export class ResourceTree extends React.Component<ResourceTreeProps, ResourceTre
|
|||||||
|
|
||||||
private buildMyNotebooksTree(): TreeNode {
|
private buildMyNotebooksTree(): TreeNode {
|
||||||
const myNotebooksTree: TreeNode = this.buildNotebookDirectoryNode(
|
const myNotebooksTree: TreeNode = this.buildNotebookDirectoryNode(
|
||||||
this.state.myNotebooksContentRoot,
|
this.props.myNotebooksContentRoot,
|
||||||
(item: NotebookContentItem) => {
|
(item: NotebookContentItem) => {
|
||||||
this.container.openNotebook(item).then((hasOpened) => {
|
this.container.openNotebook(item).then((hasOpened) => {
|
||||||
if (hasOpened) {
|
if (hasOpened) {
|
||||||
@ -625,7 +509,7 @@ export class ResourceTree extends React.Component<ResourceTreeProps, ResourceTre
|
|||||||
|
|
||||||
private buildGitHubNotebooksTree(): TreeNode {
|
private buildGitHubNotebooksTree(): TreeNode {
|
||||||
const gitHubNotebooksTree: TreeNode = this.buildNotebookDirectoryNode(
|
const gitHubNotebooksTree: TreeNode = this.buildNotebookDirectoryNode(
|
||||||
this.state.gitHubNotebooksContentRoot,
|
this.props.gitHubNotebooksContentRoot,
|
||||||
(item: NotebookContentItem) => {
|
(item: NotebookContentItem) => {
|
||||||
this.container.openNotebook(item).then((hasOpened) => {
|
this.container.openNotebook(item).then((hasOpened) => {
|
||||||
if (hasOpened) {
|
if (hasOpened) {
|
||||||
@ -723,7 +607,7 @@ export class ResourceTree extends React.Component<ResourceTreeProps, ResourceTre
|
|||||||
{
|
{
|
||||||
label: "Rename",
|
label: "Rename",
|
||||||
iconSrc: NotebookIcon,
|
iconSrc: NotebookIcon,
|
||||||
onClick: () => this.container.renameNotebook(item),
|
onClick: () => this.container.renameNotebook(item).then(() => this.triggerRender()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Delete",
|
label: "Delete",
|
||||||
@ -812,7 +696,7 @@ export class ResourceTree extends React.Component<ResourceTreeProps, ResourceTre
|
|||||||
{
|
{
|
||||||
label: "New Directory",
|
label: "New Directory",
|
||||||
iconSrc: NewNotebookIcon,
|
iconSrc: NewNotebookIcon,
|
||||||
onClick: () => this.container.onCreateDirectory(item),
|
onClick: () => this.container.onCreateDirectory(item).then(() => this.triggerRender()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "New Notebook",
|
label: "New Notebook",
|
||||||
@ -870,9 +754,7 @@ export class ResourceTree extends React.Component<ResourceTreeProps, ResourceTre
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
contextMenu:
|
contextMenu:
|
||||||
createDirectoryContextMenu && item.path !== ResourceTree.PseudoDirPath
|
createDirectoryContextMenu && item.path !== PseudoDirPath ? this.createDirectoryContextMenu(item) : undefined,
|
||||||
? this.createDirectoryContextMenu(item)
|
|
||||||
: undefined,
|
|
||||||
data: item,
|
data: item,
|
||||||
children: this.buildChildNodes(item, onFileClick, createDirectoryContextMenu, createFileContextMenu),
|
children: this.buildChildNodes(item, onFileClick, createDirectoryContextMenu, createFileContextMenu),
|
||||||
};
|
};
|
||||||
|
31
src/Main.tsx
31
src/Main.tsx
@ -55,7 +55,7 @@ import "./Libs/is-integer-polyfill";
|
|||||||
import "url-polyfill/url-polyfill.min";
|
import "url-polyfill/url-polyfill.min";
|
||||||
|
|
||||||
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
|
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
|
||||||
import { ExplorerParams } from "./Explorer/Explorer";
|
import Explorer, { ExplorerParams } from "./Explorer/Explorer";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import hdeConnectImage from "../images/HdeConnectCosmosDB.svg";
|
import hdeConnectImage from "../images/HdeConnectCosmosDB.svg";
|
||||||
@ -92,7 +92,18 @@ const App: React.FunctionComponent = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { isPanelOpen, panelContent, headerText, openSidePanel, closeSidePanel } = useSidePanel();
|
const { isPanelOpen, panelContent, headerText, openSidePanel, closeSidePanel } = useSidePanel();
|
||||||
const {lastRefreshTime, refreshList} = useNotebooks();
|
|
||||||
|
// TODO Figure out a better pattern: this is because we don't have container, yet
|
||||||
|
const context: { container: Explorer } = { container: undefined };
|
||||||
|
const {
|
||||||
|
lastRefreshTime,
|
||||||
|
galleryContentRoot,
|
||||||
|
myNotebooksContentRoot,
|
||||||
|
gitHubNotebooksContentRoot,
|
||||||
|
refreshList,
|
||||||
|
initializeGitHubRepos,
|
||||||
|
getMyNotebooksContentRoot,
|
||||||
|
} = useNotebooks(context);
|
||||||
|
|
||||||
const explorerParams: ExplorerParams = {
|
const explorerParams: ExplorerParams = {
|
||||||
setIsNotificationConsoleExpanded,
|
setIsNotificationConsoleExpanded,
|
||||||
@ -102,11 +113,16 @@ const App: React.FunctionComponent = () => {
|
|||||||
closeSidePanel,
|
closeSidePanel,
|
||||||
openDialog,
|
openDialog,
|
||||||
closeDialog,
|
closeDialog,
|
||||||
onRefreshNotebookList: refreshList
|
onRefreshNotebookList: refreshList,
|
||||||
|
initializeGitHubRepos,
|
||||||
|
getMyNotebooksContentRoot,
|
||||||
};
|
};
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const explorer = useKnockoutExplorer(config?.platform, explorerParams);
|
const explorer = useKnockoutExplorer(config?.platform, explorerParams);
|
||||||
|
|
||||||
|
// TODO fix this
|
||||||
|
context.container = explorer;
|
||||||
|
|
||||||
// const [databases, setDatabases] = useState();
|
// const [databases, setDatabases] = useState();
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// fetchDatabases().then((dbs) => {
|
// fetchDatabases().then((dbs) => {
|
||||||
@ -115,7 +131,6 @@ const App: React.FunctionComponent = () => {
|
|||||||
// });
|
// });
|
||||||
// const databases = useDatabases(explorer)
|
// const databases = useDatabases(explorer)
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flexContainer">
|
<div className="flexContainer">
|
||||||
<div
|
<div
|
||||||
@ -181,7 +196,13 @@ const App: React.FunctionComponent = () => {
|
|||||||
data-bind="if: isAuthWithResourceToken(), react:resourceTreeForResourceToken"
|
data-bind="if: isAuthWithResourceToken(), react:resourceTreeForResourceToken"
|
||||||
/>
|
/>
|
||||||
<div style={{ overflowY: "auto" }} data-bind="if: !isAuthWithResourceToken()">
|
<div style={{ overflowY: "auto" }} data-bind="if: !isAuthWithResourceToken()">
|
||||||
<ResourceTree explorer={explorer} lastRefreshedTime={lastRefreshTime} />
|
<ResourceTree
|
||||||
|
explorer={explorer}
|
||||||
|
lastRefreshedTime={lastRefreshTime}
|
||||||
|
galleryContentRoot={galleryContentRoot}
|
||||||
|
myNotebooksContentRoot={myNotebooksContentRoot}
|
||||||
|
gitHubNotebooksContentRoot={gitHubNotebooksContentRoot}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Collections Window - End */}
|
{/* Collections Window - End */}
|
||||||
|
@ -1,16 +1,149 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { Notebook } from "../Common/Constants";
|
||||||
|
import Explorer from "../Explorer/Explorer";
|
||||||
|
import { NotebookContentItem, NotebookContentItemType } from "../Explorer/Notebook/NotebookContentItem";
|
||||||
|
import { IPinnedRepo } from "../Juno/JunoClient";
|
||||||
|
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
|
||||||
|
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
import * as GitHubUtils from "../Utils/GitHubUtils";
|
||||||
|
|
||||||
|
export const DataTitle = "DATA";
|
||||||
|
export const NotebooksTitle = "NOTEBOOKS";
|
||||||
|
export const PseudoDirPath = "PseudoDir";
|
||||||
|
|
||||||
export interface NotebookHooks {
|
export interface NotebookHooks {
|
||||||
lastRefreshTime: number;
|
lastRefreshTime: number;
|
||||||
|
galleryContentRoot: NotebookContentItem;
|
||||||
|
myNotebooksContentRoot: NotebookContentItem;
|
||||||
|
gitHubNotebooksContentRoot: NotebookContentItem;
|
||||||
|
|
||||||
refreshList: () => void;
|
refreshList: () => void;
|
||||||
|
initializeGitHubRepos: (pinnedRepos: IPinnedRepo[]) => void;
|
||||||
|
getMyNotebooksContentRoot: () => NotebookContentItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useNotebooks = (): NotebookHooks => {
|
export const useNotebooks = (context: { container: Explorer }): NotebookHooks => {
|
||||||
const [lastRefreshTime, setLastRefreshTime] = useState<number>(undefined);
|
const [lastRefreshTime, setLastRefreshTime] = useState<number>(undefined);
|
||||||
|
const [galleryContentRoot, setGalleryContentRoot] = useState<NotebookContentItem>(undefined);
|
||||||
|
const [myNotebooksContentRoot, setMyNotebooksContentRoot] = useState<NotebookContentItem>(undefined);
|
||||||
|
const [gitHubNotebooksContentRoot, setGitHubNotebooksContentRoot] = useState<NotebookContentItem>(undefined);
|
||||||
|
|
||||||
const refreshList = (): void => {
|
const refreshList = (): void => {
|
||||||
|
initialize();
|
||||||
setLastRefreshTime(new Date().getTime());
|
setLastRefreshTime(new Date().getTime());
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO For now, we need to rely on this, as setMyNotebooksContentRoot() is not synchronous
|
||||||
|
let _myNotebooksContentRoot: NotebookContentItem = undefined;
|
||||||
|
const _setMyNotebooksContentRoot = (newValue: NotebookContentItem) => {
|
||||||
|
_myNotebooksContentRoot = newValue;
|
||||||
|
setMyNotebooksContentRoot(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialize = (): Promise<void[]> => {
|
||||||
|
const refreshTasks: Promise<void>[] = [];
|
||||||
|
|
||||||
|
setGalleryContentRoot({
|
||||||
|
name: "Gallery",
|
||||||
|
path: "Gallery",
|
||||||
|
type: NotebookContentItemType.File,
|
||||||
|
});
|
||||||
|
|
||||||
|
const _myNotebooksContentRoot = {
|
||||||
|
name: Notebook.MyNotebooksTitle,
|
||||||
|
path: context.container.getNotebookBasePath(),
|
||||||
|
type: NotebookContentItemType.Directory,
|
||||||
|
};
|
||||||
|
_setMyNotebooksContentRoot(_myNotebooksContentRoot);
|
||||||
|
|
||||||
|
// Only if notebook server is available we can refresh
|
||||||
|
if (context.container.notebookServerInfo().notebookServerEndpoint) {
|
||||||
|
refreshTasks.push(
|
||||||
|
context.container.refreshContentItem(_myNotebooksContentRoot).then((root) => {
|
||||||
|
_setMyNotebooksContentRoot({ ...root });
|
||||||
|
traceMyNotebookTreeInfo(root);
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { lastRefreshTime, refreshList };
|
initializeGitHubNotebooksContentRoot();
|
||||||
|
return Promise.all(refreshTasks);
|
||||||
|
};
|
||||||
|
|
||||||
|
const traceMyNotebookTreeInfo = (myNotebooksTree: NotebookContentItem) => {
|
||||||
|
if (myNotebooksTree.children) {
|
||||||
|
// Count 1st generation children (tree is lazy-loaded)
|
||||||
|
const nodeCounts = { files: 0, notebooks: 0, directories: 0 };
|
||||||
|
myNotebooksTree.children.forEach((treeNode) => {
|
||||||
|
switch ((treeNode as NotebookContentItem).type) {
|
||||||
|
case NotebookContentItemType.File:
|
||||||
|
nodeCounts.files++;
|
||||||
|
break;
|
||||||
|
case NotebookContentItemType.Directory:
|
||||||
|
nodeCounts.directories++;
|
||||||
|
break;
|
||||||
|
case NotebookContentItemType.Notebook:
|
||||||
|
nodeCounts.notebooks++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
TelemetryProcessor.trace(Action.RefreshResourceTreeMyNotebooks, ActionModifiers.Mark, { ...nodeCounts });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const initializeGitHubNotebooksContentRoot = (): NotebookContentItem => {
|
||||||
|
let root: NotebookContentItem = undefined;
|
||||||
|
|
||||||
|
if (context.container.notebookManager?.gitHubOAuthService.isLoggedIn()) {
|
||||||
|
root = {
|
||||||
|
name: Notebook.GitHubReposTitle,
|
||||||
|
path: PseudoDirPath,
|
||||||
|
type: NotebookContentItemType.Directory,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
setGitHubNotebooksContentRoot(root);
|
||||||
|
return root;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initializeGitHubRepos = (pinnedRepos: IPinnedRepo[]): void => {
|
||||||
|
const _gitHubNotebooksContentRoot = initializeGitHubNotebooksContentRoot();
|
||||||
|
|
||||||
|
if (_gitHubNotebooksContentRoot) {
|
||||||
|
_gitHubNotebooksContentRoot.children = [];
|
||||||
|
|
||||||
|
pinnedRepos?.forEach((pinnedRepo) => {
|
||||||
|
const repoFullName = GitHubUtils.toRepoFullName(pinnedRepo.owner, pinnedRepo.name);
|
||||||
|
const repoTreeItem: NotebookContentItem = {
|
||||||
|
name: repoFullName,
|
||||||
|
path: PseudoDirPath,
|
||||||
|
type: NotebookContentItemType.Directory,
|
||||||
|
children: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
pinnedRepo.branches.forEach((branch) => {
|
||||||
|
repoTreeItem.children.push({
|
||||||
|
name: branch.name,
|
||||||
|
path: GitHubUtils.toContentUri(pinnedRepo.owner, pinnedRepo.name, branch.name, ""),
|
||||||
|
type: NotebookContentItemType.Directory,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
_gitHubNotebooksContentRoot.children.push(repoTreeItem);
|
||||||
|
});
|
||||||
|
|
||||||
|
setGitHubNotebooksContentRoot({ ..._gitHubNotebooksContentRoot });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
lastRefreshTime,
|
||||||
|
galleryContentRoot,
|
||||||
|
myNotebooksContentRoot,
|
||||||
|
gitHubNotebooksContentRoot,
|
||||||
|
refreshList,
|
||||||
|
initializeGitHubRepos,
|
||||||
|
getMyNotebooksContentRoot: () => _myNotebooksContentRoot,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user