Skip to content

Commit 02f5818

Browse files
committed
feat: add popup interval + add vite to debug faster + change files in package.json
1 parent 1ce22fc commit 02f5818

File tree

10 files changed

+1666
-260
lines changed

10 files changed

+1666
-260
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module.exports = {
1111
'eslint:recommended',
1212
'plugin:@typescript-eslint/recommended',
1313
'prettier',
14+
'plugin:react-hooks/recommended',
1415
],
1516

1617
rules: {

package-lock.json

Lines changed: 1491 additions & 220 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55
"main": "build/index.js",
66
"module": "build/index.esm.js",
77
"files": [
8-
"css",
9-
"es",
10-
"lib",
11-
"umd",
8+
"build",
129
"assets"
1310
],
1411
"scripts": {
@@ -19,10 +16,11 @@
1916
"test": "jest",
2017
"test:watch": "jest --watch",
2118
"prettier": "prettier src --write",
22-
"lint": "eslint src --fix"
19+
"lint": "eslint src --fix",
20+
"publish:local": "export BUILD_MODE=dev &&npm publish --registry http://localhost:4873",
21+
"vite": "vite ./preview"
2322
},
2423
"dependencies": {
25-
"jest": "^27.3.1",
2624
"typescript": "^4.4.4"
2725
},
2826
"peerDependencies": {
@@ -45,14 +43,18 @@
4543
"babel-preset-react-app": "^10.0.0",
4644
"eslint": "^8.1.0",
4745
"eslint-config-prettier": "^8.3.0",
46+
"eslint-plugin-react-hooks": "^4.2.1-alpha-9c8161ba8-20211028",
47+
"jest": "^27.3.1",
4848
"prettier": "^2.4.1",
4949
"react": "^17.0.2",
5050
"react-dom": "^17.0.2",
5151
"react-router-dom": "^4.3.1",
5252
"rimraf": "^3.0.2",
5353
"rollup-plugin-peer-deps-external": "^2.2.4",
5454
"rollup-plugin-terser": "^7.0.2",
55-
"sass-loader": "^12.3.0"
55+
"sass-loader": "^12.3.0",
56+
"vite": "^2.6.13",
57+
"vite-react-jsx": "^1.1.2"
5658
},
5759
"author": "nvh95",
5860
"homepage": "https://github.com/nvh95/react-linkedin-login-oauth2",

preview/App.jsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from 'react';
2+
import { BrowserRouter, Route, Switch } from 'react-router-dom';
3+
4+
import { LinkedInCallback } from '../src/LinkedInCallback';
5+
import LinkedInPage from './LinkedInPage';
6+
7+
function App() {
8+
return (
9+
<BrowserRouter>
10+
<Switch>
11+
<Route exact path="/linkedin" component={LinkedInCallback} />
12+
<Route path="/" component={LinkedInPage} />
13+
</Switch>
14+
</BrowserRouter>
15+
);
16+
}
17+
18+
export default App;

preview/LinkedInPage.jsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React from 'react';
2+
3+
import { useLinkedIn } from '../src/useLinkedIn';
4+
import linkedin from '../assets/linkedin.png';
5+
6+
function LinkedInPage() {
7+
const { linkedInLogin } = useLinkedIn({
8+
clientId: '86vhj2q7ukf83q',
9+
redirectUri: `${window.location.origin}/linkedin`,
10+
onSuccess: (code) => {
11+
console.log(code);
12+
setCode(code);
13+
},
14+
scope: 'r_emailaddress',
15+
onError: (error) => {
16+
console.log(error);
17+
setErrorMessage(error.errorMessage);
18+
},
19+
});
20+
const [code, setCode] = React.useState('');
21+
const [errorMessage, setErrorMessage] = React.useState('');
22+
23+
return (
24+
<div>
25+
<div style={{ cursor: 'pointer' }} onClick={linkedInLogin}>
26+
<img
27+
src={linkedin}
28+
alt="Log in with Linked In"
29+
style={{ maxWidth: '180px' }}
30+
/>
31+
</div>
32+
33+
{!code && <div>No code</div>}
34+
{code && <div>Code: {code}</div>}
35+
{errorMessage && <div>{errorMessage}</div>}
36+
</div>
37+
);
38+
}
39+
40+
export default LinkedInPage;

preview/index.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8" />
6+
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
8+
<title>Linkedin Login</title>
9+
</head>
10+
11+
<body>
12+
<div id="root"></div>
13+
<script type="module" src="./index.tsx"></script>
14+
</body>
15+
16+
</html>

preview/index.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
3+
import App from './App';
4+
5+
ReactDOM.render(
6+
<React.StrictMode>
7+
<App />
8+
</React.StrictMode>,
9+
document.getElementById('root'),
10+
);

preview/vite.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { defineConfig } from 'vite';
2+
import reactJsx from 'vite-react-jsx';
3+
4+
// https://vitejs.dev/config/
5+
export default defineConfig({
6+
plugins: [reactJsx()],
7+
});

rollup.config.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ export default {
2525
commonjs(),
2626
typescript({ useTsconfigDeclarationDir: true }),
2727

28-
terser({
29-
output: { comments: false },
30-
compress: {
31-
drop_console: true,
32-
},
33-
}),
28+
// eslint-disable-next-line no-undef
29+
process.env.BUILD_MODE !== 'dev' &&
30+
terser({
31+
output: { comments: false },
32+
compress: {
33+
drop_console: true,
34+
},
35+
}),
3436
],
3537
};

