This commit is contained in:
Steve Faulkner
2020-12-31 15:05:34 -06:00
parent 585f75bc91
commit bf30c3190a
4 changed files with 129 additions and 38 deletions

View File

@@ -2,18 +2,24 @@ import "./Platform/Hosted/ConnectScreen.less";
import { useBoolean } from "@uifabric/react-hooks"; import { useBoolean } from "@uifabric/react-hooks";
import { import {
DefaultButton, DefaultButton,
DetailsList,
DirectionalHint, DirectionalHint,
FocusZone, FocusZone,
IContextualMenuProps,
initializeIcons, initializeIcons,
Panel, Panel,
PanelType,
Persona, Persona,
PersonaInitialsColor, PersonaInitialsColor,
PersonaSize PersonaSize,
SelectionMode,
Selection
} from "office-ui-fabric-react"; } from "office-ui-fabric-react";
import * as React from "react"; import * as React from "react";
import { render } from "react-dom"; import { render } from "react-dom";
import FeedbackIcon from "../images/Feedback.svg"; import FeedbackIcon from "../images/Feedback.svg";
import ConnectIcon from "../images/HostedConnectwhite.svg"; import ConnectIcon from "../images/HostedConnectwhite.svg";
import ChevronRight from "../images/chevron-right.svg";
import "../less/hostedexplorer.less"; import "../less/hostedexplorer.less";
import { CommandButtonComponent } from "./Explorer/Controls/CommandButton/CommandButtonComponent"; import { CommandButtonComponent } from "./Explorer/Controls/CommandButton/CommandButtonComponent";
import "./Explorer/Menus/NavBar/MeControlComponent.less"; import "./Explorer/Menus/NavBar/MeControlComponent.less";
@@ -22,6 +28,7 @@ import "./Shared/appInsights";
import { AccountSwitchComponent } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponent"; import { AccountSwitchComponent } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponent";
import { AuthContext, AuthProvider } from "./contexts/authContext"; import { AuthContext, AuthProvider } from "./contexts/authContext";
import { usePortalAccessToken } from "./hooks/usePortalAccessToken"; import { usePortalAccessToken } from "./hooks/usePortalAccessToken";
import { useDirectories } from "./hooks/useDirectories";
import { AuthType } from "./AuthType"; import { AuthType } from "./AuthType";
initializeIcons(); initializeIcons();
@@ -31,11 +38,41 @@ const App: React.FunctionComponent = () => {
const encryptedToken = params && params.get("key"); const encryptedToken = params && params.get("key");
const encryptedTokenMetadata = usePortalAccessToken(encryptedToken); const encryptedTokenMetadata = usePortalAccessToken(encryptedToken);
const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false); const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false);
const { isLoggedIn, aadlogin: login, account, aadlogout: logout } = React.useContext(AuthContext); const { isLoggedIn, aadlogin: login, account, aadlogout: logout, tenantId } = React.useContext(AuthContext);
const [isConnectionStringVisible, { setTrue: showConnectionString }] = useBoolean(false); const [isConnectionStringVisible, { setTrue: showConnectionString }] = useBoolean(false);
const photo = useGraphPhoto(); const photo = useGraphPhoto();
const directories = useDirectories();
// const [selectedItem, setSelectedItem] = React.useState<any>(undefined);
const selection = new Selection({
getKey: item => item.tenantId,
items: directories,
onSelectionChanged: () => {
const selected = selection.getSelection()[0];
if (selected.tenantId !== tenantId) {
console.log("new Tenant", selected.tenantId);
}
},
selectionMode: SelectionMode.single
});
selection.setKeySelected(tenantId, true, false);
const menuProps = { // private _renderPersonaComponent = (): JSX.Element => {
// const { user } = this.props;
// const personaProps: IPersonaSharedProps = {
// imageUrl: user.imageUrl,
// text: user.name,
// secondaryText: user.email,
// showSecondaryText: true,
// showInitialsUntilImageLoads: true,
// initialsColor: PersonaInitialsColor.teal,
// size: PersonaSize.size72,
// className: "mecontrolContextualMenuPersona"
// };
// return <Persona {...personaProps} />;
// };
const menuProps: IContextualMenuProps = {
className: "mecontrolContextualMenu", className: "mecontrolContextualMenu",
isBeakVisible: false, isBeakVisible: false,
directionalHintFixed: true, directionalHintFixed: true,
@@ -44,30 +81,15 @@ const App: React.FunctionComponent = () => {
minPagePadding: 0 minPagePadding: 0
}, },
items: [ items: [
{
key: "Persona",
onRender: () => <Persona />
},
{ {
key: "SwitchDirectory", key: "SwitchDirectory",
onRender: () => ( text: "Switch Directory",
<div className="switchDirectoryLink" onClick={() => openPanel}> onClick: openPanel
Switch Directory
</div>
)
}, },
{ {
key: "SignOut", key: "SignOut",
onRender: () => ( text: "Sign Out",
<div onClick: logout
className="signOutLink"
onClick={() => {
logout();
}}
>
Sign out
</div>
)
} }
] ]
}; };
@@ -101,8 +123,7 @@ const App: React.FunctionComponent = () => {
const buttonProps = { const buttonProps = {
id: "mecontrolHeader", id: "mecontrolHeader",
className: "mecontrolHeaderButton", className: "mecontrolHeaderButton",
menuProps: menuProps, menuProps,
onRenderMenuIcon: () => <span />,
styles: { styles: {
rootHovered: { backgroundColor: "#393939" }, rootHovered: { backgroundColor: "#393939" },
rootFocused: { backgroundColor: "#393939" }, rootFocused: { backgroundColor: "#393939" },
@@ -257,14 +278,34 @@ const App: React.FunctionComponent = () => {
)} )}
<div data-bind="react: firewallWarningComponentAdapter" /> <div data-bind="react: firewallWarningComponentAdapter" />
<div data-bind="react: dialogComponentAdapter" /> <div data-bind="react: dialogComponentAdapter" />
<Panel headerText="Select Directory" isOpen={isOpen} onDismiss={dismissPanel} closeButtonAriaLabel="Close"> <Panel
{/* <div className="directoryDropdownContainer"> type={PanelType.medium}
<DefaultDirectoryDropdownComponent /> headerText="Select Directory"
</div> isOpen={isOpen}
<div className="directoryDivider" /> onDismiss={dismissPanel}
<div className="directoryListContainer"> closeButtonAriaLabel="Close"
<DirectoryListComponent /> >
</div> */} <DetailsList
items={selection.getItems()}
columns={[
{
key: "name",
name: "Name",
minWidth: 200,
maxWidth: 200,
fieldName: "displayName"
},
{
key: "id",
name: "ID",
minWidth: 200,
maxWidth: 200,
fieldName: "tenantId"
}
]}
selectionMode={SelectionMode.single}
selection={selection}
/>
</Panel> </Panel>
</div> </div>
); );

