Skip to content

Commit

Permalink
fix onboarding flash and progress animation (#2471)
Browse files Browse the repository at this point in the history
* fix onboarding flash and progress animation

* change provider name and add usememo
  • Loading branch information
pvicensSpacedev authored Jun 6, 2024
1 parent 48836b7 commit 7687292
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 73 deletions.
23 changes: 11 additions & 12 deletions apps/web/src/components/Onboarding/FullPageCenteredStep.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import { motion } from 'framer-motion';
import { ReactNode } from 'react';
import { ReactNode, useEffect } from 'react';
import colors from 'shared/theme/colors';
import styled from 'styled-components';

import {
FOOTER_HEIGHT,
ONBOARDING_PROGRESS_BAR_STEPS,
StepName,
} from '~/components/Onboarding/constants';
import { FOOTER_HEIGHT, StepName } from '~/components/Onboarding/constants';
import { useProgress } from '~/contexts/onboardingProgress';
import GalleryRoute from '~/scenes/_Router/GalleryRoute';

import { ANIMATED_COMPONENT_TRANSITION_S, rawTransitions } from '../core/transitions';

type Props = {
children: ReactNode | ReactNode[];
// If this is true, the page height will be reduced to account for the footer height
withFooter?: boolean;
stepName: StepName;
};
Expand All @@ -23,13 +19,17 @@ type Props = {
* A helper component to easily generate full-page steps where the content is centered
*/
export default function FullPageCenteredStep({ children, withFooter, stepName }: Props) {
const { from, to } = ONBOARDING_PROGRESS_BAR_STEPS[stepName] ?? { from: 0, to: 0 };
const { to, setProgress } = useProgress();

useEffect(() => {
setProgress(stepName);
}, [setProgress, stepName]);

return (
<GalleryRoute
element={
<StyledPage withFooter={withFooter}>
<OnboardingProgressBar from={from} to={to} />
<OnboardingProgressBar to={to} />
{children}
</StyledPage>
}
Expand All @@ -49,14 +49,13 @@ const StyledPage = styled.div<{ withFooter?: boolean }>`
`;

type OnboardingProgressBarProps = {
from: number;
to: number;
};

function OnboardingProgressBar({ from, to }: OnboardingProgressBarProps) {
function OnboardingProgressBar({ to }: OnboardingProgressBarProps) {
return (
<StyledBar
initial={{ width: from }}
initial={{ width: `${to}%` }}
animate={{ width: `${to}%` }}
transition={{
duration: ANIMATED_COMPONENT_TRANSITION_S,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { startTransition, useCallback, useContext } from 'react';
import { graphql } from 'relay-runtime';

import { useModalActions } from '~/contexts/modal/ModalContext';
import { useProgress } from '~/contexts/onboardingProgress';
import { RelayResetContext } from '~/contexts/RelayResetContext';
import {
useLoginOrRedirectToOnboardingMutation,
Expand Down Expand Up @@ -44,6 +45,7 @@ export default function useLoginOrRedirectToOnboarding() {

const { push } = useRouter();

const { setProgress } = useProgress();
const { hideModal } = useModalActions();
const reset = useContext(RelayResetContext);
const mutate = useCallback(
Expand Down Expand Up @@ -97,6 +99,7 @@ export default function useLoginOrRedirectToOnboarding() {
},
'/onboarding/add-email'
);
setProgress('add-email');
}

if (authMechanism.mechanism.neynar) {
Expand All @@ -115,6 +118,7 @@ export default function useLoginOrRedirectToOnboarding() {
},
'/onboarding/add-email'
);
setProgress('add-email');
}

// TODO: this should be privy
Expand All @@ -131,6 +135,7 @@ export default function useLoginOrRedirectToOnboarding() {
},
'/onboarding/add-username'
);
setProgress('add-username');
}

// De-prioritizing gnosis safe logins for now
Expand Down
105 changes: 54 additions & 51 deletions apps/web/src/contexts/AppProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import GlobalLayoutContextProvider from './globalLayout/GlobalLayoutContext';
import SidebarDrawerProvider from './globalLayout/GlobalSidebar/SidebarDrawerContext';
import ModalProvider from './modal/ModalContext';
import NftPreviewFallbackProvider from './nftPreviewFallback/NftPreviewFallbackContext';
import { OnboardingProgressProvider } from './onboardingProgress';
import PostComposerProvider from './postComposer/PostComposerContext';
import SnowProvider from './snow/SnowContext';
import { SwrProvider } from './swr/SwrContext';
Expand Down Expand Up @@ -53,56 +54,58 @@ export default function AppProvider({
globalLayoutContextPreloadedQuery,
}: Props) {
return (
<Boundary>
<ToastProvider>
<MaintenanceStatusProvider
sanityProjectId={process.env.NEXT_PUBLIC_SANITY_PROJECT_ID}
MaintenancePageComponent={<MaintenancePage />}
>
<Boundary>
<RelayEnvironmentProvider environment={relayEnvironment}>
<PrivyProvider
appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID ?? ''}
config={privyConfig}
>
<AnalyticsProvider>
<WebErrorReportingProvider>
<SwrProvider>
<GalleryNavigationProvider>
<NftPreviewFallbackProvider>
<NftErrorProvider>
<SyncTokensLockProvider>
<PostComposerProvider>
<ModalProvider>
<SidebarDrawerProvider>
<SearchProvider>
<SnowProvider enabled={false}>
<GlobalLayoutContextProvider
preloadedQuery={globalLayoutContextPreloadedQuery}
>
<BottomSheetProvider>
<FullPageNftDetailModalListener />
{isProd ? null : <Debugger />}
{children}
</BottomSheetProvider>
</GlobalLayoutContextProvider>
</SnowProvider>
</SearchProvider>
</SidebarDrawerProvider>
</ModalProvider>
</PostComposerProvider>
</SyncTokensLockProvider>
</NftErrorProvider>
</NftPreviewFallbackProvider>
</GalleryNavigationProvider>
</SwrProvider>
</WebErrorReportingProvider>
</AnalyticsProvider>
</PrivyProvider>
</RelayEnvironmentProvider>
</Boundary>
</MaintenanceStatusProvider>
</ToastProvider>
</Boundary>
<OnboardingProgressProvider>
<Boundary>
<ToastProvider>
<MaintenanceStatusProvider
sanityProjectId={process.env.NEXT_PUBLIC_SANITY_PROJECT_ID}
MaintenancePageComponent={<MaintenancePage />}
>
<Boundary>
<RelayEnvironmentProvider environment={relayEnvironment}>
<PrivyProvider
appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID ?? ''}
config={privyConfig}
>
<AnalyticsProvider>
<WebErrorReportingProvider>
<SwrProvider>
<GalleryNavigationProvider>
<NftPreviewFallbackProvider>
<NftErrorProvider>
<SyncTokensLockProvider>
<PostComposerProvider>
<ModalProvider>
<SidebarDrawerProvider>
<SearchProvider>
<SnowProvider enabled={false}>
<GlobalLayoutContextProvider
preloadedQuery={globalLayoutContextPreloadedQuery}
>
<BottomSheetProvider>
<FullPageNftDetailModalListener />
{isProd ? null : <Debugger />}
{children}
</BottomSheetProvider>
</GlobalLayoutContextProvider>
</SnowProvider>
</SearchProvider>
</SidebarDrawerProvider>
</ModalProvider>
</PostComposerProvider>
</SyncTokensLockProvider>
</NftErrorProvider>
</NftPreviewFallbackProvider>
</GalleryNavigationProvider>
</SwrProvider>
</WebErrorReportingProvider>
</AnalyticsProvider>
</PrivyProvider>
</RelayEnvironmentProvider>
</Boundary>
</MaintenanceStatusProvider>
</ToastProvider>
</Boundary>
</OnboardingProgressProvider>
);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useRouter } from 'next/router';
import { useMemo } from 'react';

/**
* The main purpose of this hook is to prevent triggering side effects that are normally
Expand All @@ -7,13 +8,19 @@ import { useRouter } from 'next/router';
* 2) keeping modals open across certain route changes
* 3) preventing scroll reset across certain route changes
*
* NOTE: This used to be a massive conditional block but turns out we can `pathname` is
* the magic value that remains stable and provides the behavior we're looking for
* NOTE: lots of things can be improved about this, but it's too much of a headache to fix rn
*/
export default function useStabilizedRouteTransitionKey() {
const { pathname } = useRouter();
const { asPath, pathname } = useRouter();

const transitionAnimationKey = pathname;
const transitionAnimationKey = useMemo(() => {
// keep location stable for paths containing "onboarding"
if (pathname.includes('onboarding')) {
return 'onboarding';
}

return asPath;
}, [asPath, pathname]);

return transitionAnimationKey;
}
36 changes: 36 additions & 0 deletions apps/web/src/contexts/onboardingProgress/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';

import { ONBOARDING_PROGRESS_BAR_STEPS, StepName } from '~/components/Onboarding/constants';

type ProgressContextType = {
from: number;
to: number;
setProgress: (stepName: StepName) => void;
};

const ProgressContext = createContext<ProgressContextType | undefined>(undefined);

export const OnboardingProgressProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [progress, setProgressState] = useState({ from: 0, to: 0 });

const setProgress = useCallback((stepName: StepName) => {
const step = ONBOARDING_PROGRESS_BAR_STEPS[stepName];
if (step) {
setProgressState({ from: step.from, to: step.to });
}
}, []);

const contextValue = useMemo(() => ({ ...progress, setProgress }), [progress, setProgress]);

return <ProgressContext.Provider value={contextValue}>{children}</ProgressContext.Provider>;
};

export const useProgress = () => {
const context = useContext(ProgressContext);
if (!context) {
throw new Error('useProgress must be used within a OnboardingProgressProvider');
}
return context;
};
10 changes: 7 additions & 3 deletions apps/web/src/scenes/Onboarding/OnboardingAddBioPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useRouter } from 'next/router';
import { useCallback, useMemo, useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { graphql, useLazyLoadQuery } from 'react-relay';
import useUpdateUser, { BIO_MAX_CHAR_COUNT } from 'shared/hooks/useUpdateUser';
import colors from 'shared/theme/colors';
Expand All @@ -13,6 +13,7 @@ import { useNftSelectorForProfilePicture } from '~/components/NftSelector/useNft
import FullPageCenteredStep from '~/components/Onboarding/FullPageCenteredStep';
import { OnboardingFooter } from '~/components/Onboarding/OnboardingFooter';
import { ProfilePicture } from '~/components/ProfilePicture/ProfilePicture';
import { useProgress } from '~/contexts/onboardingProgress';
import { OnboardingAddBioPageQuery } from '~/generated/OnboardingAddBioPageQuery.graphql';
import unescape from '~/shared/utils/unescape';

Expand Down Expand Up @@ -47,6 +48,7 @@ export function OnboardingAddBioPage() {
const showNftSelector = useNftSelectorForProfilePicture();
const updateUser = useUpdateUser();
const { push } = useRouter();
const { setProgress } = useProgress();

const user = query.viewer?.user;
const farcasterBio = user?.socialAccounts?.farcaster?.bio;
Expand All @@ -73,19 +75,21 @@ export function OnboardingAddBioPage() {
push({
pathname: '/onboarding/add-persona',
});
setProgress('add-persona');
}
} catch (error: unknown) {
if (error instanceof Error) {
setError(error.message);
}
}
}, [bio, updateUser, push, user?.dbid, user?.username]);
}, [bio, updateUser, push, user?.dbid, user?.username, setProgress]);

