Skip to content

Commit

Permalink
add farcaster loader mobile, types, and hide modal for email
Browse files Browse the repository at this point in the history
  • Loading branch information
pvicensSpacedev committed May 7, 2024
1 parent 424af09 commit 983d05a
Show file tree
Hide file tree
Showing 15 changed files with 146 additions and 24 deletions.
5 changes: 4 additions & 1 deletion apps/mobile/src/components/BottomSheetRow.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import clsx from 'clsx';
import { View } from 'react-native';
import { ActivityIndicator, View } from 'react-native';

import { GalleryElementTrackingProps } from '~/shared/contexts/AnalyticsContext';

Expand All @@ -15,6 +15,7 @@ type BottomSheetRowProps = {
fontWeight?: 'Regular' | 'Bold';
rightIcon?: React.ReactNode;
eventContext: GalleryElementTrackingProps['eventContext'];
isLoading?: boolean;
};

export function BottomSheetRow({
Expand All @@ -26,6 +27,7 @@ export function BottomSheetRow({
fontWeight = 'Regular',
rightIcon,
eventContext,
isLoading,
}: BottomSheetRowProps) {
return (
<GalleryTouchableOpacity
Expand All @@ -48,6 +50,7 @@ export function BottomSheetRow({
{text}
</Typography>
{rightIcon && <View className="ml-auto">{rightIcon}</View>}
{isLoading && <ActivityIndicator className="ml-auto" />}
</View>
</GalleryTouchableOpacity>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ export function FarcasterAuthProvider({ children }: { children: ReactNode }) {
return <AuthKitProvider config={config}>{children}</AuthKitProvider>;
}

export function useLoginWithFarcaster() {
type FarcasterBottomSheetRowProps = {
setIsFarcasterLoading: (isLoading: boolean) => void;
};

export function useLoginWithFarcaster({ setIsFarcasterLoading }: FarcasterBottomSheetRowProps) {
const { hideBottomSheetModal } = useBottomSheetModalActions();

const getUsersByWalletAddresses = useGetUsersByWalletAddressesImperatively();
Expand All @@ -53,6 +57,7 @@ export function useLoginWithFarcaster() {

const handleFarcasterLoginError = useCallback(
(error?: AuthClientError | Error) => {
setIsFarcasterLoading(false);
const errorMessage = error?.message ?? 'unknown error';
pushToast({
message: `There was an error signing in with Farcaster: ${errorMessage}`,
Expand All @@ -64,11 +69,12 @@ export function useLoginWithFarcaster() {
});
hasRedirectedToWarpcast.current = false;
},
[pushToast, reportError]
[pushToast, reportError, setIsFarcasterLoading]
);

const handleSuccess = useCallback(
async (req: StatusAPIResponse) => {
setIsFarcasterLoading(false);
try {
if (!req.custody) {
throw new Error('no custody address produced from farcaster');
Expand Down Expand Up @@ -115,6 +121,7 @@ export function useLoginWithFarcaster() {
navigation.navigate('OnboardingEmail', {
authMethod: 'Farcaster',
authMechanism: createUserAuthMechanism,
farcasterUsername: req.username,
});
return;
}
Expand All @@ -129,6 +136,7 @@ export function useLoginWithFarcaster() {
pubKey: req.custody,
chain: 'Ethereum' as Chain,
},
farcasterUsername: req.username || '',
},
};
if (req.verifications?.[0]) {
Expand All @@ -152,6 +160,7 @@ export function useLoginWithFarcaster() {
hideBottomSheetModal();
await navigateToNotificationUpsellOrHomeScreen(navigation);
} catch (e) {
setIsFarcasterLoading(false);
if (e instanceof Error) {
handleFarcasterLoginError(e);
}
Expand All @@ -163,6 +172,7 @@ export function useLoginWithFarcaster() {
hideBottomSheetModal,
login,
navigation,
setIsFarcasterLoading,
track,
]
);
Expand All @@ -182,19 +192,44 @@ export function useLoginWithFarcaster() {
nonce,
});

const initiateConnection = useCallback(async () => {
if (!isConnected) {
const { nonce } = await createNonce();
setNonce(nonce);
const initiateConnection = useCallback(
async (shouldAttemptReconnect = false) => {
setIsFarcasterLoading(true);

if (isConnectError) {
if (shouldAttemptReconnect && !isSuccess && url) {
reconnect();
const { nonce } = await createNonce();
setNonce(nonce);
signIn();
Linking.openURL(url);
return;
}

await connect();
}
}, [connect, createNonce, isConnectError, isConnected, reconnect]);
if (!isConnected) {
const { nonce } = await createNonce();
setNonce(nonce);

if (isConnectError) {
reconnect();
setIsFarcasterLoading(false);
return;
}

await connect();
}
},
[
connect,
createNonce,
isConnectError,
isConnected,
isSuccess,
reconnect,
setIsFarcasterLoading,
signIn,
url,
]
);

useEffect(() => {
if (url && nonce.length && !isPolling && !isSuccess && !hasRedirectedToWarpcast.current) {
Expand All @@ -208,5 +243,8 @@ export function useLoginWithFarcaster() {

return {
open: initiateConnection,
isSuccess,
isPolling,
isConnected,
};
}
41 changes: 37 additions & 4 deletions apps/mobile/src/components/Login/SignInBottomSheet.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useNavigation } from '@react-navigation/native';
import { forwardRef, useCallback } from 'react';
import { View } from 'react-native';
import { forwardRef, useCallback, useEffect, useState } from 'react';
import { AppState, View } from 'react-native';
import { EmailIcon } from 'src/icons/EmailIcon';
import { FarcasterOutlineIcon } from 'src/icons/FarcasterOutlineIcon';
import { QRCodeIcon } from 'src/icons/QRCodeIcon';
Expand Down Expand Up @@ -87,13 +87,46 @@ function SignInBottomSheet({ onQrCodePress, openManageWallet }: Props) {
}

function FarcasterBottomSheetRow() {
const { open: handleConnectFarcaster } = useLoginWithFarcaster();
const [isFarcasterLoading, setIsFarcasterLoading] = useState(false);
const [attemptReconnect, setAttemptReconnect] = useState(false);

const {
open: handleConnectFarcaster,
isSuccess,
isConnected,
isPolling,
} = useLoginWithFarcaster({
setIsFarcasterLoading,
});

const [appState, setAppState] = useState(AppState.currentState);

useEffect(() => {
const subscription = AppState.addEventListener('change', (nextAppState) => {
if (appState.match(/inactive|background/) && nextAppState === 'active') {
if (isPolling || (!isConnected && !isSuccess)) {
setAttemptReconnect(true);
}
setIsFarcasterLoading(false);
}
setAppState(nextAppState);
});

return () => {
subscription.remove();
};
}, [appState, isPolling, isConnected, isSuccess]);

const handleConnectFarcasterPress = useCallback(() => {
handleConnectFarcaster(attemptReconnect);
}, [attemptReconnect, handleConnectFarcaster]);

return (
<BottomSheetRow
icon={<FarcasterOutlineIcon />}
text="Farcaster"
onPress={handleConnectFarcaster}
isLoading={isFarcasterLoading}
onPress={handleConnectFarcasterPress}
eventContext={contexts.Authentication}
fontWeight="Bold"
/>
Expand Down
3 changes: 3 additions & 0 deletions apps/mobile/src/navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,19 +130,22 @@ export type LoginStackNavigatorParamList = {
OnboardingEmail: {
authMethod: AuthMethodTitle;
authMechanism?: AuthPayloadVariables;
farcasterUsername?: string;
};

Onboarding2FA: {
authMethod: AuthMethodTitle;
email: string;
authMechanism: AuthPayloadVariables;
loginWithCode: LoginWithEmailHookResult['loginWithCode'];
farcasterUsername?: string;
};

OnboardingUsername: {
authMechanism: AuthPayloadVariables;
email?: string;
authMethod: AuthMethodTitle;
farcasterUsername?: string;
};

OnboardingProfileBio: undefined;
Expand Down
4 changes: 3 additions & 1 deletion apps/mobile/src/screens/Login/Onboarding2FAScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function InnerOnboardingEmailScreen() {
const navigation = useNavigation<LoginStackNavigatorProp>();
const route = useRoute<RouteProp<LoginStackNavigatorParamList, 'Onboarding2FA'>>();

const { authMethod, authMechanism, email, loginWithCode } = route.params;
const { authMethod, authMechanism, email, loginWithCode, farcasterUsername } = route.params;

const { bottom } = useSafeAreaInsets();

Expand Down Expand Up @@ -91,6 +91,7 @@ function InnerOnboardingEmailScreen() {
privyToken: token,
},
email,
farcasterUsername,
});
}
} else {
Expand Down Expand Up @@ -120,6 +121,7 @@ function InnerOnboardingEmailScreen() {
loginWithCode,
navigation,
track,
farcasterUsername,
]);

const handleBack = useCallback(() => {
Expand Down
14 changes: 13 additions & 1 deletion apps/mobile/src/screens/Login/OnboardingEmailScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ function InnerOnboardingEmailScreen() {

const authMethod = route.params.authMethod;
const authMechanism = route.params.authMechanism;
const farcasterUsername = route.params.farcasterUsername;

const { bottom } = useSafeAreaInsets();

Expand Down Expand Up @@ -129,6 +130,7 @@ function InnerOnboardingEmailScreen() {
authMechanism,
loginWithCode,
email,
farcasterUsername,
});
}
} catch (error) {
Expand All @@ -141,7 +143,17 @@ function InnerOnboardingEmailScreen() {
} finally {
setIsLoggingIn(false);
}
}, [authMechanism, authMethod, email, loginWithCode, navigation, reportError, sendCode, track]);
}, [
authMechanism,
authMethod,
email,
loginWithCode,
navigation,
reportError,
sendCode,
track,
farcasterUsername,
]);

const handleBack = useCallback(() => {
navigation.goBack();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,11 @@ function InnerOnboardingUsernameScreen() {
const email = route.params.email;
const authMethod = route.params.authMethod;
const authMechanism = route.params.authMechanism;
const farcasterUsername = route.params.farcasterUsername;

const [username, setUsername] = useState(user?.username ?? '');
const [username, setUsername] = useState(
farcasterUsername ? farcasterUsername : user?.username ?? ''
);
const [bio] = useState('');

// This cannot be derived from a "null" `usernameError`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export function FarcasterLoginView() {
pubKey: primaryFarcasterAddress,
chain: 'Ethereum' as Chain,
},
farcasterUsername: req.username || '',
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export default function useLoginOrRedirectToOnboarding() {
message: authMechanism.mechanism.neynar.message,
signature: authMechanism.mechanism.neynar.signature,
userFriendlyWalletName,
farcasterUsername: authMechanism.mechanism.neynar.farcasterUsername,
},
},
'/onboarding/add-email'
Expand Down
8 changes: 6 additions & 2 deletions apps/web/src/contexts/modal/AnimatedModal.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { usePrivy } from '@privy-io/react-auth';
import { ReactElement, useCallback, useEffect, useMemo } from 'react';
import styled, { css, keyframes } from 'styled-components';

Expand Down Expand Up @@ -92,10 +93,12 @@ function AnimatedModal({
hideModal();
}, [onCloseOverride, hideModal]);

const { isModalOpen } = usePrivy();

return (
<_ToggleFade isActive={isActive}>
<Overlay onClick={handleClick} />
<StyledModal isFullPage={isFullPage}>
<StyledModal isFullPage={isFullPage} isPrivyModalOpened={isModalOpen}>
<_ToggleTranslate isActive={isActive}>
<StyledContainer isFullPage={isFullPage} maxWidth={maxWidth} width={width}>
<StyledHeader isPaddingDisabled={!headerText && isPaddingDisabled}>
Expand Down Expand Up @@ -172,7 +175,8 @@ const Overlay = styled.div`
-webkit-backface-visibility: hidden;
`;

const StyledModal = styled.div<{ isFullPage: boolean }>`
const StyledModal = styled.div<{ isFullPage: boolean; isPrivyModalOpened?: boolean }>`
opacity: ${(props) => (props.isPrivyModalOpened ? '0' : '1')};
position: fixed;
top: 50%;
left: 50%;
Expand Down
6 changes: 6 additions & 0 deletions apps/web/src/hooks/api/users/useAuthPayloadQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export function isEoaPayload(payload: AuthPayloadVariables): payload is EoaPaylo
return payload.authMechanismType === 'eoa';
}

export function isNeynarPayload(payload: AuthPayloadVariables): payload is NeynarPayloadVariables {
return 'authMechanismType' in payload && payload.authMechanismType === 'neynar';
}

export default function useAuthPayloadQuery(): AuthPayloadVariables | null {
const { query } = useRouter();

Expand Down Expand Up @@ -71,6 +75,8 @@ export default function useAuthPayloadQuery(): AuthPayloadVariables | null {
address: query.address,
primaryAddress: query.primaryAddress,
email: typeof query.email === 'string' ? query.email : undefined,
farcasterUsername:
typeof query.farcasterUsername === 'string' ? query.farcasterUsername : undefined,
};
}

Expand Down
Loading

0 comments on commit 983d05a

Please sign in to comment.