2020-09-10 02:32:14 -07:00
|
|
|
import React from "react";
|
2020-09-21 03:31:49 -07:00
|
|
|
import {
|
|
|
|
|
Box,
|
|
|
|
|
Button,
|
|
|
|
|
HStack,
|
|
|
|
|
IconButton,
|
|
|
|
|
Menu,
|
|
|
|
|
MenuButton,
|
|
|
|
|
MenuList,
|
|
|
|
|
MenuItem,
|
Start building a login form, behind a feature flag
Thinking about longevity, I think I wanna cut Auth0 loose, and just go back to using our own auth.
I had figured at the time that I didn't want to integrate with OpenNeo ID's whole mess, and I didn't want to write a whole new auth system, so Auth0 seemed to make things easier.
But now, it's just kinda a lot to be carrying along an external service as a dependency for login, especially when we've got all the stuff in the database right here. I wanna remove architecture pieces! Get it outta here!
And I'll finally build account creation from the 2020 site while I'm at it, which seemed like it was gonna be a bit of a pain with Auth0 and syncing anyway. (I think at the time I was a bit more optimistic about a full transfer from one system to another, but that's much further off than I realized, and this path will be much better for keeping things in sync.)
2022-08-16 17:34:51 -07:00
|
|
|
useDisclosure,
|
2022-08-17 16:05:36 -07:00
|
|
|
useToast,
|
2020-12-25 09:08:33 -08:00
|
|
|
} from "@chakra-ui/react";
|
2020-09-21 03:31:49 -07:00
|
|
|
import { HamburgerIcon } from "@chakra-ui/icons";
|
2020-09-10 02:32:14 -07:00
|
|
|
import { Link, useLocation } from "react-router-dom";
|
2020-09-20 19:36:51 -07:00
|
|
|
import { ChevronLeftIcon } from "@chakra-ui/icons";
|
2021-11-02 00:50:39 -07:00
|
|
|
import Image from "next/image";
|
2020-09-10 02:32:14 -07:00
|
|
|
|
Start building a login form, behind a feature flag
Thinking about longevity, I think I wanna cut Auth0 loose, and just go back to using our own auth.
I had figured at the time that I didn't want to integrate with OpenNeo ID's whole mess, and I didn't want to write a whole new auth system, so Auth0 seemed to make things easier.
But now, it's just kinda a lot to be carrying along an external service as a dependency for login, especially when we've got all the stuff in the database right here. I wanna remove architecture pieces! Get it outta here!
And I'll finally build account creation from the 2020 site while I'm at it, which seemed like it was gonna be a bit of a pain with Auth0 and syncing anyway. (I think at the time I was a bit more optimistic about a full transfer from one system to another, but that's much further off than I realized, and this path will be much better for keeping things in sync.)
2022-08-16 17:34:51 -07:00
|
|
|
import useCurrentUser, {
|
|
|
|
|
useAuthModeFeatureFlag,
|
2022-08-17 16:05:36 -07:00
|
|
|
useLogout,
|
Start building a login form, behind a feature flag
Thinking about longevity, I think I wanna cut Auth0 loose, and just go back to using our own auth.
I had figured at the time that I didn't want to integrate with OpenNeo ID's whole mess, and I didn't want to write a whole new auth system, so Auth0 seemed to make things easier.
But now, it's just kinda a lot to be carrying along an external service as a dependency for login, especially when we've got all the stuff in the database right here. I wanna remove architecture pieces! Get it outta here!
And I'll finally build account creation from the 2020 site while I'm at it, which seemed like it was gonna be a bit of a pain with Auth0 and syncing anyway. (I think at the time I was a bit more optimistic about a full transfer from one system to another, but that's much further off than I realized, and this path will be much better for keeping things in sync.)
2022-08-16 17:34:51 -07:00
|
|
|
} from "./components/useCurrentUser";
|
2021-01-03 19:49:08 -08:00
|
|
|
import HomeLinkIcon from "./images/home-link-icon.png";
|
2022-08-17 16:05:36 -07:00
|
|
|
import { useAuth0 } from "@auth0/auth0-react";
|
2020-09-10 02:32:14 -07:00
|
|
|
|
2020-09-21 03:08:24 -07:00
|
|
|
function GlobalHeader() {
|
2020-09-10 02:32:14 -07:00
|
|
|
return (
|
|
|
|
|
<Box display="flex" alignItems="center" flexWrap="wrap">
|
2020-09-21 03:18:32 -07:00
|
|
|
<HomeLink marginRight="2" />
|
2020-09-10 02:32:14 -07:00
|
|
|
<Box marginLeft="auto">
|
|
|
|
|
<UserNavBarSection />
|
|
|
|
|
</Box>
|
|
|
|
|
</Box>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-21 03:18:32 -07:00
|
|
|
function HomeLink(props) {
|
|
|
|
|
const { pathname } = useLocation();
|
|
|
|
|
const isHomePage = pathname === "/";
|
|
|
|
|
|
2020-09-10 02:32:14 -07:00
|
|
|
return (
|
|
|
|
|
<Box
|
|
|
|
|
as={Link}
|
|
|
|
|
to="/"
|
|
|
|
|
display="flex"
|
|
|
|
|
alignItems="center"
|
|
|
|
|
role="group"
|
2020-10-10 02:34:02 -07:00
|
|
|
// HACK: When we're on the homepage, I want the title "Dress to Impress"
|
|
|
|
|
// to stay visible for transition, but I don't want it to be a
|
|
|
|
|
// click target. To do this, I constrain the size of the container,
|
|
|
|
|
// and also remove pointer events from the overflowing children.
|
|
|
|
|
maxWidth={isHomePage ? "32px" : "none"}
|
2020-09-20 20:13:00 -07:00
|
|
|
{...props}
|
2020-09-10 02:32:14 -07:00
|
|
|
>
|
|
|
|
|
<Box
|
2020-10-10 02:34:02 -07:00
|
|
|
flex="0 0 auto"
|
2020-09-21 03:18:32 -07:00
|
|
|
display="flex"
|
|
|
|
|
alignItems="center"
|
|
|
|
|
marginRight="2"
|
|
|
|
|
position="relative"
|
2020-09-10 02:32:14 -07:00
|
|
|
transition="all 0.2s"
|
2020-09-21 03:18:32 -07:00
|
|
|
opacity="0.8"
|
|
|
|
|
_groupHover={{ transform: "scale(1.1)", opacity: "1" }}
|
|
|
|
|
_groupFocus={{ transform: "scale(1.1)", opacity: "1" }}
|
2020-09-10 02:32:14 -07:00
|
|
|
>
|
2020-09-21 03:18:32 -07:00
|
|
|
<Box
|
|
|
|
|
position="absolute"
|
|
|
|
|
right="100%"
|
|
|
|
|
opacity={isHomePage ? "0" : "1"}
|
2020-10-10 02:34:02 -07:00
|
|
|
pointerEvents={isHomePage ? "none" : "all"}
|
2020-09-21 03:18:32 -07:00
|
|
|
transform={isHomePage ? "translateX(3px)" : "none"}
|
|
|
|
|
transition="all 0.2s"
|
|
|
|
|
>
|
|
|
|
|
<ChevronLeftIcon />
|
|
|
|
|
</Box>
|
2021-11-02 00:50:39 -07:00
|
|
|
<Box height="32px" borderRadius="lg" boxShadow="md" overflow="hidden">
|
|
|
|
|
<Image
|
|
|
|
|
src={HomeLinkIcon}
|
|
|
|
|
alt=""
|
|
|
|
|
width={32}
|
|
|
|
|
height={32}
|
|
|
|
|
layout="fixed"
|
|
|
|
|
/>
|
|
|
|
|
</Box>
|
2020-09-21 03:18:32 -07:00
|
|
|
<Box
|
|
|
|
|
height="2em"
|
|
|
|
|
width="2em"
|
|
|
|
|
position="absolute"
|
|
|
|
|
top="0"
|
|
|
|
|
left="0"
|
|
|
|
|
right="0"
|
|
|
|
|
bottom="0"
|
|
|
|
|
borderRadius="lg"
|
|
|
|
|
transition="border 0.2s"
|
|
|
|
|
/>
|
2020-09-10 02:32:14 -07:00
|
|
|
</Box>
|
|
|
|
|
<Box
|
2020-10-10 02:34:02 -07:00
|
|
|
flex="0 0 auto"
|
2020-09-21 03:18:32 -07:00
|
|
|
fontFamily="Delicious"
|
|
|
|
|
fontWeight="600"
|
|
|
|
|
fontSize="2xl"
|
|
|
|
|
display={{ base: "none", sm: "block" }}
|
|
|
|
|
opacity={isHomePage ? "0" : "1"}
|
|
|
|
|
transition="all 0.2s"
|
|
|
|
|
marginRight="2"
|
2020-10-10 02:34:02 -07:00
|
|
|
pointerEvents={isHomePage ? "none" : "all"}
|
2020-09-21 03:18:32 -07:00
|
|
|
_groupHover={{ fontWeight: "900" }}
|
|
|
|
|
_groupFocus={{ fontWeight: "900" }}
|
|
|
|
|
>
|
|
|
|
|
Dress to Impress
|
|
|
|
|
</Box>
|
2020-09-10 02:32:14 -07:00
|
|
|
</Box>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function UserNavBarSection() {
|
2021-01-05 23:22:03 -08:00
|
|
|
const { isLoading, isLoggedIn, id, username } = useCurrentUser();
|
2020-09-10 02:32:14 -07:00
|
|
|
|
|
|
|
|
if (isLoading) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-05 23:22:03 -08:00
|
|
|
if (isLoggedIn) {
|
2020-09-10 02:32:14 -07:00
|
|
|
return (
|
|
|
|
|
<HStack align="center" spacing="2">
|
|
|
|
|
{username && (
|
|
|
|
|
<Box fontSize="sm" textAlign="right">
|
|
|
|
|
Hi, {username}!
|
|
|
|
|
</Box>
|
|
|
|
|
)}
|
2020-09-21 03:31:49 -07:00
|
|
|
<NavLinksList>
|
|
|
|
|
{id && (
|
2021-06-15 22:46:43 -07:00
|
|
|
<NavLinkItem as={Link} to={`/user/${id}/lists`}>
|
2021-01-18 13:51:52 -08:00
|
|
|
Lists
|
2020-09-21 03:31:49 -07:00
|
|
|
</NavLinkItem>
|
|
|
|
|
)}
|
2021-01-04 00:13:37 -08:00
|
|
|
<NavLinkItem as={Link} to={`/your-outfits`}>
|
|
|
|
|
Outfits
|
|
|
|
|
</NavLinkItem>
|
2020-09-21 03:31:49 -07:00
|
|
|
<NavLinkItem as={Link} to="/modeling">
|
|
|
|
|
Modeling
|
|
|
|
|
</NavLinkItem>
|
2022-08-17 16:05:36 -07:00
|
|
|
<LogoutButton />
|
2020-09-21 03:31:49 -07:00
|
|
|
</NavLinksList>
|
2020-09-10 02:32:14 -07:00
|
|
|
</HStack>
|
|
|
|
|
);
|
|
|
|
|
} else {
|
2020-09-20 19:36:51 -07:00
|
|
|
return (
|
|
|
|
|
<HStack align="center" spacing="2">
|
|
|
|
|
<NavButton as={Link} to="/modeling">
|
2020-09-10 02:32:14 -07:00
|
|
|
Modeling
|
2020-09-20 19:36:51 -07:00
|
|
|
</NavButton>
|
Start building a login form, behind a feature flag
Thinking about longevity, I think I wanna cut Auth0 loose, and just go back to using our own auth.
I had figured at the time that I didn't want to integrate with OpenNeo ID's whole mess, and I didn't want to write a whole new auth system, so Auth0 seemed to make things easier.
But now, it's just kinda a lot to be carrying along an external service as a dependency for login, especially when we've got all the stuff in the database right here. I wanna remove architecture pieces! Get it outta here!
And I'll finally build account creation from the 2020 site while I'm at it, which seemed like it was gonna be a bit of a pain with Auth0 and syncing anyway. (I think at the time I was a bit more optimistic about a full transfer from one system to another, but that's much further off than I realized, and this path will be much better for keeping things in sync.)
2022-08-16 17:34:51 -07:00
|
|
|
<LoginButton />
|
2020-09-20 19:36:51 -07:00
|
|
|
</HStack>
|
|
|
|
|
);
|
|
|
|
|
}
|
2020-09-10 02:32:14 -07:00
|
|
|
}
|
|
|
|
|
|
Start building a login form, behind a feature flag
Thinking about longevity, I think I wanna cut Auth0 loose, and just go back to using our own auth.
I had figured at the time that I didn't want to integrate with OpenNeo ID's whole mess, and I didn't want to write a whole new auth system, so Auth0 seemed to make things easier.
But now, it's just kinda a lot to be carrying along an external service as a dependency for login, especially when we've got all the stuff in the database right here. I wanna remove architecture pieces! Get it outta here!
And I'll finally build account creation from the 2020 site while I'm at it, which seemed like it was gonna be a bit of a pain with Auth0 and syncing anyway. (I think at the time I was a bit more optimistic about a full transfer from one system to another, but that's much further off than I realized, and this path will be much better for keeping things in sync.)
2022-08-16 17:34:51 -07:00
|
|
|
function LoginButton() {
|
2022-08-26 16:17:25 -07:00
|
|
|
const [authMode] = useAuthModeFeatureFlag();
|
2022-08-17 16:05:36 -07:00
|
|
|
const { loginWithRedirect } = useAuth0();
|
Start building a login form, behind a feature flag
Thinking about longevity, I think I wanna cut Auth0 loose, and just go back to using our own auth.
I had figured at the time that I didn't want to integrate with OpenNeo ID's whole mess, and I didn't want to write a whole new auth system, so Auth0 seemed to make things easier.
But now, it's just kinda a lot to be carrying along an external service as a dependency for login, especially when we've got all the stuff in the database right here. I wanna remove architecture pieces! Get it outta here!
And I'll finally build account creation from the 2020 site while I'm at it, which seemed like it was gonna be a bit of a pain with Auth0 and syncing anyway. (I think at the time I was a bit more optimistic about a full transfer from one system to another, but that's much further off than I realized, and this path will be much better for keeping things in sync.)
2022-08-16 17:34:51 -07:00
|
|
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
|
|
|
|
|
|
|
|
|
const onClick = () => {
|
|
|
|
|
if (authMode === "auth0") {
|
2022-08-17 16:05:36 -07:00
|
|
|
loginWithRedirect();
|
Start building a login form, behind a feature flag
Thinking about longevity, I think I wanna cut Auth0 loose, and just go back to using our own auth.
I had figured at the time that I didn't want to integrate with OpenNeo ID's whole mess, and I didn't want to write a whole new auth system, so Auth0 seemed to make things easier.
But now, it's just kinda a lot to be carrying along an external service as a dependency for login, especially when we've got all the stuff in the database right here. I wanna remove architecture pieces! Get it outta here!
And I'll finally build account creation from the 2020 site while I'm at it, which seemed like it was gonna be a bit of a pain with Auth0 and syncing anyway. (I think at the time I was a bit more optimistic about a full transfer from one system to another, but that's much further off than I realized, and this path will be much better for keeping things in sync.)
2022-08-16 17:34:51 -07:00
|
|
|
} else if (authMode === "db") {
|
|
|
|
|
onOpen();
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(`unexpected auth mode: ${JSON.stringify(authMode)}`);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<NavButton onClick={onClick}>Log in</NavButton>
|
|
|
|
|
{authMode === "db" && (
|
|
|
|
|
<React.Suspense fallback="">
|
|
|
|
|
<LoginModal isOpen={isOpen} onClose={onClose} />
|
|
|
|
|
</React.Suspense>
|
|
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// I don't wanna load all these Chakra components as part of the bundle for
|
|
|
|
|
// every single page. Split it out!
|
|
|
|
|
const LoginModal = React.lazy(() => import("./components/LoginModal"));
|
|
|
|
|
|
2022-08-17 16:05:36 -07:00
|
|
|
function LogoutButton() {
|
|
|
|
|
const toast = useToast();
|
|
|
|
|
const [logout, { loading, error }] = useLogout();
|
|
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
if (error != null) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
toast({
|
|
|
|
|
title: "Oops, there was an error logging you out.",
|
|
|
|
|
description: "Reload the page and try again? Sorry about that!",
|
|
|
|
|
status: "warning",
|
|
|
|
|
duration: null,
|
|
|
|
|
isClosable: true,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}, [error, toast]);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<NavLinkItem
|
|
|
|
|
onClick={() => logout({ returnTo: window.location.origin })}
|
|
|
|
|
// NOTE: The `isLoading` prop will only be relevant in the desktop case,
|
|
|
|
|
// where this renders as a NavButton. In the mobile case, the menu
|
|
|
|
|
// doesn't have a loading UI, and it closes when you click the
|
|
|
|
|
// button anyway. Not ideal, but fine for a simple quick action!
|
|
|
|
|
isLoading={loading}
|
|
|
|
|
>
|
|
|
|
|
Log out
|
|
|
|
|
</NavLinkItem>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const NavLinkTypeContext = React.createContext("button");
|
|
|
|
|
|
2020-11-03 19:36:48 -08:00
|
|
|
/**
|
|
|
|
|
* Renders the given <NavLinkItem /> children as a dropdown menu or as a list
|
|
|
|
|
* of buttons, depending on the screen size.
|
|
|
|
|
*
|
|
|
|
|
* It actually renders both, and shows/hides them by media query!
|
|
|
|
|
*/
|
2020-09-21 03:31:49 -07:00
|
|
|
function NavLinksList({ children }) {
|
2020-11-03 14:57:26 -08:00
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<Box display={{ base: "block", md: "none" }}>
|
|
|
|
|
<Menu>
|
|
|
|
|
<MenuButton>
|
|
|
|
|
<NavButton icon={<HamburgerIcon />} />
|
|
|
|
|
</MenuButton>
|
|
|
|
|
<MenuList>
|
2022-08-17 16:05:36 -07:00
|
|
|
<NavLinkTypeContext.Provider value="menu">
|
|
|
|
|
{children}
|
|
|
|
|
</NavLinkTypeContext.Provider>
|
2020-11-03 14:57:26 -08:00
|
|
|
</MenuList>
|
|
|
|
|
</Menu>
|
|
|
|
|
</Box>
|
|
|
|
|
<HStack spacing="2" display={{ base: "none", md: "flex" }}>
|
2022-08-17 16:05:36 -07:00
|
|
|
<NavLinkTypeContext.Provider value="button">
|
|
|
|
|
{children}
|
|
|
|
|
</NavLinkTypeContext.Provider>
|
2020-11-03 14:57:26 -08:00
|
|
|
</HStack>
|
|
|
|
|
</>
|
|
|
|
|
);
|
2020-09-21 03:31:49 -07:00
|
|
|
}
|
|
|
|
|
|
2022-08-17 16:05:36 -07:00
|
|
|
function NavLinkItem(props) {
|
|
|
|
|
const navLinkType = React.useContext(NavLinkTypeContext);
|
|
|
|
|
if (navLinkType === "button") {
|
|
|
|
|
return <NavButton {...props} />;
|
|
|
|
|
} else if (navLinkType === "menu") {
|
|
|
|
|
return <MenuItem {...props} />;
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(`unexpected navLinkType: ${JSON.stringify(navLinkType)}`);
|
|
|
|
|
}
|
2020-09-21 03:31:49 -07:00
|
|
|
}
|
|
|
|
|
|
2020-09-10 02:32:14 -07:00
|
|
|
const NavButton = React.forwardRef(({ icon, ...props }, ref) => {
|
|
|
|
|
const Component = icon ? IconButton : Button;
|
|
|
|
|
|
|
|
|
|
// Opacity is in a separate Box, to avoid overriding the built-in Button
|
|
|
|
|
// hover/focus states.
|
|
|
|
|
return (
|
2020-09-19 22:10:52 -07:00
|
|
|
<Box
|
|
|
|
|
opacity="0.8"
|
|
|
|
|
_hover={{ opacity: "1" }}
|
|
|
|
|
_focusWithin={{ opacity: "1" }}
|
|
|
|
|
>
|
2020-09-10 02:32:14 -07:00
|
|
|
<Component size="sm" variant="outline" icon={icon} ref={ref} {...props} />
|
|
|
|
|
</Box>
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2020-09-21 03:08:24 -07:00
|
|
|
export default GlobalHeader;
|