mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-03-27 03:59:10 +00:00
* Rev up prettier * Reformat * Remove deprecated tslint * Remove call to tslint and update package-lock.json
272 lines
9.0 KiB
TypeScript
272 lines
9.0 KiB
TypeScript
/**
|
|
* Wrapper around Notebook Viewer Read only content
|
|
*/
|
|
import { IChoiceGroupProps, Icon, IProgressIndicatorProps, Link, ProgressIndicator } from "@fluentui/react";
|
|
import { Notebook } from "@nteract/commutable";
|
|
import { createContentRef } from "@nteract/core";
|
|
import * as React from "react";
|
|
import { contents } from "rx-jupyter";
|
|
import { getErrorMessage, getErrorStack, handleError } from "../../../Common/ErrorHandlingUtils";
|
|
import { IGalleryItem, JunoClient } from "../../../Juno/JunoClient";
|
|
import { SessionStorageUtility } from "../../../Shared/StorageUtility";
|
|
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
|
import { traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor";
|
|
import * as GalleryUtils from "../../../Utils/GalleryUtils";
|
|
import { DialogHost } from "../../../Utils/GalleryUtils";
|
|
import Explorer from "../../Explorer";
|
|
import { NotebookClientV2 } from "../../Notebook/NotebookClientV2";
|
|
import { NotebookComponentBootstrapper } from "../../Notebook/NotebookComponent/NotebookComponentBootstrapper";
|
|
import NotebookReadOnlyRenderer from "../../Notebook/NotebookRenderer/NotebookReadOnlyRenderer";
|
|
import { useNotebook } from "../../Notebook/useNotebook";
|
|
import { Dialog, TextFieldProps, useDialog } from "../Dialog";
|
|
import { NotebookMetadataComponent } from "./NotebookMetadataComponent";
|
|
import "./NotebookViewerComponent.less";
|
|
|
|
export interface NotebookViewerComponentProps {
|
|
container?: Explorer;
|
|
junoClient?: JunoClient;
|
|
notebookUrl: string;
|
|
galleryItem?: IGalleryItem;
|
|
isFavorite?: boolean;
|
|
backNavigationText: string;
|
|
hideInputs?: boolean;
|
|
hidePrompts?: boolean;
|
|
onBackClick: () => void;
|
|
onTagClick: (tag: string) => void;
|
|
}
|
|
|
|
interface NotebookViewerComponentState {
|
|
content: Notebook;
|
|
galleryItem?: IGalleryItem;
|
|
isFavorite?: boolean;
|
|
showProgressBar: boolean;
|
|
}
|
|
|
|
export class NotebookViewerComponent
|
|
extends React.Component<NotebookViewerComponentProps, NotebookViewerComponentState>
|
|
implements DialogHost
|
|
{
|
|
private clientManager: NotebookClientV2;
|
|
private notebookComponentBootstrapper: NotebookComponentBootstrapper;
|
|
|
|
constructor(props: NotebookViewerComponentProps) {
|
|
super(props);
|
|
|
|
this.clientManager = new NotebookClientV2({
|
|
connectionInfo: { authToken: undefined, notebookServerEndpoint: undefined, forwardingId: undefined },
|
|
databaseAccountName: undefined,
|
|
defaultExperience: "NotebookViewer",
|
|
isReadOnly: true,
|
|
cellEditorType: "codemirror",
|
|
autoSaveInterval: 365 * 24 * 3600 * 1000, // There is no way to turn off auto-save, set to 1 year
|
|
contentProvider: contents.JupyterContentProvider, // NotebookViewer only knows how to talk to Jupyter contents API
|
|
});
|
|
|
|
this.notebookComponentBootstrapper = new NotebookComponentBootstrapper({
|
|
notebookClient: this.clientManager,
|
|
contentRef: createContentRef(),
|
|
});
|
|
|
|
this.state = {
|
|
content: undefined,
|
|
galleryItem: props.galleryItem,
|
|
isFavorite: props.isFavorite,
|
|
showProgressBar: true,
|
|
};
|
|
|
|
this.loadNotebookContent();
|
|
}
|
|
|
|
private async loadNotebookContent(): Promise<void> {
|
|
const startKey = traceStart(Action.NotebooksGalleryViewNotebook, {
|
|
notebookUrl: this.props.notebookUrl,
|
|
notebookId: this.props.galleryItem?.id,
|
|
isSample: this.props.galleryItem?.isSample,
|
|
});
|
|
|
|
try {
|
|
const response = await fetch(this.props.notebookUrl);
|
|
if (!response.ok) {
|
|
this.setState({ showProgressBar: false });
|
|
throw new Error(`Received HTTP ${response.status} while fetching ${this.props.notebookUrl}`);
|
|
}
|
|
|
|
traceSuccess(
|
|
Action.NotebooksGalleryViewNotebook,
|
|
{
|
|
notebookUrl: this.props.notebookUrl,
|
|
notebookId: this.props.galleryItem?.id,
|
|
isSample: this.props.galleryItem?.isSample,
|
|
},
|
|
startKey,
|
|
);
|
|
|
|
const notebook: Notebook = await response.json();
|
|
GalleryUtils.removeNotebookViewerLink(notebook, this.props.galleryItem?.newCellId);
|
|
this.notebookComponentBootstrapper.setContent("json", notebook);
|
|
this.setState({ content: notebook, showProgressBar: false });
|
|
|
|
if (this.props.galleryItem && !SessionStorageUtility.getEntry(this.props.galleryItem.id)) {
|
|
const response = await this.props.junoClient.increaseNotebookViews(this.props.galleryItem.id);
|
|
if (!response.data) {
|
|
throw new Error(`Received HTTP ${response.status} while increasing notebook views`);
|
|
}
|
|
this.setState({ galleryItem: response.data });
|
|
SessionStorageUtility.setEntry(this.props.galleryItem?.id, "true");
|
|
}
|
|
} catch (error) {
|
|
traceFailure(
|
|
Action.NotebooksGalleryViewNotebook,
|
|
{
|
|
notebookUrl: this.props.notebookUrl,
|
|
notebookId: this.props.galleryItem?.id,
|
|
isSample: this.props.galleryItem?.isSample,
|
|
error: getErrorMessage(error),
|
|
errorStack: getErrorStack(error),
|
|
},
|
|
startKey,
|
|
);
|
|
|
|
this.setState({ showProgressBar: false });
|
|
handleError(error, "NotebookViewerComponent/loadNotebookContent", "Failed to load notebook content");
|
|
}
|
|
}
|
|
|
|
public render(): JSX.Element {
|
|
return (
|
|
<div className="notebookViewerContainer">
|
|
{this.props.backNavigationText !== undefined ? (
|
|
<Link onClick={this.props.onBackClick}>
|
|
<Icon iconName="Back" /> {this.props.backNavigationText}
|
|
</Link>
|
|
) : (
|
|
<></>
|
|
)}
|
|
|
|
{this.state.galleryItem ? (
|
|
<div style={{ margin: 10 }}>
|
|
<NotebookMetadataComponent
|
|
data={this.state.galleryItem}
|
|
isFavorite={this.state.isFavorite}
|
|
downloadButtonText={this.props.container && `Download to ${useNotebook.getState().notebookFolderName}`}
|
|
onTagClick={this.props.onTagClick}
|
|
onFavoriteClick={this.favoriteItem}
|
|
onUnfavoriteClick={this.unfavoriteItem}
|
|
onDownloadClick={this.downloadItem}
|
|
onReportAbuseClick={this.state.galleryItem.isSample ? undefined : this.reportAbuse}
|
|
/>
|
|
</div>
|
|
) : (
|
|
<></>
|
|
)}
|
|
|
|
{this.state.showProgressBar && <ProgressIndicator />}
|
|
|
|
{this.notebookComponentBootstrapper.renderComponent(NotebookReadOnlyRenderer, {
|
|
hideInputs: this.props.hideInputs,
|
|
hidePrompts: this.props.hidePrompts,
|
|
})}
|
|
<Dialog />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
public static getDerivedStateFromProps(
|
|
props: NotebookViewerComponentProps,
|
|
state: NotebookViewerComponentState,
|
|
): Partial<NotebookViewerComponentState> {
|
|
let galleryItem = props.galleryItem;
|
|
let isFavorite = props.isFavorite;
|
|
|
|
if (state.galleryItem !== undefined) {
|
|
galleryItem = state.galleryItem;
|
|
}
|
|
|
|
if (state.isFavorite !== undefined) {
|
|
isFavorite = state.isFavorite;
|
|
}
|
|
|
|
return {
|
|
galleryItem,
|
|
isFavorite,
|
|
};
|
|
}
|
|
|
|
showOkModalDialog(
|
|
title: string,
|
|
msg: string,
|
|
okLabel: string,
|
|
onOk: () => void,
|
|
progressIndicatorProps?: IProgressIndicatorProps,
|
|
): void {
|
|
useDialog.getState().openDialog({
|
|
isModal: true,
|
|
title,
|
|
subText: msg,
|
|
primaryButtonText: okLabel,
|
|
onPrimaryButtonClick: () => {
|
|
useDialog.getState().closeDialog();
|
|
onOk && onOk();
|
|
},
|
|
secondaryButtonText: undefined,
|
|
onSecondaryButtonClick: undefined,
|
|
progressIndicatorProps,
|
|
});
|
|
}
|
|
|
|
showOkCancelModalDialog(
|
|
title: string,
|
|
msg: string,
|
|
okLabel: string,
|
|
onOk: () => void,
|
|
cancelLabel: string,
|
|
onCancel: () => void,
|
|
progressIndicatorProps?: IProgressIndicatorProps,
|
|
choiceGroupProps?: IChoiceGroupProps,
|
|
textFieldProps?: TextFieldProps,
|
|
primaryButtonDisabled?: boolean,
|
|
): void {
|
|
useDialog.getState().openDialog({
|
|
isModal: true,
|
|
title,
|
|
subText: msg,
|
|
primaryButtonText: okLabel,
|
|
secondaryButtonText: cancelLabel,
|
|
onPrimaryButtonClick: () => {
|
|
useDialog.getState().closeDialog();
|
|
onOk && onOk();
|
|
},
|
|
onSecondaryButtonClick: () => {
|
|
useDialog.getState().closeDialog();
|
|
onCancel && onCancel();
|
|
},
|
|
progressIndicatorProps,
|
|
choiceGroupProps,
|
|
textFieldProps,
|
|
primaryButtonDisabled,
|
|
});
|
|
}
|
|
|
|
private favoriteItem = async (): Promise<void> => {
|
|
GalleryUtils.favoriteItem(this.props.container, this.props.junoClient, this.state.galleryItem, (item) =>
|
|
this.setState({ galleryItem: item, isFavorite: true }),
|
|
);
|
|
};
|
|
|
|
private unfavoriteItem = async (): Promise<void> => {
|
|
GalleryUtils.unfavoriteItem(this.props.container, this.props.junoClient, this.state.galleryItem, (item) =>
|
|
this.setState({ galleryItem: item, isFavorite: false }),
|
|
);
|
|
};
|
|
|
|
private downloadItem = async (): Promise<void> => {
|
|
GalleryUtils.downloadItem(this.props.container, this.props.junoClient, this.state.galleryItem, (item) =>
|
|
this.setState({ galleryItem: item }),
|
|
);
|
|
};
|
|
|
|
private reportAbuse = (): void => {
|
|
GalleryUtils.reportAbuse(this.props.junoClient, this.state.galleryItem, this, () => {});
|
|
};
|
|
}
|