const handleToUsernameStep = useCallback(() => {
push({
pathname: '/onboarding/add-username',
});
}, [push]);
setProgress('add-username');
}, [push, setProgress]);

return (
<VStack>
Expand Down
5 changes: 4 additions & 1 deletion apps/web/src/scenes/Onboarding/OnboardingAddEmailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { HStack, VStack } from '~/components/core/Spacer/Stack';
import { BaseM, TitleDiatypeM } from '~/components/core/Text/Text';
import FullPageCenteredStep from '~/components/Onboarding/FullPageCenteredStep';
import { OnboardingFooter } from '~/components/Onboarding/OnboardingFooter';
import { useProgress } from '~/contexts/onboardingProgress';
import { useToastActions } from '~/contexts/toast/ToastContext';
import { OnboardingAddEmailPageQuery } from '~/generated/OnboardingAddEmailPageQuery.graphql';
import { EMAIL_FORMAT } from '~/shared/utils/regex';
Expand All @@ -24,6 +25,7 @@ export function OnboardingAddEmailPage() {
const debouncedEmail = useDebounce(email, 500);

const { push, query: queryRouter } = useRouter();
const { setProgress } = useProgress();

const isInvalidEmail = useMemo(() => {
return !EMAIL_FORMAT.test(email);
Expand All @@ -40,7 +42,8 @@ export function OnboardingAddEmailPage() {
},
'/onboarding/add-username'
);
}, [email, push, queryRouter]);
setProgress('add-username');
}, [email, push, queryRouter, setProgress]);

const handlePrevious = useCallback(() => {
push('/');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ import { NftSelector } from '~/components/NftSelector/NftSelector';
import useUpdateProfileImage from '~/components/NftSelector/useUpdateProfileImage';
import FullPageCenteredStep from '~/components/Onboarding/FullPageCenteredStep';
import { OnboardingFooter } from '~/components/Onboarding/OnboardingFooter';
import { useProgress } from '~/contexts/onboardingProgress';

const onboardingStepName = 'add-profile-picture';
export function OnboardingAddProfilePicturePage() {
const { setProfileImage } = useUpdateProfileImage();
const { push } = useRouter();
const { setProgress } = useProgress();

const redirectToNextStep = useCallback(() => {
push('/onboarding/add-user-info');
}, [push]);
setProgress('add-user-info');
}, [push, setProgress]);

const handleSelectToken = useCallback(
(tokenId: string) => {
Expand All @@ -27,7 +30,8 @@ export function OnboardingAddProfilePicturePage() {

const handlePrevious = useCallback(() => {
push('/onboarding/add-username');
}, [push]);
setProgress('add-username');
}, [push, setProgress]);

return (
<VStack>
Expand Down
Loading

0 comments on commit 7687292

Please sign in to comment.