From 235007d2955f7d40e547c3333ba0c097eede8116 Mon Sep 17 00:00:00 2001 From: nikek Date: Mon, 8 Jun 2020 14:06:35 +0200 Subject: [PATCH 1/7] Collapsible sidebar item for auth providers --- packages/app/src/components/Root/Root.tsx | 4 +- packages/core/src/layout/Sidebar/Items.tsx | 1 - .../core/src/layout/Sidebar/UserSettings.tsx | 83 +++++++++++++++++++ packages/core/src/layout/Sidebar/index.ts | 1 + 4 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 packages/core/src/layout/Sidebar/UserSettings.tsx diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 20eabbcac5..cdb8469ee2 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -31,7 +31,7 @@ import { SidebarDivider, SidebarSearchField, SidebarSpace, - SidebarUserBadge, + SidebarUserSettings, SidebarThemeToggle, } from '@backstage/core'; @@ -84,7 +84,7 @@ const Root: FC<{}> = ({ children }) => ( - + {children} diff --git a/packages/core/src/layout/Sidebar/Items.tsx b/packages/core/src/layout/Sidebar/Items.tsx index ddd71cc189..412800447a 100644 --- a/packages/core/src/layout/Sidebar/Items.tsx +++ b/packages/core/src/layout/Sidebar/Items.tsx @@ -148,7 +148,6 @@ export const SidebarItem: FC = ({ ); } - return ( (); // for scrolling down when collapse item opens + + const googleAuth = useApi(googleAuthApiRef); + const [profile, setProfile] = useState(); + + // TODO(soapraj): List all the providers supported by the app and let user log in from here + // TODO(soapraj): How to observe if the user is logged in + useEffect(() => { + googleAuth.getProfile({ optional: true }).then(googleProfile => { + setProfile(googleProfile); + }); + }, [googleAuth, open]); + + const handleClick = () => { + setOpen(!open); + setTimeout(() => ref.current?.scrollIntoView({ behavior: 'smooth' }), 300); + }; + + // Close the provider list when sidebar collapse + useEffect(() => { + if (!sidebarOpen && open) setOpen(false); + }, [sidebarOpen]); + + // Handle main auth info that is shown on the collapsible SidebarItem + let avatar; + let displayName; + if (profile) { + // const classes = useStyles(); + const email = profile.email; + const name = profile.name; + const imageUrl = profile.picture; + const avatarFallback = email.charAt(0).toUpperCase() + email.slice(1); + const emailTrimmed = email.split('@')[0]; + const displayEmail = + emailTrimmed.charAt(0).toUpperCase() + emailTrimmed.slice(1); + displayName = name ?? displayEmail; + avatar = imageUrl + ? () => + : () => {avatarFallback[0]}; + } + + return ( + <> + + + {open ? : } + + + + + profile ? googleAuth.logout() : googleAuth.getAccessToken() + } + > + + + + + + ); +} diff --git a/packages/core/src/layout/Sidebar/index.ts b/packages/core/src/layout/Sidebar/index.ts index baefc3d0e8..2bd961e197 100644 --- a/packages/core/src/layout/Sidebar/index.ts +++ b/packages/core/src/layout/Sidebar/index.ts @@ -33,3 +33,4 @@ export { } from './config'; export type { SidebarContextType } from './config'; export { SidebarThemeToggle } from './SidebarThemeToggle'; +export { SidebarUserSettings } from './UserSettings'; From eea242f14fa3702b0a152cbdff80846984a35da0 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Mon, 8 Jun 2020 16:45:43 +0200 Subject: [PATCH 2/7] List auth providers in UserSettings --- .../core/src/layout/Sidebar/UserSettings.tsx | 148 +++++++++++++++--- 1 file changed, 125 insertions(+), 23 deletions(-) diff --git a/packages/core/src/layout/Sidebar/UserSettings.tsx b/packages/core/src/layout/Sidebar/UserSettings.tsx index 26cffa0c35..f4f22dd151 100644 --- a/packages/core/src/layout/Sidebar/UserSettings.tsx +++ b/packages/core/src/layout/Sidebar/UserSettings.tsx @@ -1,32 +1,110 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import React, { useState, useContext, useEffect, useRef } from 'react'; import Collapse from '@material-ui/core/Collapse'; import ExpandLess from '@material-ui/icons/ExpandLess'; import ExpandMore from '@material-ui/icons/ExpandMore'; import StarBorder from '@material-ui/icons/StarBorder'; +import Star from '@material-ui/icons/Star'; import { SidebarContext } from './config'; -import { SidebarItem, SidebarDivider } from './Items'; +import { SidebarItem } from './Items'; import AccountCircleIcon from '@material-ui/icons/AccountCircle'; import Divider from '@material-ui/core/Divider'; -import { useApi, googleAuthApiRef, ProfileInfo } from '@backstage/core-api'; -import { Avatar, IconButton } from '@material-ui/core'; +import { + useApi, + googleAuthApiRef, + githubAuthApiRef, + ProfileInfo, +} from '@backstage/core-api'; +import { Avatar, IconButton, makeStyles, Tooltip } from '@material-ui/core'; import PowerButton from '@material-ui/icons/PowerSettingsNew'; -import { SidebarThemeToggle } from './SidebarThemeToggle'; +// import { SidebarThemeToggle } from './SidebarThemeToggle'; + +type Provider = { + title: string; + api: any; + identity?: boolean; + isSignedIn: boolean; + icon: any; +}; + +const useProviders = () => { + const googleAuth = useApi(googleAuthApiRef); + const githubAuth = useApi(githubAuthApiRef); + const [providers, setProviders] = useState([ + { + title: 'Google', + api: googleAuth, + identity: true, + isSignedIn: false, + icon: Star, + }, + { + title: 'Github', + api: githubAuth, + isSignedIn: false, + icon: StarBorder, + }, + ]); + + Promise.all( + providers.map((provider: Provider) => + provider.identity + ? provider.api.getIdToken({ optional: true }) + : provider.api.getAccessToken('', { optional: true }), + ), + ).then(results => { + results.map((result, i) => { + providers[i].isSignedIn = !!result; + }); + + setProviders(providers); + }); + + return providers; +}; + +const useStyles = makeStyles({ + avatar: { + width: 24, + height: 24, + }, +}); export function SidebarUserSettings() { const { isOpen: sidebarOpen } = useContext(SidebarContext); const [open, setOpen] = React.useState(false); const ref = useRef(); // for scrolling down when collapse item opens - - const googleAuth = useApi(googleAuthApiRef); + const providers = useProviders(); const [profile, setProfile] = useState(); + const classes = useStyles(); // TODO(soapraj): List all the providers supported by the app and let user log in from here // TODO(soapraj): How to observe if the user is logged in useEffect(() => { - googleAuth.getProfile({ optional: true }).then(googleProfile => { - setProfile(googleProfile); - }); - }, [googleAuth, open]); + const identityProvider = providers.find( + (provider: Provider) => provider.identity, + ); + identityProvider?.api + .getProfile({ optional: true }) + .then((userProfile: ProfileInfo) => { + setProfile(userProfile); + }); + }, [providers, open]); const handleClick = () => { setOpen(!open); @@ -36,13 +114,12 @@ export function SidebarUserSettings() { // Close the provider list when sidebar collapse useEffect(() => { if (!sidebarOpen && open) setOpen(false); - }, [sidebarOpen]); + }, [open, sidebarOpen]); // Handle main auth info that is shown on the collapsible SidebarItem let avatar; let displayName; if (profile) { - // const classes = useStyles(); const email = profile.email; const name = profile.name; const imageUrl = profile.picture; @@ -52,8 +129,12 @@ export function SidebarUserSettings() { emailTrimmed.charAt(0).toUpperCase() + emailTrimmed.slice(1); displayName = name ?? displayEmail; avatar = imageUrl - ? () => - : () => {avatarFallback[0]}; + ? () => + : () => ( + + {avatarFallback[0]} + + ); } return ( @@ -65,18 +146,39 @@ export function SidebarUserSettings() { icon={avatar || AccountCircleIcon} disableSelected > - {open ? : } + {open ? : } - - - profile ? googleAuth.logout() : googleAuth.getAccessToken() - } + {providers.map((provider: Provider) => ( + - - - + + provider.isSignedIn + ? provider.api.logout() + : provider.api.getAccessToken() + } + > + + + + + + ))} ); From 8cd640394d86070518fa8e97a8bfbc89d882dfd7 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Mon, 8 Jun 2020 16:46:56 +0200 Subject: [PATCH 3/7] PinButton wip --- packages/app/src/components/Root/Root.tsx | 2 + packages/core/src/layout/Sidebar/Items.tsx | 8 +++- packages/core/src/layout/Sidebar/Page.tsx | 2 +- .../Sidebar/{UserBadge.tsx => PinButton.tsx} | 37 +++---------------- packages/core/src/layout/Sidebar/index.ts | 2 +- 5 files changed, 17 insertions(+), 34 deletions(-) rename packages/core/src/layout/Sidebar/{UserBadge.tsx => PinButton.tsx} (63%) diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index cdb8469ee2..9c70191687 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -33,6 +33,7 @@ import { SidebarSpace, SidebarUserSettings, SidebarThemeToggle, + SidebarPinButton, } from '@backstage/core'; const useSidebarLogoStyles = makeStyles({ @@ -85,6 +86,7 @@ const Root: FC<{}> = ({ children }) => ( + {children} diff --git a/packages/core/src/layout/Sidebar/Items.tsx b/packages/core/src/layout/Sidebar/Items.tsx index 412800447a..0a875c9035 100644 --- a/packages/core/src/layout/Sidebar/Items.tsx +++ b/packages/core/src/layout/Sidebar/Items.tsx @@ -59,6 +59,7 @@ const useStyles = makeStyles(theme => { fontWeight: 'bold', whiteSpace: 'nowrap', lineHeight: 1.0, + flex: '3 1 auto', }, iconContainer: { boxSizing: 'border-box', @@ -84,6 +85,11 @@ const useStyles = makeStyles(theme => { searchContainer: { width: drawerWidthOpen - iconContainerWidth, }, + secondaryAction: { + width: theme.spacing(6), + textAlign: 'center', + marginRight: theme.spacing(1), + }, selected: { '&$root': { borderLeft: `solid ${selectedIndicatorWidth}px #9BF0E1`, @@ -165,7 +171,7 @@ export const SidebarItem: FC = ({ {text} )} - {children} +
{children}
); }; diff --git a/packages/core/src/layout/Sidebar/Page.tsx b/packages/core/src/layout/Sidebar/Page.tsx index 2a4cf0fbb2..1e2537f167 100644 --- a/packages/core/src/layout/Sidebar/Page.tsx +++ b/packages/core/src/layout/Sidebar/Page.tsx @@ -57,7 +57,7 @@ export const SidebarPage: FC<{}> = props => { return ( diff --git a/packages/core/src/layout/Sidebar/UserBadge.tsx b/packages/core/src/layout/Sidebar/PinButton.tsx similarity index 63% rename from packages/core/src/layout/Sidebar/UserBadge.tsx rename to packages/core/src/layout/Sidebar/PinButton.tsx index d3e02d26cc..5d245dadf1 100644 --- a/packages/core/src/layout/Sidebar/UserBadge.tsx +++ b/packages/core/src/layout/Sidebar/PinButton.tsx @@ -14,16 +14,12 @@ * limitations under the License. */ -import React, { FC, useContext, useEffect, useState } from 'react'; +import React, { FC, useContext } from 'react'; import { makeStyles } from '@material-ui/core'; -import AccountCircleIcon from '@material-ui/icons/AccountCircle'; -import { SidebarContext } from './config'; -import { SidebarItem } from './Items'; -import { LoggedUserBadge } from './LoggedUserBadge'; import DoubleArrowIcon from '@material-ui/icons/DoubleArrow'; +import { SidebarContext } from './config'; import { BackstageTheme } from '@backstage/theme'; import { SidebarPinStateContext } from './Page'; -import { useApi, googleAuthApiRef, ProfileInfo } from '@backstage/core-api'; const ARROW_BUTTON_SIZE = 20; const useStyles = makeStyles(theme => { @@ -36,12 +32,12 @@ const useStyles = makeStyles(theme => { right: 0, width: ARROW_BUTTON_SIZE, height: ARROW_BUTTON_SIZE, - top: `calc(50% - ${ARROW_BUTTON_SIZE / 2}px)`, + top: `calc(-50% - ${ARROW_BUTTON_SIZE / 2}px)`, display: 'flex', alignItems: 'center', justifyContent: 'center', borderRadius: '2px 0px 0px 2px', - background: theme.palette.pinSidebarButton.icon, + background: 'blue', color: theme.palette.pinSidebarButton.background, border: 'none', outline: 'none', @@ -53,37 +49,16 @@ const useStyles = makeStyles(theme => { }; }); -export const SidebarUserBadge: FC<{}> = () => { +export const SidebarPinButton: FC<{}> = () => { + console.log('hello'); const { isOpen } = useContext(SidebarContext); const { isPinned, toggleSidebarPinState } = useContext( SidebarPinStateContext, ); const classes = useStyles({ isPinned }); - const googleAuth = useApi(googleAuthApiRef); - const [profile, setProfile] = useState(); - - useEffect(() => { - // TODO(soapraj): How to observe if the user is logged in - // TODO(soapraj): List all the providers supported by the app and let user log in from here - googleAuth.getProfile({ optional: true }).then(googleProfile => { - setProfile(googleProfile); - }); - }, [googleAuth]); return (
- {profile ? ( - <> - - - ) : ( - - )} {isOpen && (