mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-08-12 04:04:08 +02:00
chore: linkedin login functionality (#4072)
This commit is contained in:
119
src/components/AuthenticationFlow/LinkedInButton.tsx
Normal file
119
src/components/AuthenticationFlow/LinkedInButton.tsx
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import { useEffect, useState } from 'preact/hooks';
|
||||||
|
import Cookies from 'js-cookie';
|
||||||
|
import LinkedIn from '../../icons/linkedin.svg';
|
||||||
|
import SpinnerIcon from '../../icons/spinner.svg';
|
||||||
|
import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
|
||||||
|
import { httpGet } from '../../lib/http';
|
||||||
|
|
||||||
|
type LinkedInButtonProps = {};
|
||||||
|
|
||||||
|
const LINKEDIN_REDIRECT_AT = 'linkedInRedirectAt';
|
||||||
|
const LINKEDIN_LAST_PAGE = 'linkedInLastPage';
|
||||||
|
|
||||||
|
export function LinkedInButton(props: LinkedInButtonProps) {
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [error, setError] = useState('');
|
||||||
|
const icon = isLoading ? SpinnerIcon : LinkedIn;
|
||||||
|
|
||||||
|
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);
|
||||||
|
httpGet<{ token: string }>(
|
||||||
|
`${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);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let redirectUrl = '/';
|
||||||
|
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 = lastPageBeforeLinkedIn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.removeItem(LINKEDIN_REDIRECT_AT);
|
||||||
|
localStorage.removeItem(LINKEDIN_LAST_PAGE);
|
||||||
|
Cookies.set(TOKEN_COOKIE_NAME, response.token, {
|
||||||
|
path: '/',
|
||||||
|
expires: 30,
|
||||||
|
});
|
||||||
|
window.location.href = redirectUrl;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
setError('Something went wrong. Please try again later.');
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
setIsLoading(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);
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
localStorage.setItem(LINKEDIN_REDIRECT_AT, Date.now().toString());
|
||||||
|
localStorage.setItem(LINKEDIN_LAST_PAGE, window.location.pathname);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.href = response.loginUrl;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
setError('Something went wrong. Please try again later.');
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
class="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 focus:ring-2 focus:ring-[#333] focus:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
|
disabled={isLoading}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={icon}
|
||||||
|
alt="Google"
|
||||||
|
class={`h-[18px] w-[18px] ${isLoading ? 'animate-spin' : ''}`}
|
||||||
|
/>
|
||||||
|
Continue with LinkedIn
|
||||||
|
</button>
|
||||||
|
{error && (
|
||||||
|
<p className="mb-2 mt-1 text-sm font-medium text-red-600">{error}</p>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@@ -4,6 +4,7 @@ import EmailLoginForm from './EmailLoginForm';
|
|||||||
import Divider from './Divider.astro';
|
import Divider from './Divider.astro';
|
||||||
import { GitHubButton } from './GitHubButton';
|
import { GitHubButton } from './GitHubButton';
|
||||||
import { GoogleButton } from './GoogleButton';
|
import { GoogleButton } from './GoogleButton';
|
||||||
|
import { LinkedInButton } from './LinkedInButton';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Popup id='login-popup' title='' subtitle=''>
|
<Popup id='login-popup' title='' subtitle=''>
|
||||||
@@ -19,6 +20,7 @@ import { GoogleButton } from './GoogleButton';
|
|||||||
<div class='mt-7 flex flex-col gap-2'>
|
<div class='mt-7 flex flex-col gap-2'>
|
||||||
<GitHubButton client:load />
|
<GitHubButton client:load />
|
||||||
<GoogleButton client:load />
|
<GoogleButton client:load />
|
||||||
|
<LinkedInButton client:load />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
3
src/icons/linkedin.svg
Normal file
3
src/icons/linkedin.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="100" height="100" viewBox="0,0,256,256">
|
||||||
|
<g transform="translate(-26.66667,-26.66667) scale(1.20833,1.20833)"><g fill="none" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><g transform="scale(5.33333,5.33333)"><path d="M42,37c0,2.762 -2.238,5 -5,5h-26c-2.761,0 -5,-2.238 -5,-5v-26c0,-2.762 2.239,-5 5,-5h26c2.762,0 5,2.238 5,5z" fill="#0288d1"></path><path d="M12,19h5v17h-5zM14.485,17h-0.028c-1.492,0 -2.457,-1.112 -2.457,-2.501c0,-1.419 0.995,-2.499 2.514,-2.499c1.521,0 2.458,1.08 2.486,2.499c0,1.388 -0.965,2.501 -2.515,2.501zM36,36h-5v-9.099c0,-2.198 -1.225,-3.698 -3.192,-3.698c-1.501,0 -2.313,1.012 -2.707,1.99c-0.144,0.35 -0.101,1.318 -0.101,1.807v9h-5v-17h5v2.616c0.721,-1.116 1.85,-2.616 4.738,-2.616c3.578,0 6.261,2.25 6.261,7.274l0.001,9.726z" fill="#ffffff"></path></g></g></g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@@ -3,6 +3,7 @@ import Divider from '../components/AuthenticationFlow/Divider.astro';
|
|||||||
import EmailLoginForm from '../components/AuthenticationFlow/EmailLoginForm';
|
import EmailLoginForm from '../components/AuthenticationFlow/EmailLoginForm';
|
||||||
import { GitHubButton } from '../components/AuthenticationFlow/GitHubButton';
|
import { GitHubButton } from '../components/AuthenticationFlow/GitHubButton';
|
||||||
import { GoogleButton } from '../components/AuthenticationFlow/GoogleButton';
|
import { GoogleButton } from '../components/AuthenticationFlow/GoogleButton';
|
||||||
|
import { LinkedInButton } from '../components/AuthenticationFlow/LinkedInButton';
|
||||||
import AccountLayout from '../layouts/AccountLayout.astro';
|
import AccountLayout from '../layouts/AccountLayout.astro';
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ import AccountLayout from '../layouts/AccountLayout.astro';
|
|||||||
<div class='flex w-full flex-col gap-2'>
|
<div class='flex w-full flex-col gap-2'>
|
||||||
<GitHubButton client:load />
|
<GitHubButton client:load />
|
||||||
<GoogleButton client:load />
|
<GoogleButton client:load />
|
||||||
|
<LinkedInButton client:load />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
@@ -3,6 +3,7 @@ import Divider from '../components/AuthenticationFlow/Divider.astro';
|
|||||||
import EmailSignupForm from '../components/AuthenticationFlow/EmailSignupForm';
|
import EmailSignupForm from '../components/AuthenticationFlow/EmailSignupForm';
|
||||||
import { GitHubButton } from '../components/AuthenticationFlow/GitHubButton';
|
import { GitHubButton } from '../components/AuthenticationFlow/GitHubButton';
|
||||||
import { GoogleButton } from '../components/AuthenticationFlow/GoogleButton';
|
import { GoogleButton } from '../components/AuthenticationFlow/GoogleButton';
|
||||||
|
import { LinkedInButton } from '../components/AuthenticationFlow/LinkedInButton';
|
||||||
import AccountLayout from '../layouts/AccountLayout.astro';
|
import AccountLayout from '../layouts/AccountLayout.astro';
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ import AccountLayout from '../layouts/AccountLayout.astro';
|
|||||||
<div class='flex w-full flex-col items-stretch gap-2'>
|
<div class='flex w-full flex-col items-stretch gap-2'>
|
||||||
<GitHubButton client:load />
|
<GitHubButton client:load />
|
||||||
<GoogleButton client:load />
|
<GoogleButton client:load />
|
||||||
|
<LinkedInButton client:load />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
Reference in New Issue
Block a user