Fix new resource tree (#962)

This commit is contained in:
victor-meng 2021-07-30 16:23:36 -07:00 committed by GitHub
parent 042f980b89
commit 56699ccb1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 117 additions and 55 deletions

View File

@ -54,10 +54,10 @@ export const ResourceTreeContainer: FunctionComponent<ResourceTreeContainerProps
</div> </div>
{userContext.authType === AuthType.ResourceToken ? ( {userContext.authType === AuthType.ResourceToken ? (
<ResourceTokenTree /> <ResourceTokenTree />
) : userContext.features.enableReactResourceTree ? ( ) : userContext.features.enableKoResourceTree ? (
<ResourceTree container={container} />
) : (
<div style={{ overflowY: "auto" }} data-bind="react:resourceTree" /> <div style={{ overflowY: "auto" }} data-bind="react:resourceTree" />
) : (
<ResourceTree container={container} />
)} )}
</div> </div>
{/* Collections Window - End */} {/* Collections Window - End */}

View File

@ -537,17 +537,22 @@ export default class Explorer {
} }
} }
public uploadFile(name: string, content: string, parent: NotebookContentItem): Promise<NotebookContentItem> { public uploadFile(
name: string,
content: string,
parent: NotebookContentItem,
isGithubTree?: boolean
): Promise<NotebookContentItem> {
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) { if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
const error = "Attempt to upload notebook, but notebook is not enabled"; const error = "Attempt to upload notebook, but notebook is not enabled";
handleError(error, "Explorer/uploadFile"); handleError(error, "Explorer/uploadFile");
throw new Error(error); throw new Error(error);
} }
const promise = this.notebookManager?.notebookContentClient.uploadFileAsync(name, content, parent); const promise = this.notebookManager?.notebookContentClient.uploadFileAsync(name, content, parent, isGithubTree);
promise promise
.then(() => this.resourceTree.triggerRender()) .then(() => this.resourceTree.triggerRender())
.catch((reason) => useDialog.getState().showOkModalDialog("Unable to upload file", reason)); .catch((reason) => useDialog.getState().showOkModalDialog("Unable to upload file", getErrorMessage(reason)));
return promise; return promise;
} }
@ -672,7 +677,7 @@ export default class Explorer {
return true; return true;
} }
public renameNotebook(notebookFile: NotebookContentItem): void { public renameNotebook(notebookFile: NotebookContentItem, isGithubTree?: boolean): void {
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) { if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
const error = "Attempt to rename notebook, but notebook is not enabled"; const error = "Attempt to rename notebook, but notebook is not enabled";
handleError(error, "Explorer/renameNotebook"); handleError(error, "Explorer/renameNotebook");
@ -705,7 +710,7 @@ export default class Explorer {
paneTitle="Rename Notebook" paneTitle="Rename Notebook"
defaultInput={FileSystemUtil.stripExtension(notebookFile.name, "ipynb")} defaultInput={FileSystemUtil.stripExtension(notebookFile.name, "ipynb")}
onSubmit={(notebookFile: NotebookContentItem, input: string): Promise<NotebookContentItem> => onSubmit={(notebookFile: NotebookContentItem, input: string): Promise<NotebookContentItem> =>
this.notebookManager?.notebookContentClient.renameNotebook(notebookFile, input) this.notebookManager?.notebookContentClient.renameNotebook(notebookFile, input, isGithubTree)
} }
notebookFile={notebookFile} notebookFile={notebookFile}
/> />
@ -713,7 +718,7 @@ export default class Explorer {
} }
} }
public onCreateDirectory(parent: NotebookContentItem): void { public onCreateDirectory(parent: NotebookContentItem, isGithubTree?: boolean): void {
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) { if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
const error = "Attempt to create notebook directory, but notebook is not enabled"; const error = "Attempt to create notebook directory, but notebook is not enabled";
handleError(error, "Explorer/onCreateDirectory"); handleError(error, "Explorer/onCreateDirectory");
@ -735,7 +740,7 @@ export default class Explorer {
submitButtonLabel="Create" submitButtonLabel="Create"
defaultInput="" defaultInput=""
onSubmit={(notebookFile: NotebookContentItem, input: string): Promise<NotebookContentItem> => onSubmit={(notebookFile: NotebookContentItem, input: string): Promise<NotebookContentItem> =>
this.notebookManager?.notebookContentClient.createDirectory(notebookFile, input) this.notebookManager?.notebookContentClient.createDirectory(notebookFile, input, isGithubTree)
} }
notebookFile={parent} notebookFile={parent}
/> />
@ -804,7 +809,7 @@ export default class Explorer {
} }
}; };
public deleteNotebookFile(item: NotebookContentItem): Promise<void> { public deleteNotebookFile(item: NotebookContentItem, isGithubTree?: boolean): Promise<void> {
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) { if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
const error = "Attempt to delete notebook file, but notebook is not enabled"; const error = "Attempt to delete notebook file, but notebook is not enabled";
handleError(error, "Explorer/deleteNotebookFile"); handleError(error, "Explorer/deleteNotebookFile");
@ -837,7 +842,7 @@ export default class Explorer {
return Promise.reject(); return Promise.reject();
} }
return this.notebookManager?.notebookContentClient.deleteContentItem(item).then( return this.notebookManager?.notebookContentClient.deleteContentItem(item, isGithubTree).then(
() => logConsoleInfo(`Successfully deleted: ${item.path}`), () => logConsoleInfo(`Successfully deleted: ${item.path}`),
(reason) => logConsoleError(`Failed to delete "${item.path}": ${JSON.stringify(reason)}`) (reason) => logConsoleError(`Failed to delete "${item.path}": ${JSON.stringify(reason)}`)
); );
@ -846,7 +851,7 @@ export default class Explorer {
/** /**
* This creates a new notebook file, then opens the notebook * This creates a new notebook file, then opens the notebook
*/ */
public onNewNotebookClicked(parent?: NotebookContentItem): void { public onNewNotebookClicked(parent?: NotebookContentItem, isGithubTree?: boolean): void {
if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) { if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) {
const error = "Attempt to create new notebook, but notebook is not enabled"; const error = "Attempt to create new notebook, but notebook is not enabled";
handleError(error, "Explorer/onNewNotebookClicked"); handleError(error, "Explorer/onNewNotebookClicked");
@ -861,7 +866,7 @@ export default class Explorer {
}); });
this.notebookManager?.notebookContentClient this.notebookManager?.notebookContentClient
.createNewNotebookFile(parent) .createNewNotebookFile(parent, isGithubTree)
.then((newFile: NotebookContentItem) => { .then((newFile: NotebookContentItem) => {
logConsoleInfo(`Successfully created: ${newFile.name}`); logConsoleInfo(`Successfully created: ${newFile.name}`);
TelemetryProcessor.traceSuccess( TelemetryProcessor.traceSuccess(

View File

@ -36,7 +36,7 @@ export class NotebookContentClient {
* *
* @param parent parent folder * @param parent parent folder
*/ */
public createNewNotebookFile(parent: NotebookContentItem): Promise<NotebookContentItem> { public createNewNotebookFile(parent: NotebookContentItem, isGithubTree?: boolean): Promise<NotebookContentItem> {
if (!parent || parent.type !== NotebookContentItemType.Directory) { if (!parent || parent.type !== NotebookContentItemType.Directory) {
throw new Error(`Parent must be a directory: ${parent}`); throw new Error(`Parent must be a directory: ${parent}`);
} }
@ -57,6 +57,8 @@ export class NotebookContentClient {
const notebookFile = xhr.response; const notebookFile = xhr.response;
const item = NotebookUtil.createNotebookContentItem(notebookFile.name, notebookFile.path, notebookFile.type); const item = NotebookUtil.createNotebookContentItem(notebookFile.name, notebookFile.path, notebookFile.type);
useNotebook.getState().insertNotebookItem(parent, cloneDeep(item), isGithubTree);
// TODO: delete when ResourceTreeAdapter is removed
if (parent.children) { if (parent.children) {
item.parent = parent; item.parent = parent;
parent.children.push(item); parent.children.push(item);
@ -66,9 +68,9 @@ export class NotebookContentClient {
}); });
} }
public async deleteContentItem(item: NotebookContentItem): Promise<void> { public async deleteContentItem(item: NotebookContentItem, isGithubTree?: boolean): Promise<void> {
const path = await this.deleteNotebookFile(item.path); const path = await this.deleteNotebookFile(item.path);
useNotebook.getState().deleteNotebookItem(item); useNotebook.getState().deleteNotebookItem(item, isGithubTree);
// TODO: Delete once old resource tree is removed // TODO: Delete once old resource tree is removed
if (!path || path !== item.path) { if (!path || path !== item.path) {
@ -91,7 +93,8 @@ export class NotebookContentClient {
public async uploadFileAsync( public async uploadFileAsync(
name: string, name: string,
content: string, content: string,
parent: NotebookContentItem parent: NotebookContentItem,
isGithubTree?: boolean
): Promise<NotebookContentItem> { ): Promise<NotebookContentItem> {
if (!parent || parent.type !== NotebookContentItemType.Directory) { if (!parent || parent.type !== NotebookContentItemType.Directory) {
throw new Error(`Parent must be a directory: ${parent}`); throw new Error(`Parent must be a directory: ${parent}`);
@ -115,6 +118,8 @@ export class NotebookContentClient {
.then((xhr: AjaxResponse) => { .then((xhr: AjaxResponse) => {
const notebookFile = xhr.response; const notebookFile = xhr.response;
const item = NotebookUtil.createNotebookContentItem(notebookFile.name, notebookFile.path, notebookFile.type); const item = NotebookUtil.createNotebookContentItem(notebookFile.name, notebookFile.path, notebookFile.type);
useNotebook.getState().insertNotebookItem(parent, cloneDeep(item), isGithubTree);
// TODO: delete when ResourceTreeAdapter is removed
if (parent.children) { if (parent.children) {
item.parent = parent; item.parent = parent;
parent.children.push(item); parent.children.push(item);
@ -137,7 +142,11 @@ export class NotebookContentClient {
* @param sourcePath * @param sourcePath
* @param targetName is not prefixed with path * @param targetName is not prefixed with path
*/ */
public renameNotebook(item: NotebookContentItem, targetName: string): Promise<NotebookContentItem> { public renameNotebook(
item: NotebookContentItem,
targetName: string,
isGithubTree?: boolean
): Promise<NotebookContentItem> {
const sourcePath = item.path; const sourcePath = item.path;
// Match extension // Match extension
if (sourcePath.indexOf(".") !== -1) { if (sourcePath.indexOf(".") !== -1) {
@ -163,6 +172,9 @@ export class NotebookContentClient {
item.name = notebookFile.name; item.name = notebookFile.name;
item.path = notebookFile.path; item.path = notebookFile.path;
item.timestamp = NotebookUtil.getCurrentTimestamp(); item.timestamp = NotebookUtil.getCurrentTimestamp();
useNotebook.getState().updateNotebookItem(item, isGithubTree);
return item; return item;
}); });
} }
@ -172,7 +184,11 @@ export class NotebookContentClient {
* @param parent * @param parent
* @param newDirectoryName basename of the new directory * @param newDirectoryName basename of the new directory
*/ */
public async createDirectory(parent: NotebookContentItem, newDirectoryName: string): Promise<NotebookContentItem> { public async createDirectory(
parent: NotebookContentItem,
newDirectoryName: string,
isGithubTree?: boolean
): Promise<NotebookContentItem> {
if (parent.type !== NotebookContentItemType.Directory) { if (parent.type !== NotebookContentItemType.Directory) {
throw new Error(`Parent is not a directory: ${parent.path}`); throw new Error(`Parent is not a directory: ${parent.path}`);
} }
@ -199,8 +215,11 @@ export class NotebookContentClient {
const dir = xhr.response; const dir = xhr.response;
const item = NotebookUtil.createNotebookContentItem(dir.name, dir.path, dir.type); const item = NotebookUtil.createNotebookContentItem(dir.name, dir.path, dir.type);
useNotebook.getState().insertNotebookItem(parent, cloneDeep(item), isGithubTree);
// TODO: delete when ResourceTreeAdapter is removed
item.parent = parent; item.parent = parent;
parent.children?.push(item); parent.children?.push(item);
return item; return item;
}); });
} }

View File

@ -38,8 +38,9 @@ interface NotebookState {
setNotebookBasePath: (notebookBasePath: string) => void; setNotebookBasePath: (notebookBasePath: string) => void;
refreshNotebooksEnabledStateForAccount: () => Promise<void>; refreshNotebooksEnabledStateForAccount: () => Promise<void>;
findItem: (root: NotebookContentItem, item: NotebookContentItem) => NotebookContentItem; findItem: (root: NotebookContentItem, item: NotebookContentItem) => NotebookContentItem;
updateNotebookItem: (item: NotebookContentItem) => void; insertNotebookItem: (parent: NotebookContentItem, item: NotebookContentItem, isGithubTree?: boolean) => void;
deleteNotebookItem: (item: NotebookContentItem) => void; updateNotebookItem: (item: NotebookContentItem, isGithubTree?: boolean) => void;
deleteNotebookItem: (item: NotebookContentItem, isGithubTree?: boolean) => void;
initializeNotebooksTree: (notebookManager: NotebookManager) => Promise<void>; initializeNotebooksTree: (notebookManager: NotebookManager) => Promise<void>;
initializeGitHubRepos: (pinnedRepos: IPinnedRepo[]) => void; initializeGitHubRepos: (pinnedRepos: IPinnedRepo[]) => void;
} }
@ -141,19 +142,30 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
return undefined; return undefined;
}, },
updateNotebookItem: (item: NotebookContentItem): void => { insertNotebookItem: (parent: NotebookContentItem, item: NotebookContentItem, isGithubTree?: boolean): void => {
const root = cloneDeep(get().myNotebooksContentRoot); const root = isGithubTree ? cloneDeep(get().gitHubNotebooksContentRoot) : cloneDeep(get().myNotebooksContentRoot);
const parentItem = get().findItem(root, parent);
item.parent = parentItem;
if (parentItem.children) {
parentItem.children.push(item);
} else {
parentItem.children = [item];
}
isGithubTree ? set({ gitHubNotebooksContentRoot: root }) : set({ myNotebooksContentRoot: root });
},
updateNotebookItem: (item: NotebookContentItem, isGithubTree?: boolean): void => {
const root = isGithubTree ? cloneDeep(get().gitHubNotebooksContentRoot) : cloneDeep(get().myNotebooksContentRoot);
const parentItem = get().findItem(root, item.parent); const parentItem = get().findItem(root, item.parent);
parentItem.children = parentItem.children.filter((child) => child.path !== item.path); parentItem.children = parentItem.children.filter((child) => child.path !== item.path);
parentItem.children.push(item); parentItem.children.push(item);
item.parent = parentItem; item.parent = parentItem;
set({ myNotebooksContentRoot: root }); isGithubTree ? set({ gitHubNotebooksContentRoot: root }) : set({ myNotebooksContentRoot: root });
}, },
deleteNotebookItem: (item: NotebookContentItem): void => { deleteNotebookItem: (item: NotebookContentItem, isGithubTree?: boolean): void => {
const root = cloneDeep(get().myNotebooksContentRoot); const root = isGithubTree ? cloneDeep(get().gitHubNotebooksContentRoot) : cloneDeep(get().myNotebooksContentRoot);
const parentItem = get().findItem(root, item.parent); const parentItem = get().findItem(root, item.parent);
parentItem.children = parentItem.children.filter((child) => child.path !== item.path); parentItem.children = parentItem.children.filter((child) => child.path !== item.path);
set({ myNotebooksContentRoot: root }); isGithubTree ? set({ gitHubNotebooksContentRoot: root }) : set({ myNotebooksContentRoot: root });
}, },
initializeNotebooksTree: async (notebookManager: NotebookManager): Promise<void> => { initializeNotebooksTree: async (notebookManager: NotebookManager): Promise<void> => {
const myNotebooksContentRoot = { const myNotebooksContentRoot = {
@ -216,6 +228,7 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
path: "PsuedoDir", path: "PsuedoDir",
type: NotebookContentItemType.Directory, type: NotebookContentItemType.Directory,
children: [], children: [],
parent: gitHubNotebooksContentRoot,
}; };
pinnedRepo.branches.forEach((branch) => { pinnedRepo.branches.forEach((branch) => {
@ -223,6 +236,7 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
name: branch.name, name: branch.name,
path: GitHubUtils.toContentUri(pinnedRepo.owner, pinnedRepo.name, branch.name, ""), path: GitHubUtils.toContentUri(pinnedRepo.owner, pinnedRepo.name, branch.name, ""),
type: NotebookContentItemType.Directory, type: NotebookContentItemType.Directory,
parent: repoTreeItem,
}); });
}); });

View File

@ -98,6 +98,7 @@ export const CopyNotebookPane: FunctionComponent<CopyNotebookPanelProps> = ({
const copyNotebook = async (location: Location): Promise<NotebookContentItem> => { const copyNotebook = async (location: Location): Promise<NotebookContentItem> => {
let parent: NotebookContentItem; let parent: NotebookContentItem;
let isGithubTree: boolean;
switch (location.type) { switch (location.type) {
case "MyNotebooks": case "MyNotebooks":
parent = { parent = {
@ -105,21 +106,23 @@ export const CopyNotebookPane: FunctionComponent<CopyNotebookPanelProps> = ({
path: useNotebook.getState().notebookBasePath, path: useNotebook.getState().notebookBasePath,
type: NotebookContentItemType.Directory, type: NotebookContentItemType.Directory,
}; };
isGithubTree = false;
break; break;
case "GitHub": case "GitHub":
parent = { parent = {
name: ResourceTreeAdapter.GitHubReposTitle, name: selectedLocation.branch,
path: GitHubUtils.toContentUri(selectedLocation.owner, selectedLocation.repo, selectedLocation.branch, ""), path: GitHubUtils.toContentUri(selectedLocation.owner, selectedLocation.repo, selectedLocation.branch, ""),
type: NotebookContentItemType.Directory, type: NotebookContentItemType.Directory,
}; };
isGithubTree = true;
break; break;
default: default:
throw new Error(`Unsupported location type ${location.type}`); throw new Error(`Unsupported location type ${location.type}`);
} }
return container.uploadFile(name, content, parent); return container.uploadFile(name, content, parent, isGithubTree);
}; };
const onDropDownChange = (_: FormEvent<HTMLDivElement>, option?: IDropdownOption): void => { const onDropDownChange = (_: FormEvent<HTMLDivElement>, option?: IDropdownOption): void => {

View File

@ -1,5 +1,6 @@
import { Callout, DirectionalHint, ICalloutProps, ILinkProps, Link, Stack, Text } from "@fluentui/react"; import { Callout, DirectionalHint, ICalloutProps, ILinkProps, Link, Stack, Text } from "@fluentui/react";
import * as React from "react"; import * as React from "react";
import shallow from "zustand/shallow";
import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg"; import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg";
import DeleteIcon from "../../../images/delete.svg"; import DeleteIcon from "../../../images/delete.svg";
import GalleryIcon from "../../../images/GalleryIcon.svg"; import GalleryIcon from "../../../images/GalleryIcon.svg";
@ -55,7 +56,16 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
galleryContentRoot, galleryContentRoot,
gitHubNotebooksContentRoot, gitHubNotebooksContentRoot,
updateNotebookItem, updateNotebookItem,
} = useNotebook(); } = useNotebook(
(state) => ({
isNotebookEnabled: state.isNotebookEnabled,
myNotebooksContentRoot: state.myNotebooksContentRoot,
galleryContentRoot: state.galleryContentRoot,
gitHubNotebooksContentRoot: state.gitHubNotebooksContentRoot,
updateNotebookItem: state.updateNotebookItem,
}),
shallow
);
const { activeTab, refreshActiveTab } = useTabs(); const { activeTab, refreshActiveTab } = useTabs();
const showScriptNodes = userContext.apiType === "SQL" || userContext.apiType === "Gremlin"; const showScriptNodes = userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
const pseudoDirPath = "PsuedoDir"; const pseudoDirPath = "PsuedoDir";
@ -166,7 +176,8 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
mostRecentActivity.notebookWasItemOpened(userContext.databaseAccount?.id, item); mostRecentActivity.notebookWasItemOpened(userContext.databaseAccount?.id, item);
} }
}); });
} },
true
); );
gitHubNotebooksTree.contextMenu = [ gitHubNotebooksTree.contextMenu = [
@ -202,9 +213,9 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
}; };
const buildChildNodes = ( const buildChildNodes = (
container: Explorer,
item: NotebookContentItem, item: NotebookContentItem,
onFileClick: (item: NotebookContentItem) => void onFileClick: (item: NotebookContentItem) => void,
isGithubTree?: boolean
): TreeNode[] => { ): TreeNode[] => {
if (!item || !item.children) { if (!item || !item.children) {
return []; return [];
@ -212,8 +223,8 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
return item.children.map((item) => { return item.children.map((item) => {
const result = const result =
item.type === NotebookContentItemType.Directory item.type === NotebookContentItemType.Directory
? buildNotebookDirectoryNode(item, onFileClick) ? buildNotebookDirectoryNode(item, onFileClick, isGithubTree)
: buildNotebookFileNode(item, onFileClick); : buildNotebookFileNode(item, onFileClick, isGithubTree);
result.timestamp = item.timestamp; result.timestamp = item.timestamp;
return result; return result;
}); });
@ -222,7 +233,8 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
const buildNotebookFileNode = ( const buildNotebookFileNode = (
item: NotebookContentItem, item: NotebookContentItem,
onFileClick: (item: NotebookContentItem) => void onFileClick: (item: NotebookContentItem) => void,
isGithubTree?: boolean
): TreeNode => { ): TreeNode => {
return { return {
label: item.name, label: item.name,
@ -239,17 +251,21 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
(activeTab as any).notebookPath() === item.path (activeTab as any).notebookPath() === item.path
); );
}, },
contextMenu: createFileContextMenu(container, item), contextMenu: createFileContextMenu(container, item, isGithubTree),
data: item, data: item,
}; };
}; };
const createFileContextMenu = (container: Explorer, item: NotebookContentItem): TreeNodeMenuItem[] => { const createFileContextMenu = (
container: Explorer,
item: NotebookContentItem,
isGithubTree?: boolean
): TreeNodeMenuItem[] => {
let items: TreeNodeMenuItem[] = [ let items: TreeNodeMenuItem[] = [
{ {
label: "Rename", label: "Rename",
iconSrc: NotebookIcon, iconSrc: NotebookIcon,
onClick: () => container.renameNotebook(item), onClick: () => container.renameNotebook(item, isGithubTree),
}, },
{ {
label: "Delete", label: "Delete",
@ -261,7 +277,7 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
"Confirm delete", "Confirm delete",
`Are you sure you want to delete "${item.name}"`, `Are you sure you want to delete "${item.name}"`,
"Delete", "Delete",
() => container.deleteNotebookFile(item), () => container.deleteNotebookFile(item, isGithubTree),
"Cancel", "Cancel",
undefined undefined
); );
@ -311,12 +327,16 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
} }
}; };
const createDirectoryContextMenu = (container: Explorer, item: NotebookContentItem): TreeNodeMenuItem[] => { const createDirectoryContextMenu = (
container: Explorer,
item: NotebookContentItem,
isGithubTree?: boolean
): TreeNodeMenuItem[] => {
let items: TreeNodeMenuItem[] = [ let items: TreeNodeMenuItem[] = [
{ {
label: "Refresh", label: "Refresh",
iconSrc: RefreshIcon, iconSrc: RefreshIcon,
onClick: () => loadSubitems(item), onClick: () => loadSubitems(item, isGithubTree),
}, },
{ {
label: "Delete", label: "Delete",
@ -328,7 +348,7 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
"Confirm delete", "Confirm delete",
`Are you sure you want to delete "${item.name}?"`, `Are you sure you want to delete "${item.name}?"`,
"Delete", "Delete",
() => container.deleteNotebookFile(item), () => container.deleteNotebookFile(item, isGithubTree),
"Cancel", "Cancel",
undefined undefined
); );
@ -337,17 +357,17 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
{ {
label: "Rename", label: "Rename",
iconSrc: NotebookIcon, iconSrc: NotebookIcon,
onClick: () => container.renameNotebook(item), onClick: () => container.renameNotebook(item, isGithubTree),
}, },
{ {
label: "New Directory", label: "New Directory",
iconSrc: NewNotebookIcon, iconSrc: NewNotebookIcon,
onClick: () => container.onCreateDirectory(item), onClick: () => container.onCreateDirectory(item, isGithubTree),
}, },
{ {
label: "New Notebook", label: "New Notebook",
iconSrc: NewNotebookIcon, iconSrc: NewNotebookIcon,
onClick: () => container.onNewNotebookClicked(item), onClick: () => container.onNewNotebookClicked(item, isGithubTree),
}, },
{ {
label: "Upload File", label: "Upload File",
@ -372,7 +392,8 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
const buildNotebookDirectoryNode = ( const buildNotebookDirectoryNode = (
item: NotebookContentItem, item: NotebookContentItem,
onFileClick: (item: NotebookContentItem) => void onFileClick: (item: NotebookContentItem) => void,
isGithubTree?: boolean
): TreeNode => { ): TreeNode => {
return { return {
label: item.name, label: item.name,
@ -382,7 +403,7 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
isLeavesParentsSeparate: true, isLeavesParentsSeparate: true,
onClick: () => { onClick: () => {
if (!item.children) { if (!item.children) {
loadSubitems(item); loadSubitems(item, isGithubTree);
} }
}, },
isSelected: () => { isSelected: () => {
@ -395,9 +416,9 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
(activeTab as any).notebookPath() === item.path (activeTab as any).notebookPath() === item.path
); );
}, },
contextMenu: item.path !== pseudoDirPath ? createDirectoryContextMenu(container, item) : undefined, contextMenu: item.path !== pseudoDirPath ? createDirectoryContextMenu(container, item, isGithubTree) : undefined,
data: item, data: item,
children: buildChildNodes(container, item, onFileClick), children: buildChildNodes(item, onFileClick, isGithubTree),
}; };
}; };
@ -699,9 +720,9 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
return traverse(schema); return traverse(schema);
}; };
const loadSubitems = async (item: NotebookContentItem): Promise<void> => { const loadSubitems = async (item: NotebookContentItem, isGithubTree?: boolean): Promise<void> => {
const updatedItem = await container.notebookManager?.notebookContentClient?.updateItemChildren(item); const updatedItem = await container.notebookManager?.notebookContentClient?.updateItemChildren(item);
updateNotebookItem(updatedItem); updateNotebookItem(updatedItem, isGithubTree);
}; };
const dataRootNode = buildDataTree(); const dataRootNode = buildDataTree();

View File

@ -16,7 +16,7 @@ export type Features = {
readonly enableTtl: boolean; readonly enableTtl: boolean;
readonly executeSproc: boolean; readonly executeSproc: boolean;
readonly enableAadDataPlane: boolean; readonly enableAadDataPlane: boolean;
readonly enableReactResourceTree: boolean; readonly enableKoResourceTree: boolean;
readonly hostedDataExplorer: boolean; readonly hostedDataExplorer: boolean;
readonly junoEndpoint?: string; readonly junoEndpoint?: string;
readonly livyEndpoint?: string; readonly livyEndpoint?: string;
@ -58,7 +58,7 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
enableSDKoperations: "true" === get("enablesdkoperations"), enableSDKoperations: "true" === get("enablesdkoperations"),
enableSpark: "true" === get("enablespark"), enableSpark: "true" === get("enablespark"),
enableTtl: "true" === get("enablettl"), enableTtl: "true" === get("enablettl"),
enableReactResourceTree: "true" === get("enablereactresourcetree"), enableKoResourceTree: "true" === get("enablekoresourcetree"),
executeSproc: "true" === get("dataexplorerexecutesproc"), executeSproc: "true" === get("dataexplorerexecutesproc"),
hostedDataExplorer: "true" === get("hosteddataexplorerenabled"), hostedDataExplorer: "true" === get("hosteddataexplorerenabled"),
junoEndpoint: get("junoendpoint"), junoEndpoint: get("junoendpoint"),