Support pane - WIP

This commit is contained in:
Vignesh Rangaishenvi 2020-10-13 23:37:05 -07:00
parent 4fe2098730
commit b1862bb566
11 changed files with 4333 additions and 555 deletions

4683
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -42,6 +42,7 @@
"applicationinsights": "1.8.0",
"babel-polyfill": "6.26.0",
"bootstrap": "3.4.1",
"botframework-webchat": "4.10.1",
"canvas": "2.6.1",
"clean-webpack-plugin": "0.1.19",
"copy-webpack-plugin": "6.0.2",

View File

@ -78,3 +78,4 @@ ko.components.register("upload-file-pane", new PaneComponents.UploadFilePaneComp
ko.components.register("string-input-pane", new PaneComponents.StringInputPaneComponent());
ko.components.register("setup-notebooks-pane", new PaneComponents.SetupNotebooksPaneComponent());
ko.components.register("github-repos-pane", new PaneComponents.GitHubReposPaneComponent());
ko.components.register("support-pane", new PaneComponents.SupportPaneComponent());

View File

@ -0,0 +1,24 @@
import * as _ from "underscore";
import React from "react";
import ReactWebChat, { createDirectLine } from "botframework-webchat";
export interface SupportPaneComponentProps {
directLineToken: string;
}
export class SupportPaneComponent extends React.Component<SupportPaneComponentProps> {
private readonly userId: string = _.uniqueId();
constructor(props: SupportPaneComponentProps) {
super(props);
}
public render(): JSX.Element {
const styleOptions = {
bubbleBackground: "rgba(0, 0, 255, .1)",
bubbleFromUserBackground: "rgba(0, 255, 0, .1)"
};
const directLine = createDirectLine({ token: this.props.directLineToken });
return <ReactWebChat directLine={directLine} userID={this.userId} styleOptions={styleOptions} />;
}
}

View File

@ -0,0 +1,31 @@
import * as ko from "knockout";
import * as React from "react";
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
import Explorer from "../../Explorer";
import { SupportPaneComponent } from "./SupportPaneComponent";
export interface SupportPaneComponentParams {
directLineAccessToken: string;
}
export class SupportPaneComponentAdapter implements ReactAdapter {
public parameters: ko.Observable<SupportPaneComponentParams>;
constructor(private container: Explorer) {
this.parameters = ko.observable<SupportPaneComponentParams>({
directLineAccessToken: this.container.conversationToken()
});
this.container.conversationToken.subscribe(accessToken => {
this.parameters().directLineAccessToken = accessToken;
this.forceRender();
});
}
public renderComponent(): JSX.Element {
return <SupportPaneComponent directLineToken={this.parameters().directLineAccessToken} />;
}
public forceRender(): void {
this.parameters.valueHasMutated();
}
}

View File

@ -69,6 +69,7 @@ import { RouteHandler } from "../RouteHandlers/RouteHandler";
import { SaveQueryPane } from "./Panes/SaveQueryPane";
import { SettingsPane } from "./Panes/SettingsPane";
import { SetupNotebooksPane } from "./Panes/SetupNotebooksPane";
import { SupportPane } from "./Panes/SupportPane";
import { SplashScreenComponentAdapter } from "./SplashScreen/SplashScreenComponentApdapter";
import { Splitter, SplitterBounds, SplitterDirection } from "../Common/Splitter";
import { StringInputPane } from "./Panes/StringInputPane";
@ -175,6 +176,7 @@ export default class Explorer {
public isAuthWithResourceToken: ko.Observable<boolean>;
public isResourceTokenCollectionNodeSelected: ko.Computed<boolean>;
private resourceTreeForResourceToken: ResourceTreeAdapterForResourceToken;
public conversationToken: ko.Observable<string>;
// Tabs
public isTabsContentExpanded: ko.Observable<boolean>;
@ -195,6 +197,7 @@ export default class Explorer {
public newVertexPane: NewVertexPane;
public cassandraAddCollectionPane: CassandraAddCollectionPane;
public settingsPane: SettingsPane;
public supportPane: SupportPane;
public executeSprocParamsPane: ExecuteSprocParamsPane;
public renewAdHocAccessPane: RenewAdHocAccessPane;
public uploadItemsPane: UploadItemsPane;
@ -325,10 +328,12 @@ export default class Explorer {
this.hasStorageAnalyticsAfecFeature = ko.observable(false);
this.hasStorageAnalyticsAfecFeature.subscribe((enabled: boolean) => this.refreshCommandBarButtons());
this.isSynapseLinkUpdating = ko.observable<boolean>(false);
this.conversationToken = ko.observable<string>();
this.isAccountReady.subscribe(async (isAccountReady: boolean) => {
if (isAccountReady) {
this.isAuthWithResourceToken() ? this.refreshDatabaseForResourceToken() : this.refreshAllDatabases(true);
RouteHandler.getInstance().initHandler();
this.generateConversationToken();
this.notebookWorkspaceManager = new NotebookWorkspaceManager(this.armEndpoint());
this.arcadiaWorkspaces = ko.observableArray();
this._arcadiaManager = new ArcadiaResourceManager(this.armEndpoint());
@ -702,6 +707,12 @@ export default class Explorer {
container: this
});
this.supportPane = new SupportPane({
id: "supportpane",
visible: ko.observable<boolean>(false),
container: this
});
this.executeSprocParamsPane = new ExecuteSprocParamsPane({
id: "executesprocparamspane",
visible: ko.observable<boolean>(false),
@ -1592,6 +1603,53 @@ export default class Explorer {
});
}
private async generateConversationToken() {
const response = await fetch("https://directline.botframework.com/v3/directline/tokens/generate", {
method: "POST",
headers: {
[Constants.HttpHeaders.authorization]: "Bearer BSjLmJJHZRA.PxahjJGCNOKl7q9tiodWyVcqJOIzG894vAAqCme639o",
Accept: "application/json",
[Constants.HttpHeaders.contentType]: "application/json"
},
body: JSON.stringify({
"user": {
"id": `dl_${_.uniqueId()}`,
"name": this.getUserName()
}
})
});
if (!response.ok) {
throw new Error(await response.json());
}
const tokenResponse: { conversationId: string; token: string; expires_in: number } = await response.json();
this.conversationToken(tokenResponse?.token);
if (tokenResponse?.expires_in) {
setTimeout(() => this.generateConversationToken(), (tokenResponse?.expires_in - 1000) * 1000);
}
}
private getUserName() {
const accessToken = userContext?.authorizationToken;
if (!accessToken) {
return "Cosmos DB User";
}
let name;
try {
const tokenPayload = decryptJWTToken(accessToken);
if (tokenPayload && tokenPayload.hasOwnProperty("name")) {
name = tokenPayload.name;
}
} catch (error) {
// ignore
} finally {
return name;
}
}
private async _getArcadiaWorkspaces(): Promise<ArcadiaWorkspaceItem[]> {
try {
const workspaces = await this._arcadiaManager.listWorkspacesAsync([userContext.subscriptionId]);

View File

@ -27,6 +27,7 @@ import SynapseIcon from "../../../../images/synapse-link.svg";
import { configContext, Platform } from "../../../ConfigContext";
import Explorer from "../../Explorer";
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
import { AuthType } from "../../../AuthType";
export class CommandBarComponentButtonFactory {
private static counter: number = 0;
@ -178,6 +179,21 @@ export class CommandBarComponentButtonFactory {
buttons.push(settingsPaneButton);
}
if (window.authType === AuthType.AAD) {
const label = "Support";
const supportPaneButton: CommandButtonComponentProps = {
iconSrc: FeedbackIcon,
iconAlt: label,
onCommandClick: () => container.supportPane.open(),
commandButtonLabel: null,
ariaLabel: label,
tooltipText: label,
hasPopup: true,
disabled: false
};
buttons.push(supportPaneButton);
}
if (container.isHostedDataExplorerEnabled()) {
const label = "Open Full Screen";
const fullScreenButton: CommandButtonComponentProps = {

View File

@ -20,6 +20,7 @@ import UploadFilePaneTemplate from "./UploadFilePane.html";
import StringInputPaneTemplate from "./StringInputPane.html";
import SetupNotebooksPaneTemplate from "./SetupNotebooksPane.html";
import GitHubReposPaneTemplate from "./GitHubReposPane.html";
import SupportPaneTemplate from "./SupportPane.html";
export class PaneComponent {
constructor(data: any) {
@ -224,3 +225,12 @@ export class GitHubReposPaneComponent {
};
}
}
export class SupportPaneComponent {
constructor() {
return {
viewModel: PaneComponent,
template: SupportPaneTemplate
};
}
}

View File

@ -0,0 +1,35 @@
<div data-bind="visible: visible, event: { keydown: onPaneKeyDown }">
<div class="contextual-pane-out" data-bind="click: cancel, clickBubble: false"></div>
<div class="contextual-pane" id="supportpane">
<!-- Save Query form -- Start -->
<div class="contextual-pane-in">
<div class="paneContentContainer">
<!-- Save Query header - Start -->
<div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span>
<div
class="closeImg"
role="button"
aria-label="Close pane"
tabindex="0"
data-bind="click: cancel, event: { keypress: onCloseKeyPress }"
>
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
</div>
</div>
<!-- Save Query header - End -->
<!-- Save Query inputs - Start -->
<div class="paneMainContent">
<div class="pkPadding" style="height: 100%;" data-bind="react: supportPaneComponentAdapter"></div>
</div>
</div>
</div>
<!-- Save Query form - Start -->
<!-- Loader - Start -->
<div class="dataExplorerLoaderContainer dataExplorerPaneLoaderContainer" data-bind="visible: isExecuting">
<img class="dataExplorerLoader" src="/LoadingIndicator_3Squares.gif" />
</div>
<!-- Loader - End -->
</div>
</div>

View File

@ -0,0 +1,28 @@
import * as ViewModels from "../../Contracts/ViewModels";
import { ContextualPaneBase } from "./ContextualPaneBase";
import { SupportPaneComponentAdapter } from "../Controls/SupportPaneComponent/SupportPaneComponentAdapter";
export class SupportPane extends ContextualPaneBase {
public supportPaneComponentAdapter: SupportPaneComponentAdapter;
constructor(options: ViewModels.PaneOptions) {
super(options);
this.title("Cosmos DB Support");
this.resetData();
this.supportPaneComponentAdapter = new SupportPaneComponentAdapter(this.container);
}
public open() {
super.open();
this.supportPaneComponentAdapter.forceRender();
}
public close() {
super.close();
this.supportPaneComponentAdapter.forceRender();
}
public submit() {
// override default behavior because this is not a form
}
}

View File

@ -289,6 +289,7 @@
<upload-file-pane params="{data: uploadFilePane}"></upload-file-pane>
<string-input-pane params="{data: stringInputPane}"></string-input-pane>
<setup-notebooks-pane params="{data: setupNotebooksPane}"></setup-notebooks-pane>
<support-pane params="{data: supportPane}"></support-pane>
<!-- ko if: isGitHubPaneEnabled -->
<github-repos-pane params="{data: gitHubReposPane}"></github-repos-pane>