src/useLinkedIn.tsx

Lines changed: 66 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,54 @@ type LinkedInType = {
2222
redirectUri: string;
2323
clientId: string;
2424
onSuccess: (code: string) => void;
25-
onFailure?: ({
25+
onError?: ({
2626
error,
2727
errorMessage,
2828
}: {
2929
error: string;
3030
errorMessage: string;
3131
}) => void;
3232
scope?: string;
33+
closePopupMessage?: string;
3334
};
3435

3536
export function useLinkedIn({
3637
redirectUri,
3738
clientId,
3839
onSuccess,
39-
onFailure,
40+
onError,
4041
scope = 'r_emailaddress',
42+
closePopupMessage = 'User closed the popup',
4143
}: LinkedInType) {
4244
const popupRef = useRef<Window>(null);
45+
const popUpIntervalRef = useRef<number>(null);
46+
47+
const receiveMessage = useCallback(
48+
(event: MessageEvent) => {
49+
const state = localStorage.getItem(LINKEDIN_OAUTH2_STATE);
50+
if (event.origin === window.location.origin) {
51+
if (event.data.errorMessage && event.data.from === 'Linked In') {
52+
// Prevent CSRF attack by testing state
53+
if (event.data.state !== state) {
54+
popupRef.current && popupRef.current.close();
55+
return;
56+
}
57+
onError && onError(event.data);
58+
popupRef.current && popupRef.current.close();
59+
} else if (event.data.code && event.data.from === 'Linked In') {
60+
// Prevent CSRF attack by testing state
61+
if (event.data.state !== state) {
62+
console.error('State does not match');
63+
popupRef.current && popupRef.current.close();
64+
return;
65+
}
66+
onSuccess && onSuccess(event.data.code);
67+
popupRef.current && popupRef.current.close();
68+
}
69+
}
70+
},
71+
[onError, onSuccess],
72+
);
4373

4474
useEffect(() => {
4575
return () => {
@@ -49,8 +79,19 @@ export function useLinkedIn({
4979
popupRef.current.close();
5080
popupRef.current = null;
5181
}
82+
if (popUpIntervalRef.current) {
83+
window.clearInterval(popUpIntervalRef.current);
84+
popUpIntervalRef.current = null;
85+
}
86+
};
87+
}, [receiveMessage]);
88+
89+
useEffect(() => {
90+
window.addEventListener('message', receiveMessage, false);
91+
return () => {
92+
window.removeEventListener('message', receiveMessage, false);
5293
};
53-
}, []);
94+
}, [receiveMessage]);
5495

5596
const getUrl = () => {
5697
const scopeParam = `&scope=${encodeURI(scope)}`;
@@ -61,38 +102,36 @@ export function useLinkedIn({
61102
};
62103

63104
const linkedInLogin = () => {
64-
window.removeEventListener('message', receiveMessage, false);
105+
popupRef.current?.close();
65106
popupRef.current = window.open(
66107
getUrl(),
67108
'_blank',
68109
getPopupPositionProperties({ width: 600, height: 600 }),
69110
);
70-
window.addEventListener('message', receiveMessage, false);
71-
};
72111

73-
const receiveMessage = useCallback((event: MessageEvent) => {
74-
const state = localStorage.getItem(LINKEDIN_OAUTH2_STATE);
75-
if (event.origin === window.location.origin) {
76-
if (event.data.errorMessage && event.data.from === 'Linked In') {
77-
// Prevent CSRF attack by testing state
78-
if (event.data.state !== state) {
79-
popupRef.current && popupRef.current.close();
80-
return;
81-
}
82-
onFailure(event.data);
83-
popupRef.current && popupRef.current.close();
84-
} else if (event.data.code && event.data.from === 'Linked In') {
85-
// Prevent CSRF attack by testing state
86-
if (event.data.state !== state) {
87-
console.error('State does not match');
88-
popupRef.current && popupRef.current.close();
89-
return;
112+
if (popUpIntervalRef.current) {
113+
window.clearInterval(popUpIntervalRef.current);
114+
popUpIntervalRef.current = null;
115+
}
116+
popUpIntervalRef.current = window.setInterval(() => {
117+
try {
118+
if (popupRef.current && popupRef.current.closed) {
119+
window.clearInterval(popUpIntervalRef.current);
120+
popUpIntervalRef.current = null;
121+
if (onError) {
122+
onError({
123+
error: 'user_closed_popup',
124+
errorMessage: closePopupMessage,
125+
});
126+
}
90127
}
91-
onSuccess(event.data.code);
92-
popupRef.current && popupRef.current.close();
128+
} catch (error) {
129+
console.error(error);
130+
window.clearInterval(popUpIntervalRef.current);
131+
popUpIntervalRef.current = null;
93132
}
94-
}
95-
}, []);
133+
}, 1000);
134+
};
96135

97136
return {
98137
linkedInLogin,

0 commit comments

Comments
 (0)