-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
82a7fba
commit f89f9ed
Showing
42 changed files
with
598 additions
and
263 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
{ | ||
"cSpell.words": ["cookieconsent", "csrf"] | ||
"cSpell.words": ["Rollbar", "cookieconsent", "csrf"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import React, { CSSProperties } from "react" | ||
import openIconicSprite from "open-iconic/sprite/open-iconic.min.svg" | ||
|
||
interface Props { | ||
icon: string | ||
fillColor?: string | ||
className?: string | ||
style?: CSSProperties | ||
} | ||
|
||
const IconicIcon = (props: Props): JSX.Element => { | ||
const className = props.className | ||
? "iconic-icon ".concat(props.className) | ||
: "iconic-icon" | ||
return ( | ||
<svg viewBox="0 0 8 8" className={className} style={props.style}> | ||
<use | ||
xlinkHref={openIconicSprite + "#" + props.icon} | ||
style={{ fill: props.fillColor }} | ||
></use> | ||
</svg> | ||
) | ||
} | ||
|
||
export default IconicIcon |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import React from "react" | ||
import IconicIcon from "../IconicIcon" | ||
import { AuthUser } from "./UserProvider" | ||
|
||
interface Props { | ||
isLoading: boolean | ||
user: AuthUser | null | ||
} | ||
|
||
const Avatar = (props: Props): JSX.Element => { | ||
const { isLoading, user } = props | ||
if (isLoading) { | ||
return <IconicIcon icon="loop-circular" /> | ||
} else if (!user) { | ||
return <IconicIcon icon="person" /> | ||
} else { | ||
return ( | ||
<> | ||
<IconicIcon icon="person" fillColor="white" /> | ||
</> | ||
) | ||
} | ||
} | ||
|
||
export default Avatar |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import React from "react" | ||
import { useUserContext } from "./UserProvider" | ||
import SignInWithApple from "./SignInWithApple" | ||
import SignInWithGoogle from "./SignInWithGoogle" | ||
|
||
const LoginOrLogout = (): JSX.Element => { | ||
return ( | ||
<> | ||
<LogoutLink /> | ||
<LoginLinks /> | ||
</> | ||
) | ||
} | ||
|
||
const LogoutLink = (): JSX.Element | null => { | ||
const { isAuthenticated, logout } = useUserContext() | ||
return isAuthenticated ? ( | ||
<button className="dropdown-item" onClick={logout}> | ||
Logout | ||
</button> | ||
) : null | ||
} | ||
|
||
const LoginLinks = (): JSX.Element | null => { | ||
const { isAuthenticated } = useUserContext() | ||
return isAuthenticated ? null : ( | ||
<> | ||
<SignInWithApple buttonClassName="dropdown-item" /> | ||
<SignInWithGoogle buttonClassName="dropdown-item" /> | ||
</> | ||
) | ||
} | ||
|
||
export default LoginOrLogout |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,26 @@ | ||
import React from "react" | ||
import "./SignInWithGoogle.css" | ||
import SignInWithProvider from "./SignInWithProvider" | ||
import SignInWithProvider, { | ||
SignInWithProviderProps, | ||
} from "./SignInWithProvider" | ||
import providerLogo from "./images/btn_google_light_normal_ios.svg" | ||
import { combineClassNames } from "../../lib/reactUtil" | ||
|
||
const providerName = "GOOGLE" | ||
const providerLabel = "Sign in with Google" | ||
|
||
type Props = Pick<SignInWithProviderProps, "buttonClassName"> | ||
|
||
// note styled according to https://developers.google.com/identity/branding-guidelines | ||
const SignInWithGoogle2 = (): JSX.Element => { | ||
const SignInWithGoogle = (props: Props): JSX.Element => { | ||
return ( | ||
<SignInWithProvider | ||
provider={providerName} | ||
label={providerLabel} | ||
logoUrl={providerLogo} | ||
buttonClassName="goog" | ||
buttonClassName={combineClassNames("goog", props.buttonClassName)} | ||
/> | ||
) | ||
} | ||
|
||
export default SignInWithGoogle2 | ||
export default SignInWithGoogle |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import React, { useContext, useEffect, useState } from "react" | ||
import { fetchJson } from "../../lib/fetch" | ||
import { doLogin, doLogout } from "./authUtil" | ||
|
||
/** Defines the attributes of an authenticated user. */ | ||
export interface AuthUser { | ||
/** The unique identifier for the user (the subject). */ | ||
sub: string | ||
/** Time User last updated (milliseconds since epoch) */ | ||
updatedAt: number | ||
/** If specified, specifies the email address of the user. */ | ||
email?: string | ||
/** If specified, specifies the name of the user. */ | ||
name?: string | ||
} | ||
|
||
export interface ProvidedUserContext { | ||
user: AuthUser | null | ||
isAuthenticated: boolean | ||
isLoading: boolean | ||
login: (providerName: string) => Promise<void> | ||
logout: () => Promise<void> | ||
// TODO: add the below login/logout/token implementations: | ||
// getAccessToken: (options?: AccessTokenOptions) => Promise<string> | ||
} | ||
|
||
const DefaultUserContext: ProvidedUserContext = { | ||
user: null, | ||
isAuthenticated: false, | ||
isLoading: true, | ||
login: async (providerName: string) => { | ||
doLogin(providerName) | ||
}, | ||
logout: async () => { | ||
doLogout() | ||
}, | ||
} | ||
|
||
const UserContext = React.createContext<ProvidedUserContext>(DefaultUserContext) | ||
export const useUserContext = (): ProvidedUserContext => useContext(UserContext) | ||
|
||
interface Props { | ||
children?: React.ReactNode | ||
} | ||
|
||
export const UserProvider = (props: Props): JSX.Element => { | ||
const [user, setUser] = useState<AuthUser | null>(null) | ||
const [isLoading, setIsLoading] = useState(true) | ||
|
||
useEffect(() => { | ||
async function go(): Promise<void> { | ||
try { | ||
// see if this user is authenticated by calling the UserInfo endpoint | ||
const userInfo = await fetchJson<AuthUser>( | ||
`${process.env.PUBLIC_URL}/auth/me` | ||
) | ||
setUser(userInfo) | ||
} catch (e) { | ||
// TODO: fix fetchJson so we can get the response code and get error body (AuthUser | AuthError). | ||
// eslint-disable-next-line no-console | ||
console.error("Failed to authenticate user: " + e.toString()) | ||
} | ||
setIsLoading(false) | ||
} | ||
go() | ||
}, []) | ||
|
||
return ( | ||
<UserContext.Provider | ||
value={{ | ||
user, | ||
isAuthenticated: Boolean(user), | ||
isLoading, | ||
login: DefaultUserContext.login, | ||
logout: DefaultUserContext.logout, | ||
}} | ||
> | ||
{props.children} | ||
</UserContext.Provider> | ||
) | ||
} |
Oops, something went wrong.