Skip to content

Commit

Permalink
Merge 61b1114 into 8085516
Browse files Browse the repository at this point in the history
  • Loading branch information
AlinaGoaga committed Feb 23, 2022
2 parents 8085516 + 61b1114 commit 860883e
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 44 deletions.
27 changes: 3 additions & 24 deletions ui/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ErrorBoundary from "./components/ErrorBoundary";
import Layout from "./components/Layout";
import AppContextProvider from "./contexts/AppContext";
import AuthContextProvider, { AuthCheck } from "./contexts/AuthContext";
import FeatureFlagsContextProvider from "./contexts/FeatureFlags";
import {
Applications as appsClient,
GitProvider,
Expand Down Expand Up @@ -86,33 +87,13 @@ const App = () => (
);

export default function AppContainer() {
const [authFlag, setAuthFlag] = React.useState<boolean>(null);

const getAuthFlag = React.useCallback(() => {
fetch("/v1/featureflags")
.then((response) => response.json())
.then((data) =>
setAuthFlag(data.flags.WEAVE_GITOPS_AUTH_ENABLED === "true")
)
.catch((err) => console.log(err));
}, []);

React.useEffect(() => {
getAuthFlag();
}, [getAuthFlag]);

// Loading...
if (authFlag === null) {
return null;
}

return (
<MuiThemeProvider theme={muiTheme}>
<ThemeProvider theme={theme}>
<Fonts />
<GlobalStyle />
<Router>
{authFlag ? (
<FeatureFlagsContextProvider>
<AuthContextProvider>
<Switch>
{/* <Signin> does not use the base page <Layout> so pull it up here */}
Expand All @@ -125,9 +106,7 @@ export default function AppContainer() {
</Route>
</Switch>
</AuthContextProvider>
) : (
<App />
)}
</FeatureFlagsContextProvider>
</Router>
</ThemeProvider>
</MuiThemeProvider>
Expand Down
5 changes: 5 additions & 0 deletions ui/components/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import RemoveCircleIcon from "@material-ui/icons/RemoveCircle";
import SaveAltIcon from "@material-ui/icons/SaveAlt";
import SkipNextIcon from "@material-ui/icons/SkipNext";
import SkipPreviousIcon from "@material-ui/icons/SkipPrevious";
import LogoutIcon from "@material-ui/icons/ExitToApp";
import * as React from "react";
import styled from "styled-components";
import { colors, spacing } from "../typedefs/styled";
Expand All @@ -34,6 +35,7 @@ export enum IconType {
SkipNextIcon,
SkipPreviousIcon,
RemoveCircleIcon,
LogoutIcon,
}

type Props = {
Expand Down Expand Up @@ -91,6 +93,9 @@ function getIcon(i: IconType) {
case IconType.RemoveCircleIcon:
return RemoveCircleIcon;

case IconType.LogoutIcon:
return LogoutIcon;

default:
break;
}
Expand Down
11 changes: 5 additions & 6 deletions ui/components/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import styled from "styled-components";
import useNavigation from "../hooks/navigation";
import { PageRoute } from "../lib/types";
import { formatURL, getNavValue } from "../lib/utils";
import { FeatureFlags } from "../contexts/FeatureFlags";
import Flex from "./Flex";
import Link from "./Link";
import Logo from "./Logo";
import UserSettings from "./UserSettings";

type Props = {
className?: string;
children?: any;
authFlag?: boolean;
};

const navItems = [{ value: PageRoute.Applications, label: "Applications" }];
Expand Down Expand Up @@ -94,20 +97,16 @@ const TopToolBar = styled(Flex)`
}
`;

//style for account icon - disabled while no account functionality exists
// const UserAvatar = styled(Icon)`
// padding-right: ${(props) => props.theme.spacing.medium};
// `;

function Layout({ className, children }: Props) {
const { authFlag } = React.useContext(FeatureFlags);
const { currentPage } = useNavigation();

return (
<div className={className}>
<AppContainer>
<TopToolBar between align>
<Logo />
{/* code for account icon - disabled while no account functionality exists <UserAvatar size="xl" type={IconType.Account} color="white" /> */}
{authFlag ? <UserSettings /> : null}
</TopToolBar>
<Main wide>
<NavContainer>
Expand Down
68 changes: 68 additions & 0 deletions ui/components/UserSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {
IconButton,
ListItemIcon,
Menu,
MenuItem,
Tooltip,
} from "@material-ui/core";
import * as React from "react";
import styled from "styled-components";
import { Auth } from "../contexts/AuthContext";
import Icon, { IconType } from "./Icon";

const UserAvatar = styled(Icon)`
padding-right: ${(props) => props.theme.spacing.medium};
`;

const SettingsMenu = styled(Menu)`
.MuiListItemIcon-root {
min-width: 25px;
color: ${(props) => props.theme.colors.black};
}
`;

function UserSettings() {
const [anchorEl, setAnchorEl] = React.useState(null);
const { userInfo, logOut } = React.useContext(Auth);

const open = Boolean(anchorEl);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};

return (
<>
<Tooltip title="Account settings">
<IconButton
onClick={handleClick}
aria-controls={open ? "account-menu" : undefined}
aria-haspopup="true"
aria-expanded={open ? "true" : undefined}
>
<UserAvatar size="xl" type={IconType.Account} color="white" />
</IconButton>
</Tooltip>
<SettingsMenu
anchorEl={anchorEl}
id="account-menu"
open={open}
onClose={handleClose}
onClick={handleClose}
transformOrigin={{ horizontal: "right", vertical: "top" }}
>
<MenuItem>Hello, {userInfo?.email}</MenuItem>
<MenuItem onClick={() => logOut()}>
<ListItemIcon>
<Icon type={IconType.LogoutIcon} size="base" />
</ListItemIcon>
Logout
</MenuItem>
</SettingsMenu>
</>
);
}

export default styled(UserSettings)``;
65 changes: 51 additions & 14 deletions ui/contexts/AuthContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@ import * as React from "react";
import { useHistory, Redirect } from "react-router-dom";
import Layout from "../components/Layout";
import LoadingPage from "../components/LoadingPage";
import { FeatureFlags } from "../contexts/FeatureFlags";

const USER_INFO = "/oauth2/userinfo";
const SIGN_IN = "/oauth2/sign_in";
const LOG_OUT = "/oauth2/logout";
const AUTH_PATH_SIGNIN = "/sign_in";

export const AuthCheck = ({ children }) => {
// If the auth flag is null go straight to rendering the children
const { authFlag } = React.useContext(FeatureFlags);

if (!authFlag) {
return children;
}

const { loading, userInfo } = React.useContext(Auth);

// Wait until userInfo is loaded before showing signin or app content
Expand Down Expand Up @@ -36,15 +45,18 @@ export type AuthContext = {
};
error: { status: number; statusText: string };
loading: boolean;
logOut: () => void;
};

export const Auth = React.createContext<AuthContext | null>(null);

export default function AuthContextProvider({ children }) {
const [userInfo, setUserInfo] = React.useState<{
email: string;
groups: string[];
}>(null);
const { authFlag } = React.useContext(FeatureFlags);
const [userInfo, setUserInfo] =
React.useState<{
email: string;
groups: string[];
}>(null);
const [loading, setLoading] = React.useState<boolean>(true);
const [error, setError] = React.useState(null);
const history = useHistory();
Expand Down Expand Up @@ -80,21 +92,46 @@ export default function AuthContextProvider({ children }) {
.finally(() => setLoading(false));
}, []);

const logOut = React.useCallback(() => {
setLoading(true);
fetch(LOG_OUT, {
method: "POST",
})
.then((response) => {
if (response.status !== 200) {
setError(response);
return;
}
history.push("/sign_in");
})
.finally(() => setLoading(false));
}, []);

React.useEffect(() => {
if (!authFlag) {
return null;
}
getUserInfo();
return history.listen(getUserInfo);
}, [getUserInfo, history]);

return (
<Auth.Provider
value={{
signIn,
userInfo,
error,
loading,
}}
>
{children}
</Auth.Provider>
<>
{authFlag ? (
<Auth.Provider
value={{
signIn,
userInfo,
error,
loading,
logOut,
}}
>
{children}
</Auth.Provider>
) : (
children
)}
</>
);
}
39 changes: 39 additions & 0 deletions ui/contexts/FeatureFlags.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as React from "react";

export type FeatureFlagsContext = {
authFlag: boolean | null;
};

export const FeatureFlags =
React.createContext<FeatureFlagsContext | null>(null);

export default function FeatureFlagsContextProvider({ children }) {
const [authFlag, setAuthFlag] = React.useState<boolean>(null);

const getAuthFlag = React.useCallback(() => {
fetch("/v1/featureflags")
.then((response) => response.json())
.then((data) =>
setAuthFlag(data.flags.WEAVE_GITOPS_AUTH_ENABLED === "true")
)
.catch((err) => console.log(err));
}, []);

React.useEffect(() => {
getAuthFlag();
}, [getAuthFlag]);

// Loading...
if (authFlag === null) {
return null;
}
return (
<FeatureFlags.Provider
value={{
authFlag,
}}
>
{children}
</FeatureFlags.Provider>
);
}

0 comments on commit 860883e

Please sign in to comment.