View File

@@ -5,12 +5,12 @@ import Explorer from "./Explorer/Explorer";
export const applyExplorerBindings = (explorer: Explorer) => { export const applyExplorerBindings = (explorer: Explorer) => {
if (!!explorer) { if (!!explorer) {
// This message should ideally be sent immediately after explorer has been initialized for optimal data explorer load times.
// TODO: Send another message to describe that the bindings have been applied, and handle message transfers accordingly in the portal
sendMessage("ready");
window.dataExplorer = explorer; window.dataExplorer = explorer;
BindingHandlersRegisterer.registerBindingHandlers(); BindingHandlersRegisterer.registerBindingHandlers();
ko.applyBindings(explorer); ko.applyBindings(explorer);
// This message should ideally be sent immediately after explorer has been initialized for optimal data explorer load times.
// TODO: Send another message to describe that the bindings have been applied, and handle message transfers accordingly in the portal
sendMessage("ready");
$("#divExplorer").show(); $("#divExplorer").show();
} }
}; };

View File

@@ -18,6 +18,7 @@ interface AuthContext {
account?: Msal.Account; account?: Msal.Account;
graphToken?: string; graphToken?: string;
armToken?: string; armToken?: string;
tenantId?: string;
aadlogout: () => unknown; aadlogout: () => unknown;
aadlogin: () => unknown; aadlogin: () => unknown;
} }
@@ -37,15 +38,22 @@ export const AuthProvider: React.FunctionComponent = ({ children }) => {
const [account, setAccount] = useState<Msal.Account>(); const [account, setAccount] = useState<Msal.Account>();
const [graphToken, setGraphToken] = useState<string>(); const [graphToken, setGraphToken] = useState<string>();
const [armToken, setArmToken] = useState<string>(); const [armToken, setArmToken] = useState<string>();
const [tenantId, setTenantId] = useState<string>();
const aadlogin = useCallback(async () => { const aadlogin = useCallback(async () => {
const response = await msal.loginPopup(); const response = await msal.loginPopup();
setLoggedIn(); setLoggedIn();
setAccount(response.account); setAccount(response.account);
setTenantId(response.tenantId);
msal.authority = "https://login.microsoftonline.com/481f23b0-3fb3-4e76-812d-15513d11dbfc";
const [graphTokenResponse, armTokenResponse] = await Promise.all([ const [graphTokenResponse, armTokenResponse] = await Promise.all([
msal.acquireTokenSilent({ scopes: ["https://graph.windows.net//.default"] }), msal.acquireTokenSilent({
msal.acquireTokenSilent({ scopes: ["https://management.azure.com//.default"] }) scopes: ["https://graph.windows.net//.default"]
}),
msal.acquireTokenSilent({
scopes: ["https://management.azure.com//.default"]
})
]); ]);
setGraphToken(graphTokenResponse.accessToken); setGraphToken(graphTokenResponse.accessToken);
@@ -58,7 +66,7 @@ export const AuthProvider: React.FunctionComponent = ({ children }) => {
}, []); }, []);
return ( return (
<AuthContext.Provider value={{ isLoggedIn, account, aadlogin, aadlogout, graphToken, armToken }}> <AuthContext.Provider value={{ isLoggedIn, account, aadlogin, aadlogout, graphToken, armToken, tenantId }}>
{children} {children}
</AuthContext.Provider> </AuthContext.Provider>
); );

View File

@@ -0,0 +1,42 @@
import { useContext, useEffect, useState } from "react";
import { AuthContext } from "../contexts/authContext";
import { Tenant } from "../Contracts/DataModels";
interface TenantListResult {
nextLink: string;
value: Tenant[];
}
export async function fetchDirectories(accessToken: string): Promise<Tenant[]> {
const headers = new Headers();
const bearer = `Bearer ${accessToken}`;
headers.append("Authorization", bearer);
let tenents: Array<Tenant> = [];
let nextLink = `https://management.azure.com/tenants?api-version=2020-01-01`;
while (nextLink) {
const response = await fetch(nextLink, { headers });
const result: TenantListResult =
response.status === 204 || response.status === 304 ? undefined : await response.json();
if (!response.ok) {
throw result;
}
nextLink = result.nextLink;
tenents = [...tenents, ...result.value];
}
return tenents;
}
export function useDirectories(): Tenant[] {
const { armToken } = useContext(AuthContext);
const [state, setState] = useState<Tenant[]>();
useEffect(() => {
if (armToken) {
fetchDirectories(armToken).then(response => setState(response));
}
}, [armToken]);
return state || [];
}