Add telemetry for Notebooks Gallery and other updates (#413)
* Add telemetry for Notebooks Gallery * More changes * Address feedback and fix lint error * Fix margins for My published work
This commit is contained in:
parent
e0063c76d9
commit
5038a01079
|
@ -79,12 +79,16 @@ export class GalleryCardComponent extends React.Component<GalleryCardComponentPr
|
||||||
|
|
||||||
<Card.Section styles={{ root: { padding: GalleryCardComponent.cardItemGapBig } }}>
|
<Card.Section styles={{ root: { padding: GalleryCardComponent.cardItemGapBig } }}>
|
||||||
<Text variant="small" nowrap>
|
<Text variant="small" nowrap>
|
||||||
{this.props.data.tags?.map((tag, index, array) => (
|
{this.props.data.tags ? (
|
||||||
<span key={tag}>
|
this.props.data.tags.map((tag, index, array) => (
|
||||||
<Link onClick={(event) => this.onClick(event, () => this.props.onTagClick(tag))}>{tag}</Link>
|
<span key={tag}>
|
||||||
{index === array.length - 1 ? <></> : ", "}
|
<Link onClick={(event) => this.onClick(event, () => this.props.onTagClick(tag))}>{tag}</Link>
|
||||||
</span>
|
{index === array.length - 1 ? <></> : ", "}
|
||||||
))}
|
</span>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<br />
|
||||||
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Text
|
<Text
|
||||||
|
|
|
@ -2,7 +2,9 @@ import * as React from "react";
|
||||||
import { JunoClient } from "../../../Juno/JunoClient";
|
import { JunoClient } from "../../../Juno/JunoClient";
|
||||||
import { HttpStatusCodes, CodeOfConductEndpoints } from "../../../Common/Constants";
|
import { HttpStatusCodes, CodeOfConductEndpoints } from "../../../Common/Constants";
|
||||||
import { Stack, Text, Checkbox, PrimaryButton, Link } from "office-ui-fabric-react";
|
import { Stack, Text, Checkbox, PrimaryButton, Link } from "office-ui-fabric-react";
|
||||||
import { handleError } from "../../../Common/ErrorHandlingUtils";
|
import { getErrorMessage, getErrorStack, handleError } from "../../../Common/ErrorHandlingUtils";
|
||||||
|
import { trace, traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||||
|
|
||||||
export interface CodeOfConductComponentProps {
|
export interface CodeOfConductComponentProps {
|
||||||
junoClient: JunoClient;
|
junoClient: JunoClient;
|
||||||
|
@ -14,11 +16,11 @@ interface CodeOfConductComponentState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CodeOfConductComponent extends React.Component<CodeOfConductComponentProps, CodeOfConductComponentState> {
|
export class CodeOfConductComponent extends React.Component<CodeOfConductComponentProps, CodeOfConductComponentState> {
|
||||||
|
private viewCodeOfConductTraced: boolean;
|
||||||
private descriptionPara1: string;
|
private descriptionPara1: string;
|
||||||
private descriptionPara2: string;
|
private descriptionPara2: string;
|
||||||
private descriptionPara3: string;
|
private descriptionPara3: string;
|
||||||
private link1: { label: string; url: string };
|
private link1: { label: string; url: string };
|
||||||
private link2: { label: string; url: string };
|
|
||||||
|
|
||||||
constructor(props: CodeOfConductComponentProps) {
|
constructor(props: CodeOfConductComponentProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -27,23 +29,34 @@ export class CodeOfConductComponent extends React.Component<CodeOfConductCompone
|
||||||
readCodeOfConduct: false,
|
readCodeOfConduct: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.descriptionPara1 = "Azure CosmosDB Notebook Gallery - Code of Conduct and Privacy Statement";
|
this.descriptionPara1 = "Azure Cosmos DB Notebook Gallery - Code of Conduct";
|
||||||
this.descriptionPara2 =
|
this.descriptionPara2 = "The notebook public gallery contains notebook samples shared by users of Azure Cosmos DB.";
|
||||||
"Azure Cosmos DB Notebook Public Gallery contains notebook samples shared by users of Cosmos DB.";
|
this.descriptionPara3 = "In order to view and publish your samples to the gallery, you must accept the ";
|
||||||
this.descriptionPara3 = "In order to access Azure Cosmos DB Notebook Gallery resources, you must accept the ";
|
this.link1 = { label: "code of conduct.", url: CodeOfConductEndpoints.codeOfConduct };
|
||||||
this.link1 = { label: "code of conduct", url: CodeOfConductEndpoints.codeOfConduct };
|
|
||||||
this.link2 = { label: "privacy statement", url: CodeOfConductEndpoints.privacyStatement };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async acceptCodeOfConduct(): Promise<void> {
|
private async acceptCodeOfConduct(): Promise<void> {
|
||||||
|
const startKey = traceStart(Action.NotebooksGalleryAcceptCodeOfConduct);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await this.props.junoClient.acceptCodeOfConduct();
|
const response = await this.props.junoClient.acceptCodeOfConduct();
|
||||||
if (response.status !== HttpStatusCodes.OK && response.status !== HttpStatusCodes.NoContent) {
|
if (response.status !== HttpStatusCodes.OK && response.status !== HttpStatusCodes.NoContent) {
|
||||||
throw new Error(`Received HTTP ${response.status} when accepting code of conduct`);
|
throw new Error(`Received HTTP ${response.status} when accepting code of conduct`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
traceSuccess(Action.NotebooksGalleryAcceptCodeOfConduct, startKey);
|
||||||
|
|
||||||
this.props.onAcceptCodeOfConduct(response.data);
|
this.props.onAcceptCodeOfConduct(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
traceFailure(
|
||||||
|
Action.NotebooksGalleryAcceptCodeOfConduct,
|
||||||
|
{
|
||||||
|
error: getErrorMessage(error),
|
||||||
|
errorStack: getErrorStack(error),
|
||||||
|
},
|
||||||
|
startKey
|
||||||
|
);
|
||||||
|
|
||||||
handleError(error, "CodeOfConductComponent/acceptCodeOfConduct", "Failed to accept code of conduct");
|
handleError(error, "CodeOfConductComponent/acceptCodeOfConduct", "Failed to accept code of conduct");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,6 +66,11 @@ export class CodeOfConductComponent extends React.Component<CodeOfConductCompone
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
|
if (!this.viewCodeOfConductTraced) {
|
||||||
|
this.viewCodeOfConductTraced = true;
|
||||||
|
trace(Action.NotebooksGalleryViewCodeOfConduct);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack tokens={{ childrenGap: 20 }}>
|
<Stack tokens={{ childrenGap: 20 }}>
|
||||||
<Stack.Item>
|
<Stack.Item>
|
||||||
|
@ -69,10 +87,6 @@ export class CodeOfConductComponent extends React.Component<CodeOfConductCompone
|
||||||
<Link href={this.link1.url} target="_blank">
|
<Link href={this.link1.url} target="_blank">
|
||||||
{this.link1.label}
|
{this.link1.label}
|
||||||
</Link>
|
</Link>
|
||||||
{" and "}
|
|
||||||
<Link href={this.link2.url} target="_blank">
|
|
||||||
{this.link2.label}
|
|
||||||
</Link>
|
|
||||||
</Text>
|
</Text>
|
||||||
</Stack.Item>
|
</Stack.Item>
|
||||||
|
|
||||||
|
@ -87,7 +101,7 @@ export class CodeOfConductComponent extends React.Component<CodeOfConductCompone
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
label="I have read and accepted the code of conduct and privacy statement"
|
label="I have read and accepted the code of conduct."
|
||||||
onChange={this.onChangeCheckbox}
|
onChange={this.onChangeCheckbox}
|
||||||
/>
|
/>
|
||||||
</Stack.Item>
|
</Stack.Item>
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
IPivotProps,
|
IPivotProps,
|
||||||
IRectangle,
|
IRectangle,
|
||||||
Label,
|
Label,
|
||||||
|
Link,
|
||||||
List,
|
List,
|
||||||
Overlay,
|
Overlay,
|
||||||
Pivot,
|
Pivot,
|
||||||
|
@ -28,6 +29,8 @@ import Explorer from "../../Explorer";
|
||||||
import { CodeOfConductComponent } from "./CodeOfConductComponent";
|
import { CodeOfConductComponent } from "./CodeOfConductComponent";
|
||||||
import { InfoComponent } from "./InfoComponent/InfoComponent";
|
import { InfoComponent } from "./InfoComponent/InfoComponent";
|
||||||
import { handleError } from "../../../Common/ErrorHandlingUtils";
|
import { handleError } from "../../../Common/ErrorHandlingUtils";
|
||||||
|
import { trace } from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||||
|
|
||||||
export interface GalleryViewerComponentProps {
|
export interface GalleryViewerComponentProps {
|
||||||
container?: Explorer;
|
container?: Explorer;
|
||||||
|
@ -87,6 +90,12 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||||
|
|
||||||
private readonly sortingOptions: IDropdownOption[];
|
private readonly sortingOptions: IDropdownOption[];
|
||||||
|
|
||||||
|
private viewGalleryTraced: boolean;
|
||||||
|
private viewOfficialSamplesTraced: boolean;
|
||||||
|
private viewPublicGalleryTraced: boolean;
|
||||||
|
private viewFavoritesTraced: boolean;
|
||||||
|
private viewPublishedNotebooksTraced: boolean;
|
||||||
|
|
||||||
private sampleNotebooks: IGalleryItem[];
|
private sampleNotebooks: IGalleryItem[];
|
||||||
private publicNotebooks: IGalleryItem[];
|
private publicNotebooks: IGalleryItem[];
|
||||||
private favoriteNotebooks: IGalleryItem[];
|
private favoriteNotebooks: IGalleryItem[];
|
||||||
|
@ -138,6 +147,8 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
|
this.traceViewGallery();
|
||||||
|
|
||||||
const tabs: GalleryTabInfo[] = [this.createSamplesTab(GalleryTab.OfficialSamples, this.state.sampleNotebooks)];
|
const tabs: GalleryTabInfo[] = [this.createSamplesTab(GalleryTab.OfficialSamples, this.state.sampleNotebooks)];
|
||||||
|
|
||||||
if (this.props.container?.isGalleryPublishEnabled()) {
|
if (this.props.container?.isGalleryPublishEnabled()) {
|
||||||
|
@ -185,11 +196,58 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private traceViewGallery = (): void => {
|
||||||
|
if (!this.viewGalleryTraced) {
|
||||||
|
this.viewGalleryTraced = true;
|
||||||
|
trace(Action.NotebooksGalleryViewGallery);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this.state.selectedTab) {
|
||||||
|
case GalleryTab.OfficialSamples:
|
||||||
|
if (!this.viewOfficialSamplesTraced) {
|
||||||
|
this.resetViewGalleryTabTracedFlags();
|
||||||
|
this.viewOfficialSamplesTraced = true;
|
||||||
|
trace(Action.NotebooksGalleryViewOfficialSamples);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GalleryTab.PublicGallery:
|
||||||
|
if (!this.viewPublicGalleryTraced) {
|
||||||
|
this.resetViewGalleryTabTracedFlags();
|
||||||
|
this.viewPublicGalleryTraced = true;
|
||||||
|
trace(Action.NotebooksGalleryViewPublicGallery);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GalleryTab.Favorites:
|
||||||
|
if (!this.viewFavoritesTraced) {
|
||||||
|
this.resetViewGalleryTabTracedFlags();
|
||||||
|
this.viewFavoritesTraced = true;
|
||||||
|
trace(Action.NotebooksGalleryViewFavorites);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GalleryTab.Published:
|
||||||
|
if (!this.viewPublishedNotebooksTraced) {
|
||||||
|
this.resetViewGalleryTabTracedFlags();
|
||||||
|
this.viewPublishedNotebooksTraced = true;
|
||||||
|
trace(Action.NotebooksGalleryViewPublishedNotebooks);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown selected tab ${this.state.selectedTab}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private resetViewGalleryTabTracedFlags = (): void => {
|
||||||
|
this.viewOfficialSamplesTraced = false;
|
||||||
|
this.viewPublicGalleryTraced = false;
|
||||||
|
this.viewFavoritesTraced = false;
|
||||||
|
this.viewPublishedNotebooksTraced = false;
|
||||||
|
};
|
||||||
|
|
||||||
private isEmptyData = (data: IGalleryItem[]): boolean => {
|
private isEmptyData = (data: IGalleryItem[]): boolean => {
|
||||||
return !data || data.length === 0;
|
return !data || data.length === 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
private createEmptyTabContent = (iconName: string, line1: string, line2: string): JSX.Element => {
|
private createEmptyTabContent = (iconName: string, line1: JSX.Element, line2: JSX.Element): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Stack horizontalAlign="center" tokens={{ childrenGap: 10 }}>
|
<Stack horizontalAlign="center" tokens={{ childrenGap: 10 }}>
|
||||||
<FontIcon iconName={iconName} style={{ fontSize: 100, color: "lightgray", marginTop: 20 }} />
|
<FontIcon iconName={iconName} style={{ fontSize: 100, color: "lightgray", marginTop: 20 }} />
|
||||||
|
@ -223,8 +281,12 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||||
content: this.isEmptyData(data)
|
content: this.isEmptyData(data)
|
||||||
? this.createEmptyTabContent(
|
? this.createEmptyTabContent(
|
||||||
"ContactHeart",
|
"ContactHeart",
|
||||||
"You have not favorited anything",
|
<>You don't have any favorites yet</>,
|
||||||
"Favorite any notebook from Official samples or Public gallery"
|
<>
|
||||||
|
Favorite any notebook from the{" "}
|
||||||
|
<Link onClick={() => this.setState({ selectedTab: GalleryTab.OfficialSamples })}>official samples</Link>{" "}
|
||||||
|
or <Link onClick={() => this.setState({ selectedTab: GalleryTab.PublicGallery })}>public gallery</Link>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
: this.createSearchBarHeader(this.createCardsTabContent(data)),
|
: this.createSearchBarHeader(this.createCardsTabContent(data)),
|
||||||
};
|
};
|
||||||
|
@ -236,8 +298,11 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||||
content: this.isEmptyData(data)
|
content: this.isEmptyData(data)
|
||||||
? this.createEmptyTabContent(
|
? this.createEmptyTabContent(
|
||||||
"Contact",
|
"Contact",
|
||||||
"You have not published anything",
|
<>
|
||||||
"Publish your sample notebooks to share your published work with others"
|
You have not published anything to the{" "}
|
||||||
|
<Link onClick={() => this.setState({ selectedTab: GalleryTab.PublicGallery })}>public gallery</Link> yet
|
||||||
|
</>,
|
||||||
|
<>Publish your notebooks to share your work with other users</>
|
||||||
)
|
)
|
||||||
: this.createPublishedNotebooksTabContent(data),
|
: this.createPublishedNotebooksTabContent(data),
|
||||||
};
|
};
|
||||||
|
@ -250,7 +315,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||||
{published?.length > 0 &&
|
{published?.length > 0 &&
|
||||||
this.createPublishedNotebooksSectionContent(
|
this.createPublishedNotebooksSectionContent(
|
||||||
undefined,
|
undefined,
|
||||||
"You have successfully published the following notebook(s) to public gallery and shared with other Azure Cosmos DB users.",
|
"You have successfully published and shared the following notebook(s) to the public gallery.",
|
||||||
this.createCardsTabContent(published)
|
this.createCardsTabContent(published)
|
||||||
)}
|
)}
|
||||||
{underReview?.length > 0 &&
|
{underReview?.length > 0 &&
|
||||||
|
@ -278,8 +343,10 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||||
): JSX.Element => {
|
): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Stack tokens={{ childrenGap: 10 }}>
|
<Stack tokens={{ childrenGap: 10 }}>
|
||||||
{title && <Text styles={{ root: { fontWeight: FontWeights.semibold } }}>{title}</Text>}
|
{title && (
|
||||||
{description && <Text>{description}</Text>}
|
<Text styles={{ root: { fontWeight: FontWeights.semibold, marginLeft: 10, marginRight: 10 } }}>{title}</Text>
|
||||||
|
)}
|
||||||
|
{description && <Text styles={{ root: { marginLeft: 10, marginRight: 10 } }}>{description}</Text>}
|
||||||
{content}
|
{content}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
@ -344,7 +411,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||||
|
|
||||||
private createPolicyViolationsListContent(data: IGalleryItem[]): JSX.Element {
|
private createPolicyViolationsListContent(data: IGalleryItem[]): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<table>
|
<table style={{ margin: 10 }}>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
|
|
|
@ -17,35 +17,28 @@ exports[`CodeOfConductComponent renders 1`] = `
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Azure CosmosDB Notebook Gallery - Code of Conduct and Privacy Statement
|
Azure Cosmos DB Notebook Gallery - Code of Conduct
|
||||||
</Text>
|
</Text>
|
||||||
</StackItem>
|
</StackItem>
|
||||||
<StackItem>
|
<StackItem>
|
||||||
<Text>
|
<Text>
|
||||||
Azure Cosmos DB Notebook Public Gallery contains notebook samples shared by users of Cosmos DB.
|
The notebook public gallery contains notebook samples shared by users of Azure Cosmos DB.
|
||||||
</Text>
|
</Text>
|
||||||
</StackItem>
|
</StackItem>
|
||||||
<StackItem>
|
<StackItem>
|
||||||
<Text>
|
<Text>
|
||||||
In order to access Azure Cosmos DB Notebook Gallery resources, you must accept the
|
In order to view and publish your samples to the gallery, you must accept the
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
href="https://aka.ms/cosmos-code-of-conduct"
|
href="https://aka.ms/cosmos-code-of-conduct"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
code of conduct
|
code of conduct.
|
||||||
</StyledLinkBase>
|
|
||||||
and
|
|
||||||
<StyledLinkBase
|
|
||||||
href="https://aka.ms/ms-privacy-policy"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
privacy statement
|
|
||||||
</StyledLinkBase>
|
</StyledLinkBase>
|
||||||
</Text>
|
</Text>
|
||||||
</StackItem>
|
</StackItem>
|
||||||
<StackItem>
|
<StackItem>
|
||||||
<StyledCheckboxBase
|
<StyledCheckboxBase
|
||||||
label="I have read and accepted the code of conduct and privacy statement"
|
label="I have read and accepted the code of conduct."
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
|
|
|
@ -18,7 +18,9 @@ import Explorer from "../../Explorer";
|
||||||
import { NotebookV4 } from "@nteract/commutable/lib/v4";
|
import { NotebookV4 } from "@nteract/commutable/lib/v4";
|
||||||
import { SessionStorageUtility } from "../../../Shared/StorageUtility";
|
import { SessionStorageUtility } from "../../../Shared/StorageUtility";
|
||||||
import { DialogHost } from "../../../Utils/GalleryUtils";
|
import { DialogHost } from "../../../Utils/GalleryUtils";
|
||||||
import { handleError } from "../../../Common/ErrorHandlingUtils";
|
import { getErrorMessage, getErrorStack, handleError } from "../../../Common/ErrorHandlingUtils";
|
||||||
|
import { traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||||
|
|
||||||
export interface NotebookViewerComponentProps {
|
export interface NotebookViewerComponentProps {
|
||||||
container?: Explorer;
|
container?: Explorer;
|
||||||
|
@ -77,6 +79,11 @@ export class NotebookViewerComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadNotebookContent(): Promise<void> {
|
private async loadNotebookContent(): Promise<void> {
|
||||||
|
const startKey = traceStart(Action.NotebooksGalleryViewNotebook, {
|
||||||
|
notebookUrl: this.props.notebookUrl,
|
||||||
|
notebookId: this.props.galleryItem?.id,
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(this.props.notebookUrl);
|
const response = await fetch(this.props.notebookUrl);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
@ -84,6 +91,12 @@ export class NotebookViewerComponent
|
||||||
throw new Error(`Received HTTP ${response.status} while fetching ${this.props.notebookUrl}`);
|
throw new Error(`Received HTTP ${response.status} while fetching ${this.props.notebookUrl}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
traceSuccess(
|
||||||
|
Action.NotebooksGalleryViewNotebook,
|
||||||
|
{ notebookUrl: this.props.notebookUrl, notebookId: this.props.galleryItem?.id },
|
||||||
|
startKey
|
||||||
|
);
|
||||||
|
|
||||||
const notebook: Notebook = await response.json();
|
const notebook: Notebook = await response.json();
|
||||||
this.removeNotebookViewerLink(notebook, this.props.galleryItem?.newCellId);
|
this.removeNotebookViewerLink(notebook, this.props.galleryItem?.newCellId);
|
||||||
this.notebookComponentBootstrapper.setContent("json", notebook);
|
this.notebookComponentBootstrapper.setContent("json", notebook);
|
||||||
|
@ -98,6 +111,17 @@ export class NotebookViewerComponent
|
||||||
SessionStorageUtility.setEntry(this.props.galleryItem?.id, "true");
|
SessionStorageUtility.setEntry(this.props.galleryItem?.id, "true");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
traceFailure(
|
||||||
|
Action.NotebooksGalleryViewNotebook,
|
||||||
|
{
|
||||||
|
notebookUrl: this.props.notebookUrl,
|
||||||
|
notebookId: this.props.galleryItem?.id,
|
||||||
|
error: getErrorMessage(error),
|
||||||
|
errorStack: getErrorStack(error),
|
||||||
|
},
|
||||||
|
startKey
|
||||||
|
);
|
||||||
|
|
||||||
this.setState({ showProgressBar: false });
|
this.setState({ showProgressBar: false });
|
||||||
handleError(error, "NotebookViewerComponent/loadNotebookContent", "Failed to load notebook content");
|
handleError(error, "NotebookViewerComponent/loadNotebookContent", "Failed to load notebook content");
|
||||||
}
|
}
|
||||||
|
|
|
@ -2253,7 +2253,7 @@ export default class Explorer {
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async publishNotebook(name: string, content: string | unknown, parentDomElement: HTMLElement): Promise<void> {
|
public async publishNotebook(name: string, content: string | unknown, parentDomElement?: HTMLElement): Promise<void> {
|
||||||
if (this.notebookManager) {
|
if (this.notebookManager) {
|
||||||
await this.notebookManager.openPublishNotebookPane(
|
await this.notebookManager.openPublishNotebookPane(
|
||||||
name,
|
name,
|
||||||
|
|
|
@ -10,8 +10,10 @@ import { ImmutableNotebook } from "@nteract/commutable/src";
|
||||||
import { toJS } from "@nteract/commutable";
|
import { toJS } from "@nteract/commutable";
|
||||||
import { CodeOfConductComponent } from "../Controls/NotebookGallery/CodeOfConductComponent";
|
import { CodeOfConductComponent } from "../Controls/NotebookGallery/CodeOfConductComponent";
|
||||||
import { HttpStatusCodes } from "../../Common/Constants";
|
import { HttpStatusCodes } from "../../Common/Constants";
|
||||||
import { handleError, getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
import { handleError, getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||||
import { GalleryTab } from "../Controls/NotebookGallery/GalleryViewerComponent";
|
import { GalleryTab } from "../Controls/NotebookGallery/GalleryViewerComponent";
|
||||||
|
import { traceFailure, traceStart, traceSuccess } from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
|
|
||||||
export class PublishNotebookPaneAdapter implements ReactAdapter {
|
export class PublishNotebookPaneAdapter implements ReactAdapter {
|
||||||
parameters: ko.Observable<number>;
|
parameters: ko.Observable<number>;
|
||||||
|
@ -141,11 +143,18 @@ export class PublishNotebookPaneAdapter implements ReactAdapter {
|
||||||
this.isExecuting = true;
|
this.isExecuting = true;
|
||||||
this.triggerRender();
|
this.triggerRender();
|
||||||
|
|
||||||
|
let startKey: number;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!this.name || !this.description || !this.author) {
|
if (!this.name || !this.description || !this.author) {
|
||||||
throw new Error("Name, description, and author are required");
|
throw new Error("Name, description, and author are required");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startKey = traceStart(Action.NotebooksGalleryPublish, {
|
||||||
|
databaseAccountName: this.container.databaseAccount()?.name,
|
||||||
|
defaultExperience: this.container.defaultExperience(),
|
||||||
|
});
|
||||||
|
|
||||||
const response = await this.junoClient.publishNotebook(
|
const response = await this.junoClient.publishNotebook(
|
||||||
this.name,
|
this.name,
|
||||||
this.description,
|
this.description,
|
||||||
|
@ -158,7 +167,10 @@ export class PublishNotebookPaneAdapter implements ReactAdapter {
|
||||||
|
|
||||||
const data = response.data;
|
const data = response.data;
|
||||||
if (data) {
|
if (data) {
|
||||||
|
let isPublishPending = false;
|
||||||
|
|
||||||
if (data.pendingScanJobIds?.length > 0) {
|
if (data.pendingScanJobIds?.length > 0) {
|
||||||
|
isPublishPending = true;
|
||||||
NotificationConsoleUtils.logConsoleInfo(
|
NotificationConsoleUtils.logConsoleInfo(
|
||||||
`Content of ${this.name} is currently being scanned for illegal content. It will not be available in the public gallery until the review is complete (may take a few days).`
|
`Content of ${this.name} is currently being scanned for illegal content. It will not be available in the public gallery until the review is complete (may take a few days).`
|
||||||
);
|
);
|
||||||
|
@ -166,8 +178,30 @@ export class PublishNotebookPaneAdapter implements ReactAdapter {
|
||||||
NotificationConsoleUtils.logConsoleInfo(`Published ${this.name} to gallery`);
|
NotificationConsoleUtils.logConsoleInfo(`Published ${this.name} to gallery`);
|
||||||
this.container.openGallery(GalleryTab.Published);
|
this.container.openGallery(GalleryTab.Published);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
traceSuccess(
|
||||||
|
Action.NotebooksGalleryPublish,
|
||||||
|
{
|
||||||
|
databaseAccountName: this.container.databaseAccount()?.name,
|
||||||
|
defaultExperience: this.container.defaultExperience(),
|
||||||
|
notebookId: data.id,
|
||||||
|
isPublishPending,
|
||||||
|
},
|
||||||
|
startKey
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
traceFailure(
|
||||||
|
Action.NotebooksGalleryPublish,
|
||||||
|
{
|
||||||
|
databaseAccountName: this.container.databaseAccount()?.name,
|
||||||
|
defaultExperience: this.container.defaultExperience(),
|
||||||
|
error: getErrorMessage(error),
|
||||||
|
errorStack: getErrorStack(error),
|
||||||
|
},
|
||||||
|
startKey
|
||||||
|
);
|
||||||
|
|
||||||
const errorMessage = getErrorMessage(error);
|
const errorMessage = getErrorMessage(error);
|
||||||
this.formError = `Failed to publish ${this.name} to gallery`;
|
this.formError = `Failed to publish ${this.name} to gallery`;
|
||||||
this.formErrorDetail = `${errorMessage}`;
|
this.formErrorDetail = `${errorMessage}`;
|
||||||
|
|
|
@ -14,7 +14,7 @@ export interface PublishNotebookPaneProps {
|
||||||
notebookAuthor: string;
|
notebookAuthor: string;
|
||||||
notebookCreatedDate: string;
|
notebookCreatedDate: string;
|
||||||
notebookObject: ImmutableNotebook;
|
notebookObject: ImmutableNotebook;
|
||||||
notebookParentDomElement: HTMLElement;
|
notebookParentDomElement?: HTMLElement;
|
||||||
onChangeName: (newValue: string) => void;
|
onChangeName: (newValue: string) => void;
|
||||||
onChangeDescription: (newValue: string) => void;
|
onChangeDescription: (newValue: string) => void;
|
||||||
onChangeTags: (newValue: string) => void;
|
onChangeTags: (newValue: string) => void;
|
||||||
|
@ -110,7 +110,7 @@ export class PublishNotebookPaneComponent extends React.Component<PublishNoteboo
|
||||||
};
|
};
|
||||||
|
|
||||||
this.descriptionPara1 =
|
this.descriptionPara1 =
|
||||||
"This notebook has your data. Please make sure you delete any sensitive data/output before publishing.";
|
"When published, this notebook will appear in the Azure Cosmos DB notebooks public gallery. Make sure you have removed any sensitive data or output before publishing.";
|
||||||
|
|
||||||
this.descriptionPara2 = `Would you like to publish and share "${FileSystemUtil.stripExtension(
|
this.descriptionPara2 = `Would you like to publish and share "${FileSystemUtil.stripExtension(
|
||||||
this.props.notebookName,
|
this.props.notebookName,
|
||||||
|
@ -140,16 +140,20 @@ export class PublishNotebookPaneComponent extends React.Component<PublishNoteboo
|
||||||
this.props.onError(formError, formErrorDetail, area);
|
this.props.onError(formError, formErrorDetail, area);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const options: ImageTypes[] = [ImageTypes.Url, ImageTypes.CustomImage];
|
||||||
|
|
||||||
|
if (this.props.notebookParentDomElement) {
|
||||||
|
options.push(ImageTypes.TakeScreenshot);
|
||||||
|
if (this.props.notebookObject) {
|
||||||
|
options.push(ImageTypes.UseFirstDisplayOutput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.thumbnailSelectorProps = {
|
this.thumbnailSelectorProps = {
|
||||||
label: "Cover image",
|
label: "Cover image",
|
||||||
defaultSelectedKey: ImageTypes.Url,
|
defaultSelectedKey: ImageTypes.Url,
|
||||||
ariaLabel: "Cover image",
|
ariaLabel: "Cover image",
|
||||||
options: [
|
options: options.map((value: string) => ({ text: value, key: value })),
|
||||||
ImageTypes.Url,
|
|
||||||
ImageTypes.CustomImage,
|
|
||||||
ImageTypes.TakeScreenshot,
|
|
||||||
ImageTypes.UseFirstDisplayOutput,
|
|
||||||
].map((value: string) => ({ text: value, key: value })),
|
|
||||||
onChange: async (event, options) => {
|
onChange: async (event, options) => {
|
||||||
this.props.clearFormError();
|
this.props.clearFormError();
|
||||||
if (options.text === ImageTypes.TakeScreenshot) {
|
if (options.text === ImageTypes.TakeScreenshot) {
|
||||||
|
@ -301,9 +305,9 @@ export class PublishNotebookPaneComponent extends React.Component<PublishNoteboo
|
||||||
policyViolations: undefined,
|
policyViolations: undefined,
|
||||||
pendingScanJobIds: undefined,
|
pendingScanJobIds: undefined,
|
||||||
}}
|
}}
|
||||||
isFavorite={false}
|
isFavorite={undefined}
|
||||||
showDownload={true}
|
showDownload={false}
|
||||||
showDelete={true}
|
showDelete={false}
|
||||||
onClick={undefined}
|
onClick={undefined}
|
||||||
onTagClick={undefined}
|
onTagClick={undefined}
|
||||||
onFavoriteClick={undefined}
|
onFavoriteClick={undefined}
|
||||||
|
|
|
@ -14,7 +14,7 @@ exports[`PublishNotebookPaneComponent renders 1`] = `
|
||||||
>
|
>
|
||||||
<StackItem>
|
<StackItem>
|
||||||
<Text>
|
<Text>
|
||||||
This notebook has your data. Please make sure you delete any sensitive data/output before publishing.
|
When published, this notebook will appear in the Azure Cosmos DB notebooks public gallery. Make sure you have removed any sensitive data or output before publishing.
|
||||||
</Text>
|
</Text>
|
||||||
</StackItem>
|
</StackItem>
|
||||||
<StackItem>
|
<StackItem>
|
||||||
|
@ -65,14 +65,6 @@ exports[`PublishNotebookPaneComponent renders 1`] = `
|
||||||
"key": "Custom Image",
|
"key": "Custom Image",
|
||||||
"text": "Custom Image",
|
"text": "Custom Image",
|
||||||
},
|
},
|
||||||
Object {
|
|
||||||
"key": "Take Screenshot",
|
|
||||||
"text": "Take Screenshot",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"key": "Use First Display Output",
|
|
||||||
"text": "Use First Display Output",
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -112,9 +104,8 @@ exports[`PublishNotebookPaneComponent renders 1`] = `
|
||||||
"views": 0,
|
"views": 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isFavorite={false}
|
showDelete={false}
|
||||||
showDelete={true}
|
showDownload={false}
|
||||||
showDownload={true}
|
|
||||||
/>
|
/>
|
||||||
</StackItem>
|
</StackItem>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
|
@ -716,6 +716,19 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (item.type === NotebookContentItemType.Notebook) {
|
||||||
|
items.push({
|
||||||
|
label: "Publish to gallery",
|
||||||
|
iconSrc: undefined, // TODO
|
||||||
|
onClick: async () => {
|
||||||
|
const content = await this.container.readFile(item);
|
||||||
|
if (content) {
|
||||||
|
await this.container.publishNotebook(item.name, content);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// "Copy to ..." isn't needed if github locations are not available
|
// "Copy to ..." isn't needed if github locations are not available
|
||||||
if (!this.container.notebookManager?.gitHubOAuthService.isLoggedIn()) {
|
if (!this.container.notebookManager?.gitHubOAuthService.isLoggedIn()) {
|
||||||
items = items.filter((item) => item.label !== "Copy to ...");
|
items = items.filter((item) => item.label !== "Copy to ...");
|
||||||
|
|
|
@ -92,6 +92,23 @@ export enum Action {
|
||||||
SettingsV2Updated,
|
SettingsV2Updated,
|
||||||
SettingsV2Discarded,
|
SettingsV2Discarded,
|
||||||
MongoIndexUpdated,
|
MongoIndexUpdated,
|
||||||
|
NotebooksGalleryPublish,
|
||||||
|
NotebooksGalleryReportAbuse,
|
||||||
|
NotebooksGalleryClickReportAbuse,
|
||||||
|
NotebooksGalleryViewCodeOfConduct,
|
||||||
|
NotebooksGalleryAcceptCodeOfConduct,
|
||||||
|
NotebooksGalleryFavorite,
|
||||||
|
NotebooksGalleryUnfavorite,
|
||||||
|
NotebooksGalleryClickDelete,
|
||||||
|
NotebooksGalleryDelete,
|
||||||
|
NotebooksGalleryClickDownload,
|
||||||
|
NotebooksGalleryDownload,
|
||||||
|
NotebooksGalleryViewNotebook,
|
||||||
|
NotebooksGalleryViewGallery,
|
||||||
|
NotebooksGalleryViewOfficialSamples,
|
||||||
|
NotebooksGalleryViewPublicGallery,
|
||||||
|
NotebooksGalleryViewFavorites,
|
||||||
|
NotebooksGalleryViewPublishedNotebooks,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ActionModifiers = {
|
export const ActionModifiers = {
|
||||||
|
|
|
@ -9,8 +9,10 @@ import {
|
||||||
import Explorer from "../Explorer/Explorer";
|
import Explorer from "../Explorer/Explorer";
|
||||||
import { IChoiceGroupOption, IChoiceGroupProps, IProgressIndicatorProps } from "office-ui-fabric-react";
|
import { IChoiceGroupOption, IChoiceGroupProps, IProgressIndicatorProps } from "office-ui-fabric-react";
|
||||||
import { TextFieldProps } from "../Explorer/Controls/DialogReactComponent/DialogComponent";
|
import { TextFieldProps } from "../Explorer/Controls/DialogReactComponent/DialogComponent";
|
||||||
import { handleError } from "../Common/ErrorHandlingUtils";
|
import { getErrorMessage, getErrorStack, handleError } from "../Common/ErrorHandlingUtils";
|
||||||
import { HttpStatusCodes } from "../Common/Constants";
|
import { HttpStatusCodes } from "../Common/Constants";
|
||||||
|
import { trace, traceFailure, traceStart, traceSuccess } from "../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
|
||||||
|
|
||||||
const defaultSelectedAbuseCategory = "Other";
|
const defaultSelectedAbuseCategory = "Other";
|
||||||
const abuseCategories: IChoiceGroupOption[] = [
|
const abuseCategories: IChoiceGroupOption[] = [
|
||||||
|
@ -109,6 +111,8 @@ export function reportAbuse(
|
||||||
dialogHost: DialogHost,
|
dialogHost: DialogHost,
|
||||||
onComplete: (success: boolean) => void
|
onComplete: (success: boolean) => void
|
||||||
): void {
|
): void {
|
||||||
|
trace(Action.NotebooksGalleryClickReportAbuse, ActionModifiers.Mark, { notebookId: data.id });
|
||||||
|
|
||||||
const notebookId = data.id;
|
const notebookId = data.id;
|
||||||
let abuseCategory = defaultSelectedAbuseCategory;
|
let abuseCategory = defaultSelectedAbuseCategory;
|
||||||
let additionalDetails: string;
|
let additionalDetails: string;
|
||||||
|
@ -131,6 +135,8 @@ export function reportAbuse(
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const startKey = traceStart(Action.NotebooksGalleryReportAbuse, { notebookId: data.id });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await junoClient.reportAbuse(notebookId, abuseCategory, additionalDetails);
|
const response = await junoClient.reportAbuse(notebookId, abuseCategory, additionalDetails);
|
||||||
if (response.status !== HttpStatusCodes.Accepted) {
|
if (response.status !== HttpStatusCodes.Accepted) {
|
||||||
|
@ -147,8 +153,20 @@ export function reportAbuse(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
traceSuccess(Action.NotebooksGalleryReportAbuse, { notebookId: data.id, abuseCategory }, startKey);
|
||||||
|
|
||||||
onComplete(response.data);
|
onComplete(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
traceFailure(
|
||||||
|
Action.NotebooksGalleryReportAbuse,
|
||||||
|
{
|
||||||
|
notebookId: data.id,
|
||||||
|
error: getErrorMessage(error),
|
||||||
|
errorStack: getErrorStack(error),
|
||||||
|
},
|
||||||
|
startKey
|
||||||
|
);
|
||||||
|
|
||||||
handleError(
|
handleError(
|
||||||
error,
|
error,
|
||||||
"GalleryUtils/reportAbuse",
|
"GalleryUtils/reportAbuse",
|
||||||
|
@ -195,6 +213,12 @@ export function downloadItem(
|
||||||
data: IGalleryItem,
|
data: IGalleryItem,
|
||||||
onComplete: (item: IGalleryItem) => void
|
onComplete: (item: IGalleryItem) => void
|
||||||
): void {
|
): void {
|
||||||
|
trace(Action.NotebooksGalleryClickDownload, ActionModifiers.Mark, {
|
||||||
|
notebookId: data.id,
|
||||||
|
downloadCount: data.downloads,
|
||||||
|
isSample: data.isSample,
|
||||||
|
});
|
||||||
|
|
||||||
const name = data.name;
|
const name = data.name;
|
||||||
container.showOkCancelModalDialog(
|
container.showOkCancelModalDialog(
|
||||||
"Download to My Notebooks",
|
"Download to My Notebooks",
|
||||||
|
@ -206,6 +230,11 @@ export function downloadItem(
|
||||||
`Downloading ${name} to My Notebooks`
|
`Downloading ${name} to My Notebooks`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const startKey = traceStart(Action.NotebooksGalleryDownload, {
|
||||||
|
notebookId: data.id,
|
||||||
|
downloadCount: data.downloads,
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await junoClient.getNotebookContent(data.id);
|
const response = await junoClient.getNotebookContent(data.id);
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
|
@ -220,9 +249,25 @@ export function downloadItem(
|
||||||
|
|
||||||
const increaseDownloadResponse = await junoClient.increaseNotebookDownloadCount(data.id);
|
const increaseDownloadResponse = await junoClient.increaseNotebookDownloadCount(data.id);
|
||||||
if (increaseDownloadResponse.data) {
|
if (increaseDownloadResponse.data) {
|
||||||
|
traceSuccess(
|
||||||
|
Action.NotebooksGalleryDownload,
|
||||||
|
{ notebookId: data.id, downloadCount: increaseDownloadResponse.data.downloads },
|
||||||
|
startKey
|
||||||
|
);
|
||||||
onComplete(increaseDownloadResponse.data);
|
onComplete(increaseDownloadResponse.data);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
traceFailure(
|
||||||
|
Action.NotebooksGalleryDownload,
|
||||||
|
{
|
||||||
|
notebookId: data.id,
|
||||||
|
downloadCount: data.downloads,
|
||||||
|
error: getErrorMessage(error),
|
||||||
|
errorStack: getErrorStack(error),
|
||||||
|
},
|
||||||
|
startKey
|
||||||
|
);
|
||||||
|
|
||||||
handleError(error, "GalleryUtils/downloadItem", `Failed to download ${data.name}`);
|
handleError(error, "GalleryUtils/downloadItem", `Failed to download ${data.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,14 +285,36 @@ export async function favoriteItem(
|
||||||
onComplete: (item: IGalleryItem) => void
|
onComplete: (item: IGalleryItem) => void
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (container) {
|
if (container) {
|
||||||
|
const startKey = traceStart(Action.NotebooksGalleryFavorite, {
|
||||||
|
notebookId: data.id,
|
||||||
|
favoriteCount: data.favorites,
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await junoClient.favoriteNotebook(data.id);
|
const response = await junoClient.favoriteNotebook(data.id);
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
throw new Error(`Received HTTP ${response.status} when favoriting ${data.name}`);
|
throw new Error(`Received HTTP ${response.status} when favoriting ${data.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
traceSuccess(
|
||||||
|
Action.NotebooksGalleryFavorite,
|
||||||
|
{ notebookId: data.id, favoriteCount: response.data.favorites },
|
||||||
|
startKey
|
||||||
|
);
|
||||||
|
|
||||||
onComplete(response.data);
|
onComplete(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
traceFailure(
|
||||||
|
Action.NotebooksGalleryFavorite,
|
||||||
|
{
|
||||||
|
notebookId: data.id,
|
||||||
|
favoriteCount: data.favorites,
|
||||||
|
error: getErrorMessage(error),
|
||||||
|
errorStack: getErrorStack(error),
|
||||||
|
},
|
||||||
|
startKey
|
||||||
|
);
|
||||||
|
|
||||||
handleError(error, "GalleryUtils/favoriteItem", `Failed to favorite ${data.name}`);
|
handleError(error, "GalleryUtils/favoriteItem", `Failed to favorite ${data.name}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -260,14 +327,36 @@ export async function unfavoriteItem(
|
||||||
onComplete: (item: IGalleryItem) => void
|
onComplete: (item: IGalleryItem) => void
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (container) {
|
if (container) {
|
||||||
|
const startKey = traceStart(Action.NotebooksGalleryUnfavorite, {
|
||||||
|
notebookId: data.id,
|
||||||
|
favoriteCount: data.favorites,
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await junoClient.unfavoriteNotebook(data.id);
|
const response = await junoClient.unfavoriteNotebook(data.id);
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
throw new Error(`Received HTTP ${response.status} when unfavoriting ${data.name}`);
|
throw new Error(`Received HTTP ${response.status} when unfavoriting ${data.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
traceSuccess(
|
||||||
|
Action.NotebooksGalleryUnfavorite,
|
||||||
|
{ notebookId: data.id, favoriteCount: response.data.favorites },
|
||||||
|
startKey
|
||||||
|
);
|
||||||
|
|
||||||
onComplete(response.data);
|
onComplete(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
traceFailure(
|
||||||
|
Action.NotebooksGalleryUnfavorite,
|
||||||
|
{
|
||||||
|
notebookId: data.id,
|
||||||
|
favoriteCount: data.favorites,
|
||||||
|
error: getErrorMessage(error),
|
||||||
|
errorStack: getErrorStack(error),
|
||||||
|
},
|
||||||
|
startKey
|
||||||
|
);
|
||||||
|
|
||||||
handleError(error, "GalleryUtils/unfavoriteItem", `Failed to unfavorite ${data.name}`);
|
handleError(error, "GalleryUtils/unfavoriteItem", `Failed to unfavorite ${data.name}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -280,6 +369,8 @@ export function deleteItem(
|
||||||
onComplete: (item: IGalleryItem) => void
|
onComplete: (item: IGalleryItem) => void
|
||||||
): void {
|
): void {
|
||||||
if (container) {
|
if (container) {
|
||||||
|
trace(Action.NotebooksGalleryClickDelete, ActionModifiers.Mark, { notebookId: data.id });
|
||||||
|
|
||||||
container.showOkCancelModalDialog(
|
container.showOkCancelModalDialog(
|
||||||
"Remove published notebook",
|
"Remove published notebook",
|
||||||
`Would you like to remove ${data.name} from the gallery?`,
|
`Would you like to remove ${data.name} from the gallery?`,
|
||||||
|
@ -291,15 +382,25 @@ export function deleteItem(
|
||||||
`Removing ${name} from gallery`
|
`Removing ${name} from gallery`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const startKey = traceStart(Action.NotebooksGalleryDelete, { notebookId: data.id });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await junoClient.deleteNotebook(data.id);
|
const response = await junoClient.deleteNotebook(data.id);
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
throw new Error(`Received HTTP ${response.status} while removing ${name}`);
|
throw new Error(`Received HTTP ${response.status} while removing ${name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
traceSuccess(Action.NotebooksGalleryDelete, { notebookId: data.id }, startKey);
|
||||||
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Info, `Successfully removed ${name} from gallery`);
|
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Info, `Successfully removed ${name} from gallery`);
|
||||||
onComplete(response.data);
|
onComplete(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
traceFailure(
|
||||||
|
Action.NotebooksGalleryDelete,
|
||||||
|
{ notebookId: data.id, error: getErrorMessage(error), errorStack: getErrorStack(error) },
|
||||||
|
startKey
|
||||||
|
);
|
||||||
|
|
||||||
handleError(error, "GalleryUtils/deleteItem", `Failed to remove ${name} from gallery`);
|
handleError(error, "GalleryUtils/deleteItem", `Failed to remove ${name} from gallery`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue