Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: uniform post page login #4046

Merged
merged 27 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3747050
redirect on social click
AmarTrebinjac Jan 6, 2025
b5ea268
handle email case
AmarTrebinjac Jan 6, 2025
83c768f
add logic for banner as well
AmarTrebinjac Jan 6, 2025
850e9ad
linting memes
AmarTrebinjac Jan 6, 2025
b8fa471
Merge branch 'main' into MI-696
AmarTrebinjac Jan 6, 2025
585d762
lint
AmarTrebinjac Jan 6, 2025
49ca5c5
linter decided to not work anymore :)
AmarTrebinjac Jan 6, 2025
9d1bcab
update loginbutton test
AmarTrebinjac Jan 6, 2025
6f86e2e
update trigger type
AmarTrebinjac Jan 6, 2025
f868195
import as type
AmarTrebinjac Jan 6, 2025
78c0eb4
Merge branch 'main' into MI-696
AmarTrebinjac Jan 7, 2025
7a9a3c3
Merge branch 'main' into MI-696
AmarTrebinjac Jan 16, 2025
a5699c1
Merge branch 'main' into MI-696
AmarTrebinjac Jan 18, 2025
d56e3cd
keep old params
AmarTrebinjac Jan 18, 2025
a50ef4a
delete authmodal
AmarTrebinjac Jan 18, 2025
dd57779
useAuthContext
AmarTrebinjac Jan 20, 2025
ff3fac6
Merge branch 'main' into MI-696
AmarTrebinjac Jan 20, 2025
1ca32d1
Merge branch 'main' into MI-696
AmarTrebinjac Jan 21, 2025
a34d028
refactor showLogin
AmarTrebinjac Jan 21, 2025
ebfe354
add after auth param key
AmarTrebinjac Jan 21, 2025
96c482f
remove toString on params
AmarTrebinjac Jan 21, 2025
1d8c0cd
Merge branch 'main' into MI-696
AmarTrebinjac Jan 21, 2025
0998583
remove auth trigger param from onboarding
AmarTrebinjac Jan 21, 2025
9997cde
redirect correclty to signin page
AmarTrebinjac Jan 21, 2025
333771f
update test
AmarTrebinjac Jan 21, 2025
382d5c3
Merge branch 'main' into MI-696
AmarTrebinjac Jan 22, 2025
f275c6d
lint
AmarTrebinjac Jan 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 2 additions & 21 deletions packages/extension/src/newtab/App.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import type { ReactElement } from 'react';
import React, { useCallback, useContext, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import dynamic from 'next/dynamic';
import Modal from 'react-modal';
import 'focus-visible';
import { ProgressiveEnhancementContextProvider } from '@dailydotdev/shared/src/contexts/ProgressiveEnhancementContext';
import AuthContext, {
useAuthContext,
} from '@dailydotdev/shared/src/contexts/AuthContext';
import { useAuthContext } from '@dailydotdev/shared/src/contexts/AuthContext';
import { SubscriptionContextProvider } from '@dailydotdev/shared/src/contexts/SubscriptionContext';
import browser from 'webextension-polyfill';
import type { BootDataProviderProps } from '@dailydotdev/shared/src/contexts/BootProvider';
Expand Down Expand Up @@ -53,13 +50,6 @@ structuredCloneJsonPolyfill();
const DEFAULT_TAB_TITLE = 'New Tab';
const router = new CustomRouter();
const queryClient = new QueryClient(defaultQueryClientConfig);
const AuthModal = dynamic(
() =>
import(
/* webpackChunkName: "authModal" */ '@dailydotdev/shared/src/components/auth/AuthModal'
),
);

Modal.setAppElement('#__next');
Modal.defaultStyles = {};

Expand All @@ -75,7 +65,6 @@ function InternalApp(): ReactElement {
null,
);
const { unreadCount } = useNotificationContext();
const { closeLogin, shouldShowLogin, loginState } = useContext(AuthContext);
const { contentScriptGranted } = useContentScriptStatus();
const { hostGranted, isFetching: isCheckingHostPermissions } =
useHostStatus();
Expand Down Expand Up @@ -147,14 +136,6 @@ function InternalApp(): ReactElement {
return (
<DndContextProvider>
<MainFeedPage onPageChanged={onPageChanged} />
{shouldShowLogin && (
<AuthModal
isOpen={shouldShowLogin}
onRequestClose={closeLogin}
contentLabel="Login Modal"
{...loginState}
/>
)}
</DndContextProvider>
);
}
Expand Down
54 changes: 46 additions & 8 deletions packages/shared/src/components/LoginButton.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,73 @@
/* eslint-disable no-console */
import React from 'react';
import type { RenderResult } from '@testing-library/react';
import { fireEvent, render, screen } from '@testing-library/react';
import { fireEvent, render, screen, act } from '@testing-library/react';
import { QueryClient } from '@tanstack/react-query';
import LoginButton from './LoginButton';
import { TestBootProvider } from '../../__tests__/helpers/boot';
import type { LoggedUser } from '../lib/user';
import { AuthContextProvider } from '../contexts/AuthContext';
import { AuthTriggers } from '../lib/auth';

describe('LoginButton', () => {
const showLogin = jest.fn();
const logEvent = jest.fn();

beforeEach(() => {
showLogin.mockReset();
logEvent.mockReset();
});

const renderLayout = (user: LoggedUser = null): RenderResult => {
const client = new QueryClient();

return render(
<TestBootProvider client={client} auth={{ user, showLogin }}>
<LoginButton />
</TestBootProvider>,
<AuthContextProvider
user={user}
updateUser={jest.fn()}
tokenRefreshed
getRedirectUri={jest.fn()}
loadingUser={false}
loadedUserFromCache
>
<TestBootProvider client={client} auth={{ user, showLogin }}>
<LoginButton />
</TestBootProvider>
</AuthContextProvider>,
);
};

it('should show login when clicking on the button', async () => {
it('should call showLogin when clicking the login button', async () => {
renderLayout();
const el = await screen.findByText('Log in');
const loginButton = await screen.findByText('Log in');
expect(loginButton).toBeInTheDocument();

fireEvent.click(el);
await act(async () => {
fireEvent.click(loginButton);
});

expect(showLogin).toBeCalledTimes(1);
expect(showLogin).toHaveBeenCalledWith({
trigger: AuthTriggers.MainButton,
options: {
isLogin: true,
},
});
});

it('should call showLogin when clicking the signup button', async () => {
renderLayout();
const signupButton = await screen.findByText('Sign up');
expect(signupButton).toBeInTheDocument();

await act(async () => {
fireEvent.click(signupButton);
});

expect(showLogin).toHaveBeenCalledWith({
trigger: AuthTriggers.MainButton,
options: {
isLogin: false,
},
});
});
});
11 changes: 6 additions & 5 deletions packages/shared/src/components/LoginButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import type { ReactElement } from 'react';
import React, { useContext } from 'react';
import classNames from 'classnames';
import { Button, ButtonVariant } from './buttons/Button';
import AuthContext from '../contexts/AuthContext';
import LogContext from '../contexts/LogContext';
import type { LogEvent } from '../hooks/log/useLogQueue';
import { AuthTriggers } from '../lib/auth';
import { TargetType } from '../lib/log';
import { useAuthContext } from '../contexts/AuthContext';
import { AuthTriggers } from '../lib/auth';

interface ClassName {
container?: string;
Expand All @@ -33,14 +33,15 @@ const getLogEvent = (copy: ButtonCopy): LogEvent => ({
export default function LoginButton({
className = {},
}: LoginButtonProps): ReactElement {
const { showLogin } = useContext(AuthContext);
const { logEvent } = useContext(LogContext);

const { showLogin } = useAuthContext();
const onClick = (copy: ButtonCopy) => {
logEvent(getLogEvent(copy));
showLogin({
trigger: AuthTriggers.MainButton,
options: { isLogin: copy === ButtonCopy.Login },
options: {
isLogin: copy === ButtonCopy.Login,
},
});
};

Expand Down
88 changes: 0 additions & 88 deletions packages/shared/src/components/auth/AuthModal.tsx

This file was deleted.

12 changes: 4 additions & 8 deletions packages/shared/src/components/auth/AuthenticationBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,12 @@ export function AuthenticationBanner({
className={{
onboardingSignup: '!gap-4',
}}
onAuthStateUpdate={(props) =>
onAuthStateUpdate={(props) => {
showLogin({
trigger: AuthTriggers.Onboarding,
options: {
formValues: {
email: props?.email,
},
},
})
}
options: { isLogin: true, formValues: props },
});
}}
onboardingSignupButton={{
variant: ButtonVariant.Primary,
}}
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/src/components/auth/common.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export interface Provider {
value: string;
}

export const AFTER_AUTH_PARAM = 'after_auth';

export enum SocialProvider {
// Twitter = 'twitter',
Facebook = 'facebook',
Expand Down
31 changes: 21 additions & 10 deletions packages/shared/src/contexts/AuthContext.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ReactElement, ReactNode } from 'react';
import React, { useCallback, useContext, useState } from 'react';
import type { QueryObserverResult } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import type { AnonymousUser, LoggedUser } from '../lib/user';
import {
deleteAccount,
Expand All @@ -13,6 +14,7 @@ import type { AuthTriggersType } from '../lib/auth';
import { AuthTriggers } from '../lib/auth';
import type { Squad } from '../graphql/sources';
import { checkIsExtension, isNullOrUndefined } from '../lib/func';
import { AFTER_AUTH_PARAM } from '../components/auth/common';
import { Continent, outsideGdpr } from '../lib/geo';

export interface LoginState {
Expand Down Expand Up @@ -140,7 +142,7 @@ export const AuthContextProvider = ({
const endUser = user && 'providers' in user ? user : null;
const referral = user?.referralId || user?.referrer;
const referralOrigin = user?.referralOrigin;

const router = useRouter();
if (firstLoad === true && endUser && !endUser?.infoConfirmed) {
logout(LogoutReason.IncomleteOnboarding);
}
Expand All @@ -160,15 +162,24 @@ export const AuthContextProvider = ({
firstVisit: user?.firstVisit,
trackingId: user?.id,
shouldShowLogin: loginState !== null,
showLogin: useCallback(({ trigger, options = {} }) => {
const hasCompanion = !!isCompanionActivated();
if (hasCompanion) {
const signup = `${process.env.NEXT_PUBLIC_WEBAPP_URL}signup?close=true`;
window.open(signup);
} else {
setLoginState({ ...options, trigger });
}
}, []),
showLogin: useCallback(
({ trigger, options = {} }) => {
const hasCompanion = !!isCompanionActivated();
if (hasCompanion) {
const signup = `${process.env.NEXT_PUBLIC_WEBAPP_URL}signup?close=true`;
window.open(signup);
} else {
const params = new URLSearchParams(globalThis?.location.search);

setLoginState({ ...options, trigger });
if (!params.get(AFTER_AUTH_PARAM)) {
params.set(AFTER_AUTH_PARAM, window.location.pathname);
}
router.push(`/onboarding?${params.toString()}`);
}
},
[router],
),
closeLogin: useCallback(() => setLoginState(null), []),
loginState,
updateUser,
Expand Down
10 changes: 8 additions & 2 deletions packages/shared/src/hooks/useRegistration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { getUserDefaultTimezone } from '../lib/timezones';
import LogContext from '../contexts/LogContext';
import { Origin } from '../lib/log';
import { LogoutReason } from '../lib/user';
import { AFTER_AUTH_PARAM } from '../components/auth/common';

type ParamKeys = keyof RegistrationParameters;

Expand Down Expand Up @@ -86,11 +87,16 @@ const useRegistration = ({
origin: Origin.InitializeRegistrationFlow,
}),
});
const params = new URLSearchParams(window.location.search);
const afterAuth = params.get(AFTER_AUTH_PARAM);
/**
* In case a valid session exists on kratos, but not FE we should logout the user
* In case a valid session exists on kratos, but not FE we should logout the user.
* We ignore it if 'after_auth' param exists, because it means we manually redirected the user here, and that will trigger this error.
*/
if (
registration.error?.id === KRATOS_ERROR_MESSAGE.SESSION_ALREADY_AVAILABLE
registration.error?.id ===
KRATOS_ERROR_MESSAGE.SESSION_ALREADY_AVAILABLE &&
!afterAuth
) {
logout(LogoutReason.KratosSessionAlreadyAvailable);
}
Expand Down
Loading
Loading