Skip to content

Commit d78dfd2

Browse files
authored
adblock detector (tam315#8)
* adblock detection hook * add emotion * add components WIP * eslint * adblock guard
1 parent 11663d0 commit d78dfd2

File tree

9 files changed

+953
-21
lines changed

9 files changed

+953
-21
lines changed

frontend/.eslintrc.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
module.exports = {
2+
parser: '@typescript-eslint/parser',
3+
extends: [
4+
'eslint:recommended',
5+
'plugin:@typescript-eslint/recommended',
6+
'plugin:react/recommended',
7+
'plugin:prettier/recommended',
8+
'prettier',
9+
'prettier/react',
10+
],
11+
plugins: ['@typescript-eslint', 'prettier', 'react-hooks'],
12+
globals: {
13+
document: true,
14+
window: true,
15+
},
16+
rules: {
17+
'no-unused-expressions': [
18+
'error',
19+
{ allowShortCircuit: true, allowTernary: true },
20+
],
21+
'react-hooks/rules-of-hooks': 'error',
22+
'react-hooks/exhaustive-deps': 'warn',
23+
'react/prop-types': 'off',
24+
},
25+
};

frontend/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
"export": "next build && next export"
1010
},
1111
"dependencies": {
12+
"adblock-detector-hook": "^0.1.0",
13+
"emotion": "^10.0.27",
1214
"next": "9.5.0",
1315
"normalize.css": "^8.0.1",
1416
"react": "16.13.1",
@@ -17,6 +19,13 @@
1719
"devDependencies": {
1820
"@types/node": "^14.0.27",
1921
"@types/react": "^16.9.43",
22+
"@typescript-eslint/eslint-plugin": "^3.7.1",
23+
"@typescript-eslint/parser": "^3.7.1",
24+
"eslint": "^7.5.0",
25+
"eslint-config-prettier": "^6.11.0",
26+
"eslint-plugin-prettier": "^3.1.4",
27+
"eslint-plugin-react": "^7.20.5",
28+
"eslint-plugin-react-hooks": "^4.0.8",
2029
"typescript": "^3.9.7"
2130
}
2231
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { css } from 'emotion';
2+
import React from 'react';
3+
import { Portal } from '../Portal/Portal';
4+
5+
interface IProps {
6+
isOpen: boolean;
7+
children: React.ReactElement;
8+
}
9+
10+
export const ModalDialog: React.FC<IProps> = (props) => {
11+
const { children, isOpen } = props;
12+
13+
// オープン状態になるたびに新しくPortalを生成することで、
14+
// 時間的に後に開いたモーダルを<body>直下レベルの「より後方」に描写させる。
15+
// これにより、z-indexを意識することなく、重なり順を正しく制御することができる。
16+
if (!isOpen) return null;
17+
18+
const styles = {
19+
container: css`
20+
align-items: center;
21+
display: flex;
22+
height: 100vh;
23+
justify-content: center;
24+
left: 0;
25+
position: fixed;
26+
top: 0;
27+
width: 100vw;
28+
`,
29+
backdrop: css`
30+
background-color: #000;
31+
height: 100%;
32+
opacity: 0.5;
33+
position: absolute;
34+
width: 100%;
35+
`,
36+
modalContent: css`
37+
background: white;
38+
border-radius: 5px;
39+
max-width: 600px;
40+
padding: 2rem;
41+
position: absolute;
42+
width: 50%;
43+
`,
44+
};
45+
46+
return (
47+
<Portal>
48+
<div className={styles.container}>
49+
<div className={styles.backdrop} />
50+
<div className={styles.modalContent}>{children}</div>
51+
</div>
52+
</Portal>
53+
);
54+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './ModalDialog';
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React, { useEffect } from 'react';
2+
import ReactDOM from 'react-dom';
3+
4+
interface IProps {
5+
children: React.ReactNode;
6+
}
7+
8+
export const Portal: React.FC<IProps> = (props) => {
9+
const { children } = props;
10+
let targetNode;
11+
12+
// SSR
13+
if (typeof document === 'undefined') {
14+
return null;
15+
}
16+
17+
useEffect(() => {
18+
return () => {
19+
document.body.removeChild(targetNode);
20+
};
21+
}, []);
22+
23+
// Create a new div element as a child of BODY
24+
if (!targetNode) {
25+
targetNode = document.createElement('div');
26+
document.body.appendChild(targetNode);
27+
}
28+
29+
// Render children as a child element of the created div
30+
return ReactDOM.createPortal(children, targetNode);
31+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './Portal';
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { useAdBlockDetector } from 'adblock-detector-hook';
2+
import { css } from 'emotion';
3+
import React, { useCallback, useEffect, useState } from 'react';
4+
import { ModalDialog } from '../../components/ModalDialog';
5+
6+
interface IProps {
7+
children: React.ReactElement;
8+
}
9+
10+
export const AdBlockGuard: React.FC<IProps> = (props) => {
11+
const { children } = props;
12+
const { detected } = useAdBlockDetector();
13+
const [isCautionDialogOpen, setIsCautionDialogOpen] = useState<boolean>(
14+
false,
15+
);
16+
17+
useEffect(() => {
18+
if (detected) {
19+
setIsCautionDialogOpen(true);
20+
}
21+
}, [detected]);
22+
23+
const onCloseButtonClicked = useCallback(() => {
24+
setIsCautionDialogOpen(false);
25+
}, []);
26+
27+
const styles = {
28+
buttonRow: css`
29+
text-align: right;
30+
`,
31+
button: css`
32+
background: none;
33+
`,
34+
};
35+
36+
return (
37+
<>
38+
{children}
39+
<ModalDialog isOpen={isCautionDialogOpen}>
40+
<>
41+
<div>
42+
<p>We&apos;ve noticed that you are using an ad blocker.</p>
43+
<p>
44+
Please <b>disable your ad blocker</b> for this site and help us to
45+
provide free services.
46+
</p>
47+
</div>
48+
<div className={styles.buttonRow}>
49+
<button className={styles.button} onClick={onCloseButtonClicked}>
50+
Close
51+
</button>
52+
</div>
53+
</>
54+
</ModalDialog>
55+
</>
56+
);
57+
};

frontend/src/pages/_app.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { AppProps } from 'next/app';
22
import Head from 'next/head';
33
import React from 'react';
4+
import { AdBlockGuard } from '../features/ads/AdBlockGuard';
45
import './global.css';
56

67
function App({ Component, pageProps }: AppProps) {
@@ -31,7 +32,9 @@ function App({ Component, pageProps }: AppProps) {
3132
<meta name="description" content="Generate pairwise testcases online" />
3233
<title>Pairwise Pict Online</title>
3334
</Head>
34-
<Component {...pageProps} />
35+
<AdBlockGuard>
36+
<Component {...pageProps} />
37+
</AdBlockGuard>
3538
</>
3639
);
3740
}

0 commit comments

Comments
 (0)