computer-scienceangular-roadmapbackend-roadmapblockchain-roadmapdba-roadmapdeveloper-roadmapdevops-roadmapfrontend-roadmapgo-roadmaphactoberfestjava-roadmapjavascript-roadmapnodejs-roadmappython-roadmapqa-roadmapreact-roadmaproadmapstudy-planvue-roadmapweb3-roadmap
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
5.6 KiB
173 lines
5.6 KiB
import { useEffect, useState } from 'react'; |
|
import Cookies from 'js-cookie'; |
|
import { |
|
FIRST_LOGIN_PARAM, |
|
COURSE_PURCHASE_PARAM, |
|
TOKEN_COOKIE_NAME, |
|
setAuthToken, |
|
} from '../../lib/jwt'; |
|
import { cn } from '../../lib/classname.ts'; |
|
import { httpGet } from '../../lib/http'; |
|
import { LinkedInIcon } from '../ReactIcons/LinkedInIcon.tsx'; |
|
import { Spinner } from '../ReactIcons/Spinner.tsx'; |
|
import { CHECKOUT_AFTER_LOGIN_KEY } from './CourseLoginPopup.tsx'; |
|
import { triggerUtmRegistration } from '../../lib/browser.ts'; |
|
|
|
type LinkedInButtonProps = { |
|
isDisabled?: boolean; |
|
setIsDisabled?: (isDisabled: boolean) => void; |
|
className?: string; |
|
}; |
|
|
|
const LINKEDIN_REDIRECT_AT = 'linkedInRedirectAt'; |
|
const LINKEDIN_LAST_PAGE = 'linkedInLastPage'; |
|
|
|
export function LinkedInButton(props: LinkedInButtonProps) { |
|
const { isDisabled, setIsDisabled, className } = props; |
|
|
|
const [isLoading, setIsLoading] = useState(false); |
|
const [error, setError] = useState(''); |
|
|
|
useEffect(() => { |
|
const urlParams = new URLSearchParams(window.location.search); |
|
const code = urlParams.get('code'); |
|
const state = urlParams.get('state'); |
|
const provider = urlParams.get('provider'); |
|
|
|
if (!code || !state || provider !== 'linkedin') { |
|
return; |
|
} |
|
|
|
setIsLoading(true); |
|
setIsDisabled?.(true); |
|
httpGet<{ token: string; isNewUser: boolean }>( |
|
`${import.meta.env.PUBLIC_API_URL}/v1-linkedin-callback${ |
|
window.location.search |
|
}`, |
|
) |
|
.then(({ response, error }) => { |
|
if (!response?.token) { |
|
setError(error?.message || 'Something went wrong.'); |
|
setIsLoading(false); |
|
setIsDisabled?.(false); |
|
|
|
return; |
|
} |
|
|
|
triggerUtmRegistration(); |
|
|
|
let redirectUrl = new URL('/', window.location.origin); |
|
const linkedInRedirectAt = localStorage.getItem(LINKEDIN_REDIRECT_AT); |
|
const lastPageBeforeLinkedIn = localStorage.getItem(LINKEDIN_LAST_PAGE); |
|
|
|
// If the social redirect is there and less than 30 seconds old |
|
// redirect to the page that user was on before they clicked the github login button |
|
if (linkedInRedirectAt && lastPageBeforeLinkedIn) { |
|
const socialRedirectAtTime = parseInt(linkedInRedirectAt, 10); |
|
const now = Date.now(); |
|
const timeSinceRedirect = now - socialRedirectAtTime; |
|
|
|
if (timeSinceRedirect < 30 * 1000) { |
|
redirectUrl = new URL( |
|
lastPageBeforeLinkedIn, |
|
window.location.origin, |
|
); |
|
} |
|
} |
|
|
|
const authRedirectUrl = localStorage.getItem('authRedirect'); |
|
if (authRedirectUrl) { |
|
localStorage.removeItem('authRedirect'); |
|
redirectUrl = new URL(authRedirectUrl, window.location.origin); |
|
} |
|
|
|
if (response?.isNewUser) { |
|
redirectUrl.searchParams.set(FIRST_LOGIN_PARAM, '1'); |
|
} |
|
|
|
const shouldTriggerPurchase = |
|
localStorage.getItem(CHECKOUT_AFTER_LOGIN_KEY) !== '0'; |
|
if ( |
|
redirectUrl.pathname.includes('/courses/sql') && |
|
shouldTriggerPurchase |
|
) { |
|
redirectUrl.searchParams.set(COURSE_PURCHASE_PARAM, '1'); |
|
localStorage.removeItem(CHECKOUT_AFTER_LOGIN_KEY); |
|
} |
|
|
|
localStorage.removeItem(LINKEDIN_REDIRECT_AT); |
|
localStorage.removeItem(LINKEDIN_LAST_PAGE); |
|
setAuthToken(response.token); |
|
|
|
window.location.href = redirectUrl.toString(); |
|
}) |
|
.catch((err) => { |
|
setError('Something went wrong. Please try again later.'); |
|
setIsLoading(false); |
|
setIsDisabled?.(false); |
|
}); |
|
}, []); |
|
|
|
const handleClick = () => { |
|
setIsLoading(true); |
|
setIsDisabled?.(true); |
|
httpGet<{ loginUrl: string }>( |
|
`${import.meta.env.PUBLIC_API_URL}/v1-linkedin-login`, |
|
) |
|
.then(({ response, error }) => { |
|
if (!response?.loginUrl) { |
|
setError(error?.message || 'Something went wrong.'); |
|
setIsLoading(false); |
|
setIsDisabled?.(false); |
|
|
|
return; |
|
} |
|
|
|
// For non authentication pages, we want to redirect back to the page |
|
// the user was on before they clicked the social login button |
|
if (!['/login', '/signup'].includes(window.location.pathname)) { |
|
const pagePath = [ |
|
'/respond-invite', |
|
'/befriend', |
|
'/r', |
|
'/ai', |
|
].includes(window.location.pathname) |
|
? window.location.pathname + window.location.search |
|
: window.location.pathname; |
|
|
|
localStorage.setItem(LINKEDIN_REDIRECT_AT, Date.now().toString()); |
|
localStorage.setItem(LINKEDIN_LAST_PAGE, pagePath); |
|
} |
|
|
|
window.location.href = response.loginUrl; |
|
}) |
|
.catch((err) => { |
|
setError('Something went wrong. Please try again later.'); |
|
setIsLoading(false); |
|
setIsDisabled?.(false); |
|
}); |
|
}; |
|
|
|
return ( |
|
<> |
|
<button |
|
className={cn( |
|
'inline-flex h-10 w-full items-center justify-center gap-2 rounded border border-slate-300 bg-white p-2 text-sm font-medium text-black outline-none hover:border-gray-400 focus:ring-2 focus:ring-[#333] focus:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-60', |
|
className, |
|
)} |
|
disabled={isLoading || isDisabled} |
|
onClick={handleClick} |
|
> |
|
{isLoading ? ( |
|
<Spinner className={'h-[18px] w-[18px]'} isDualRing={false} /> |
|
) : ( |
|
<LinkedInIcon className={'h-[18px] w-[18px] text-blue-700'} /> |
|
)} |
|
Continue with LinkedIn |
|
</button> |
|
{error && ( |
|
<p className="mb-2 mt-1 text-sm font-medium text-red-600">{error}</p> |
|
)} |
|
</> |
|
); |
|
}
|
|
|