Add connect tab for new quick start (#1273)
* Add connect tab * Error handling * Add button to open quick start blade * Handle scenario where user don't have write access
This commit is contained in:
parent
dc83bf6fa0
commit
2ab60a7a40
|
@ -34,6 +34,7 @@ export enum MessageTypes {
|
||||||
CreateSparkPool,
|
CreateSparkPool,
|
||||||
RefreshDatabaseAccount,
|
RefreshDatabaseAccount,
|
||||||
CloseTab,
|
CloseTab,
|
||||||
|
OpenQuickstartBlade,
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Versions, ActionContracts, Diagnostics };
|
export { Versions, ActionContracts, Diagnostics };
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* Accordion top class
|
* Accordion top class
|
||||||
*/
|
*/
|
||||||
import { Coachmark, DirectionalHint, Image, Link, Stack, TeachingBubbleContent, Text } from "@fluentui/react";
|
import { Coachmark, DirectionalHint, Image, Link, Stack, TeachingBubbleContent, Text } from "@fluentui/react";
|
||||||
|
import { useTabs } from "hooks/useTabs";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import AddDatabaseIcon from "../../../images/AddDatabase.svg";
|
import AddDatabaseIcon from "../../../images/AddDatabase.svg";
|
||||||
import NewQueryIcon from "../../../images/AddSqlQuery_16x16.svg";
|
import NewQueryIcon from "../../../images/AddSqlQuery_16x16.svg";
|
||||||
|
@ -237,8 +238,7 @@ export class SplashScreen extends React.Component<SplashScreenProps, SplashScree
|
||||||
iconSrc: ConnectIcon,
|
iconSrc: ConnectIcon,
|
||||||
title: "Connect",
|
title: "Connect",
|
||||||
description: "Prefer using your own choice of tooling? Find the connection string you need to connect",
|
description: "Prefer using your own choice of tooling? Find the connection string you need to connect",
|
||||||
// TODO: replace onClick function
|
onClick: () => useTabs.getState().openAndActivateConnectTab(),
|
||||||
onClick: () => 2,
|
|
||||||
};
|
};
|
||||||
heroes.push(connectBtn);
|
heroes.push(connectBtn);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,232 @@
|
||||||
|
import {
|
||||||
|
IconButton,
|
||||||
|
ITextFieldStyles,
|
||||||
|
Link,
|
||||||
|
Pivot,
|
||||||
|
PivotItem,
|
||||||
|
PrimaryButton,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
TextField,
|
||||||
|
} from "@fluentui/react";
|
||||||
|
import { handleError } from "Common/ErrorHandlingUtils";
|
||||||
|
import { sendMessage } from "Common/MessageHandler";
|
||||||
|
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { userContext } from "UserContext";
|
||||||
|
import { listKeys, listReadOnlyKeys } from "Utils/arm/generatedClients/cosmos/databaseAccounts";
|
||||||
|
import {
|
||||||
|
DatabaseAccountListKeysResult,
|
||||||
|
DatabaseAccountListReadOnlyKeysResult,
|
||||||
|
} from "Utils/arm/generatedClients/cosmos/types";
|
||||||
|
|
||||||
|
export const ConnectTab: React.FC = (): JSX.Element => {
|
||||||
|
const [primaryMasterKey, setPrimaryMasterKey] = useState<string>("");
|
||||||
|
const [secondaryMasterKey, setSecondaryMasterKey] = useState<string>("");
|
||||||
|
const [primaryReadonlyMasterKey, setPrimaryReadonlyMasterKey] = useState<string>("");
|
||||||
|
const [secondaryReadonlyMasterKey, setSecondaryReadonlyMasterKey] = useState<string>("");
|
||||||
|
const uri: string = userContext.databaseAccount.properties?.documentEndpoint;
|
||||||
|
const primaryConnectionStr = `AccountEndpoint=${uri};AccountKey=${primaryMasterKey}`;
|
||||||
|
const secondaryConnectionStr = `AccountEndpoint=${uri};AccountKey=${secondaryMasterKey}`;
|
||||||
|
const primaryReadonlyConnectionStr = `AccountEndpoint=${uri};AccountKey=${primaryReadonlyMasterKey}`;
|
||||||
|
const secondaryReadonlyConnectionStr = `AccountEndpoint=${uri};AccountKey=${secondaryReadonlyMasterKey}`;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchKeys();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const fetchKeys = async (): Promise<void> => {
|
||||||
|
try {
|
||||||
|
if (userContext.hasWriteAccess) {
|
||||||
|
const listKeysResult: DatabaseAccountListKeysResult = await listKeys(
|
||||||
|
userContext.subscriptionId,
|
||||||
|
userContext.resourceGroup,
|
||||||
|
userContext.databaseAccount.name
|
||||||
|
);
|
||||||
|
setPrimaryMasterKey(listKeysResult.primaryMasterKey);
|
||||||
|
setSecondaryMasterKey(listKeysResult.secondaryMasterKey);
|
||||||
|
setPrimaryReadonlyMasterKey(listKeysResult.primaryReadonlyMasterKey);
|
||||||
|
setSecondaryReadonlyMasterKey(listKeysResult.secondaryReadonlyMasterKey);
|
||||||
|
} else {
|
||||||
|
const listReadonlyKeysResult: DatabaseAccountListReadOnlyKeysResult = await listReadOnlyKeys(
|
||||||
|
userContext.subscriptionId,
|
||||||
|
userContext.resourceGroup,
|
||||||
|
userContext.databaseAccount.name
|
||||||
|
);
|
||||||
|
setPrimaryReadonlyMasterKey(listReadonlyKeysResult.primaryReadonlyMasterKey);
|
||||||
|
setSecondaryReadonlyMasterKey(listReadonlyKeysResult.secondaryReadonlyMasterKey);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, "listKeys", "listKeys request has failed: ");
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCopyBtnClicked = (selector: string): void => {
|
||||||
|
const textfield: HTMLInputElement = document.querySelector(selector);
|
||||||
|
textfield.select();
|
||||||
|
document.execCommand("copy");
|
||||||
|
};
|
||||||
|
|
||||||
|
const textfieldStyles: Partial<ITextFieldStyles> = {
|
||||||
|
root: { width: "100%" },
|
||||||
|
field: { backgroundColor: "rgb(230, 230, 230)" },
|
||||||
|
fieldGroup: { borderColor: "rgb(138, 136, 134)" },
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ width: "100%", padding: 16 }}>
|
||||||
|
<Stack style={{ marginLeft: 10 }}>
|
||||||
|
<Text variant="medium">
|
||||||
|
Ensure you have the right networking / access configuration before you establish the connection with your app
|
||||||
|
or 3rd party tool.
|
||||||
|
</Text>
|
||||||
|
<Link style={{ fontSize: 14 }} target="_blank" href="">
|
||||||
|
Configure networking in Azure portal
|
||||||
|
</Link>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Pivot>
|
||||||
|
{userContext.hasWriteAccess && (
|
||||||
|
<PivotItem headerText="Read-write Keys">
|
||||||
|
<Stack style={{ margin: 10 }}>
|
||||||
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
||||||
|
<TextField label="URI" id="uriTextfield" readOnly value={uri} styles={textfieldStyles} />
|
||||||
|
<IconButton iconProps={{ iconName: "Copy" }} onClick={() => onCopyBtnClicked("#uriTextfield")} />
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
||||||
|
<TextField
|
||||||
|
label="PRIMARY KEY"
|
||||||
|
id="primaryKeyTextfield"
|
||||||
|
readOnly
|
||||||
|
value={primaryMasterKey}
|
||||||
|
styles={textfieldStyles}
|
||||||
|
/>
|
||||||
|
<IconButton iconProps={{ iconName: "Copy" }} onClick={() => onCopyBtnClicked("#primaryKeyTextfield")} />
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
||||||
|
<TextField
|
||||||
|
label="SECONDARY KEY"
|
||||||
|
id="secondaryKeyTextfield"
|
||||||
|
readOnly
|
||||||
|
value={secondaryMasterKey}
|
||||||
|
styles={textfieldStyles}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
iconProps={{ iconName: "Copy" }}
|
||||||
|
onClick={() => onCopyBtnClicked("#secondaryKeyTextfield")}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
||||||
|
<TextField
|
||||||
|
label="PRIMARY CONNECTION STRING"
|
||||||
|
id="primaryConStrTextfield"
|
||||||
|
readOnly
|
||||||
|
value={primaryConnectionStr}
|
||||||
|
styles={textfieldStyles}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
iconProps={{ iconName: "Copy" }}
|
||||||
|
onClick={() => onCopyBtnClicked("#primaryConStrTextfield")}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
||||||
|
<TextField
|
||||||
|
label="SECONDARY CONNECTION STRING"
|
||||||
|
id="secondaryConStrTextfield"
|
||||||
|
readOnly
|
||||||
|
value={secondaryConnectionStr}
|
||||||
|
styles={textfieldStyles}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
iconProps={{ iconName: "Copy" }}
|
||||||
|
onClick={() => onCopyBtnClicked("#secondaryConStrTextfield")}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</PivotItem>
|
||||||
|
)}
|
||||||
|
<PivotItem headerText="Read-only Keys">
|
||||||
|
<Stack style={{ margin: 10 }}>
|
||||||
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
||||||
|
<TextField label="URI" id="uriReadOnlyTextfield" readOnly value={uri} styles={textfieldStyles} />
|
||||||
|
<IconButton iconProps={{ iconName: "Copy" }} onClick={() => onCopyBtnClicked("#uriReadOnlyTextfield")} />
|
||||||
|
</Stack>
|
||||||
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
||||||
|
<TextField
|
||||||
|
label="PRIMARY READ-ONLY KEY"
|
||||||
|
id="primaryReadonlyKeyTextfield"
|
||||||
|
readOnly
|
||||||
|
value={primaryReadonlyMasterKey}
|
||||||
|
styles={textfieldStyles}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
iconProps={{ iconName: "Copy" }}
|
||||||
|
onClick={() => onCopyBtnClicked("#primaryReadonlyKeyTextfield")}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
||||||
|
<TextField
|
||||||
|
label="SECONDARY READ-ONLY KEY"
|
||||||
|
id="secondaryReadonlyKeyTextfield"
|
||||||
|
readOnly
|
||||||
|
value={secondaryReadonlyMasterKey}
|
||||||
|
styles={textfieldStyles}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
iconProps={{ iconName: "Copy" }}
|
||||||
|
onClick={() => onCopyBtnClicked("#secondaryReadonlyKeyTextfield")}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
||||||
|
<TextField
|
||||||
|
label="PRIMARY READ-ONLY CONNECTION STRING"
|
||||||
|
id="primaryReadonlyConStrTextfield"
|
||||||
|
readOnly
|
||||||
|
value={primaryReadonlyConnectionStr}
|
||||||
|
styles={textfieldStyles}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
iconProps={{ iconName: "Copy" }}
|
||||||
|
onClick={() => onCopyBtnClicked("#primaryReadonlyConStrTextfield")}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
||||||
|
<TextField
|
||||||
|
label="SECONDARY READ-ONLY CONNECTION STRING"
|
||||||
|
id="secondaryReadonlyConStrTextfield"
|
||||||
|
value={secondaryReadonlyConnectionStr}
|
||||||
|
readOnly
|
||||||
|
styles={textfieldStyles}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
iconProps={{ iconName: "Copy" }}
|
||||||
|
onClick={() => onCopyBtnClicked("#secondaryReadonlyConStrTextfield")}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</PivotItem>
|
||||||
|
</Pivot>
|
||||||
|
|
||||||
|
<Stack style={{ margin: 10 }}>
|
||||||
|
<Text style={{ fontWeight: 600, marginBottom: 8 }}>Download sample app</Text>
|
||||||
|
<Text style={{ marginBottom: 8 }}>
|
||||||
|
Don’t have an app ready? No worries, download one of our sample app with a platform of your choice. Connection
|
||||||
|
string is already included in the app.
|
||||||
|
</Text>
|
||||||
|
<PrimaryButton
|
||||||
|
style={{ width: 185 }}
|
||||||
|
onClick={() =>
|
||||||
|
sendMessage({
|
||||||
|
type: MessageTypes.OpenQuickstartBlade,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
text="Download sample app"
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,4 +1,5 @@
|
||||||
import { CollectionTabKind } from "Contracts/ViewModels";
|
import { CollectionTabKind } from "Contracts/ViewModels";
|
||||||
|
import { ConnectTab } from "Explorer/Tabs/ConnectTab";
|
||||||
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
||||||
import ko from "knockout";
|
import ko from "knockout";
|
||||||
import React, { MutableRefObject, useEffect, useRef, useState } from "react";
|
import React, { MutableRefObject, useEffect, useRef, useState } from "react";
|
||||||
|
@ -12,17 +13,21 @@ type Tab = TabsBase | (TabsBase & { render: () => JSX.Element });
|
||||||
|
|
||||||
export const Tabs = (): JSX.Element => {
|
export const Tabs = (): JSX.Element => {
|
||||||
const { openedTabs, activeTab } = useTabs();
|
const { openedTabs, activeTab } = useTabs();
|
||||||
|
const isConnectTabOpen = useTabs((state) => state.isConnectTabOpen);
|
||||||
|
const isConnectTabActive = useTabs((state) => state.isConnectTabActive);
|
||||||
return (
|
return (
|
||||||
<div className="tabsManagerContainer">
|
<div className="tabsManagerContainer">
|
||||||
<div id="content" className="flexContainer hideOverflows">
|
<div id="content" className="flexContainer hideOverflows">
|
||||||
<div className="nav-tabs-margin">
|
<div className="nav-tabs-margin">
|
||||||
<ul className="nav nav-tabs level navTabHeight" id="navTabs" role="tablist">
|
<ul className="nav nav-tabs level navTabHeight" id="navTabs" role="tablist">
|
||||||
|
{isConnectTabOpen && <TabNav key="connect" tab={undefined} active={isConnectTabActive} />}
|
||||||
{openedTabs.map((tab) => (
|
{openedTabs.map((tab) => (
|
||||||
<TabNav key={tab.tabId} tab={tab} active={activeTab === tab} />
|
<TabNav key={tab.tabId} tab={tab} active={activeTab === tab} />
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div className="tabPanesContainer">
|
<div className="tabPanesContainer">
|
||||||
|
{isConnectTabActive && <ConnectTab />}
|
||||||
{openedTabs.map((tab) => (
|
{openedTabs.map((tab) => (
|
||||||
<TabPane key={tab.tabId} tab={tab} active={activeTab === tab} />
|
<TabPane key={tab.tabId} tab={tab} active={activeTab === tab} />
|
||||||
))}
|
))}
|
||||||
|
@ -35,6 +40,7 @@ export const Tabs = (): JSX.Element => {
|
||||||
function TabNav({ tab, active }: { tab: Tab; active: boolean }) {
|
function TabNav({ tab, active }: { tab: Tab; active: boolean }) {
|
||||||
const [hovering, setHovering] = useState(false);
|
const [hovering, setHovering] = useState(false);
|
||||||
const focusTab = useRef<HTMLLIElement>() as MutableRefObject<HTMLLIElement>;
|
const focusTab = useRef<HTMLLIElement>() as MutableRefObject<HTMLLIElement>;
|
||||||
|
const tabId = tab ? tab.tabId : "connect";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (active && focusTab.current) {
|
if (active && focusTab.current) {
|
||||||
|
@ -45,27 +51,27 @@ function TabNav({ tab, active }: { tab: Tab; active: boolean }) {
|
||||||
<li
|
<li
|
||||||
onMouseOver={() => setHovering(true)}
|
onMouseOver={() => setHovering(true)}
|
||||||
onMouseLeave={() => setHovering(false)}
|
onMouseLeave={() => setHovering(false)}
|
||||||
onClick={() => tab.onTabClick()}
|
onClick={() => (tab ? tab.onTabClick() : useTabs.getState().activateConnectTab())}
|
||||||
onKeyPress={({ nativeEvent: e }) => tab.onKeyPressActivate(undefined, e)}
|
onKeyPress={({ nativeEvent: e }) => (tab ? tab.onKeyPressActivate(undefined, e) : onKeyPressConnectTab(e))}
|
||||||
className={active ? "active tabList" : "tabList"}
|
className={active ? "active tabList" : "tabList"}
|
||||||
title={useObservable(tab.tabPath)}
|
title={useObservable(tab?.tabPath || ko.observable(""))}
|
||||||
aria-selected={active}
|
aria-selected={active}
|
||||||
aria-expanded={active}
|
aria-expanded={active}
|
||||||
aria-controls={tab.tabId}
|
aria-controls={tabId}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
role="tab"
|
role="tab"
|
||||||
ref={focusTab}
|
ref={focusTab}
|
||||||
>
|
>
|
||||||
<span className="tabNavContentContainer">
|
<span className="tabNavContentContainer">
|
||||||
<a data-toggle="tab" href={"#" + tab.tabId} tabIndex={-1}>
|
<a data-toggle="tab" href={"#" + tabId} tabIndex={-1}>
|
||||||
<div className="tab_Content">
|
<div className="tab_Content">
|
||||||
<span className="statusIconContainer">
|
<span className="statusIconContainer">
|
||||||
{useObservable(tab.isExecutionError) && <ErrorIcon tab={tab} active={active} />}
|
{useObservable(tab?.isExecutionError || ko.observable(false)) && <ErrorIcon tab={tab} active={active} />}
|
||||||
{useObservable(tab.isExecuting) && (
|
{useObservable(tab?.isExecuting || ko.observable(false)) && (
|
||||||
<img className="loadingIcon" title="Loading" src={loadingIcon} alt="Loading" />
|
<img className="loadingIcon" title="Loading" src={loadingIcon} alt="Loading" />
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span className="tabNavText">{useObservable(tab.tabTitle)}</span>
|
<span className="tabNavText">{useObservable(tab?.tabTitle || ko.observable("Connect"))}</span>
|
||||||
<span className="tabIconSection">
|
<span className="tabIconSection">
|
||||||
<CloseButton tab={tab} active={active} hovering={hovering} />
|
<CloseButton tab={tab} active={active} hovering={hovering} />
|
||||||
</span>
|
</span>
|
||||||
|
@ -83,7 +89,7 @@ const CloseButton = ({ tab, active, hovering }: { tab: Tab; active: boolean; hov
|
||||||
role="button"
|
role="button"
|
||||||
aria-label="Close Tab"
|
aria-label="Close Tab"
|
||||||
className="cancelButton"
|
className="cancelButton"
|
||||||
onClick={() => tab.onCloseTabButtonClick()}
|
onClick={() => (tab ? tab.onCloseTabButtonClick() : useTabs.getState().closeConnectTab())}
|
||||||
tabIndex={active ? 0 : undefined}
|
tabIndex={active ? 0 : undefined}
|
||||||
onKeyPress={({ nativeEvent: e }) => tab.onKeyPressClose(undefined, e)}
|
onKeyPress={({ nativeEvent: e }) => tab.onKeyPressClose(undefined, e)}
|
||||||
>
|
>
|
||||||
|
@ -133,9 +139,18 @@ function TabPane({ tab, active }: { tab: Tab; active: boolean }) {
|
||||||
}
|
}
|
||||||
}, [ref, tab]);
|
}, [ref, tab]);
|
||||||
|
|
||||||
if ("render" in tab) {
|
if (tab) {
|
||||||
return <div {...attrs}>{tab.render()}</div>;
|
if ("render" in tab) {
|
||||||
|
return <div {...attrs}>{tab.render()}</div>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div {...attrs} ref={ref} data-bind="html:html" />;
|
return <div {...attrs} ref={ref} data-bind="html:html" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onKeyPressConnectTab = (e: KeyboardEvent): void => {
|
||||||
|
if (e.key === "Enter" || e.key === "Space") {
|
||||||
|
useTabs.getState().activateConnectTab();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { TeachingBubble } from "@fluentui/react";
|
import { TeachingBubble } from "@fluentui/react";
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
import { useDatabases } from "Explorer/useDatabases";
|
||||||
|
import { useTabs } from "hooks/useTabs";
|
||||||
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
@ -140,7 +141,7 @@ export const QuickstartTutorial: React.FC = (): JSX.Element => {
|
||||||
hasCloseButton
|
hasCloseButton
|
||||||
primaryButtonProps={{
|
primaryButtonProps={{
|
||||||
text: "Launch connect",
|
text: "Launch connect",
|
||||||
//onClick: () => setStep(7),
|
onClick: () => useTabs.getState().openAndActivateConnectTab(),
|
||||||
}}
|
}}
|
||||||
secondaryButtonProps={{
|
secondaryButtonProps={{
|
||||||
text: "Previous",
|
text: "Previous",
|
||||||
|
|
|
@ -60,6 +60,7 @@ initializeIcons();
|
||||||
const App: React.FunctionComponent = () => {
|
const App: React.FunctionComponent = () => {
|
||||||
const [isLeftPaneExpanded, setIsLeftPaneExpanded] = useState<boolean>(true);
|
const [isLeftPaneExpanded, setIsLeftPaneExpanded] = useState<boolean>(true);
|
||||||
const openedTabs = useTabs((state) => state.openedTabs);
|
const openedTabs = useTabs((state) => state.openedTabs);
|
||||||
|
const isConnectTabOpen = useTabs((state) => state.isConnectTabOpen);
|
||||||
|
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const explorer = useKnockoutExplorer(config?.platform);
|
const explorer = useKnockoutExplorer(config?.platform);
|
||||||
|
@ -103,7 +104,7 @@ const App: React.FunctionComponent = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Collections Tree - End */}
|
{/* Collections Tree - End */}
|
||||||
{openedTabs.length === 0 && <SplashScreen explorer={explorer} />}
|
{openedTabs.length === 0 && !isConnectTabOpen && <SplashScreen explorer={explorer} />}
|
||||||
<Tabs />
|
<Tabs />
|
||||||
</div>
|
</div>
|
||||||
{/* Collections Tree and Tabs - End */}
|
{/* Collections Tree and Tabs - End */}
|
||||||
|
|
|
@ -7,6 +7,8 @@ import TabsBase from "../Explorer/Tabs/TabsBase";
|
||||||
interface TabsState {
|
interface TabsState {
|
||||||
openedTabs: TabsBase[];
|
openedTabs: TabsBase[];
|
||||||
activeTab: TabsBase;
|
activeTab: TabsBase;
|
||||||
|
isConnectTabOpen: boolean;
|
||||||
|
isConnectTabActive: boolean;
|
||||||
activateTab: (tab: TabsBase) => void;
|
activateTab: (tab: TabsBase) => void;
|
||||||
activateNewTab: (tab: TabsBase) => void;
|
activateNewTab: (tab: TabsBase) => void;
|
||||||
updateTab: (tab: TabsBase) => void;
|
updateTab: (tab: TabsBase) => void;
|
||||||
|
@ -15,19 +17,24 @@ interface TabsState {
|
||||||
closeTabsByComparator: (comparator: (tab: TabsBase) => boolean) => void;
|
closeTabsByComparator: (comparator: (tab: TabsBase) => boolean) => void;
|
||||||
closeTab: (tab: TabsBase) => void;
|
closeTab: (tab: TabsBase) => void;
|
||||||
closeAllNotebookTabs: (hardClose: boolean) => void;
|
closeAllNotebookTabs: (hardClose: boolean) => void;
|
||||||
|
activateConnectTab: () => void;
|
||||||
|
openAndActivateConnectTab: () => void;
|
||||||
|
closeConnectTab: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
||||||
openedTabs: [],
|
openedTabs: [],
|
||||||
activeTab: undefined,
|
activeTab: undefined,
|
||||||
|
isConnectTabOpen: false,
|
||||||
|
isConnectTabActive: false,
|
||||||
activateTab: (tab: TabsBase): void => {
|
activateTab: (tab: TabsBase): void => {
|
||||||
if (get().openedTabs.some((openedTab) => openedTab.tabId === tab.tabId)) {
|
if (get().openedTabs.some((openedTab) => openedTab.tabId === tab.tabId)) {
|
||||||
set({ activeTab: tab });
|
set({ activeTab: tab, isConnectTabActive: false });
|
||||||
tab.onActivate();
|
tab.onActivate();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
activateNewTab: (tab: TabsBase): void => {
|
activateNewTab: (tab: TabsBase): void => {
|
||||||
set((state) => ({ openedTabs: [...state.openedTabs, tab], activeTab: tab }));
|
set((state) => ({ openedTabs: [...state.openedTabs, tab], activeTab: tab, isConnectTabActive: false }));
|
||||||
tab.onActivate();
|
tab.onActivate();
|
||||||
},
|
},
|
||||||
updateTab: (tab: TabsBase) => {
|
updateTab: (tab: TabsBase) => {
|
||||||
|
@ -66,7 +73,7 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
if (updatedTabs.length === 0) {
|
if (updatedTabs.length === 0) {
|
||||||
set({ activeTab: undefined });
|
set({ activeTab: undefined, isConnectTabActive: get().isConnectTabOpen });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tab.tabId === activeTab.tabId && tabIndex !== -1) {
|
if (tab.tabId === activeTab.tabId && tabIndex !== -1) {
|
||||||
|
@ -104,8 +111,21 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
if (get().openedTabs.length === 0) {
|
if (get().openedTabs.length === 0) {
|
||||||
set({ activeTab: undefined });
|
set({ activeTab: undefined, isConnectTabActive: get().isConnectTabOpen });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
activateConnectTab: () => {
|
||||||
|
if (get().isConnectTabOpen) {
|
||||||
|
set({ isConnectTabActive: true, activeTab: undefined });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openAndActivateConnectTab: () => set({ isConnectTabActive: true, isConnectTabOpen: true, activeTab: undefined }),
|
||||||
|
closeConnectTab: () => {
|
||||||
|
const { isConnectTabActive, openedTabs } = get();
|
||||||
|
if (isConnectTabActive && openedTabs?.length > 0) {
|
||||||
|
set({ activeTab: openedTabs[0] });
|
||||||
|
}
|
||||||
|
set({ isConnectTabActive: false, isConnectTabOpen: false });
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
Loading…
Reference in New Issue