From 2ccebb34fcac7c2014d2280eba76508ed9bcc541 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Tue, 6 Dec 2022 15:05:38 +0200 Subject: [PATCH 01/10] tweaks + autoreload QR with loader display --- .../MobileAppVerification.module.scss | 28 ++++++++ .../MobileAppVerification.tsx | 68 +++++++++++++------ .../parts/QRCode/QRCode.test.tsx | 2 +- .../parts/QRCode/{index.tsx => QRCode.tsx} | 5 +- .../containers/UserSettings/UserSettings.tsx | 5 +- 5 files changed, 84 insertions(+), 24 deletions(-) rename grafana-plugin/src/containers/MobileAppVerification/parts/QRCode/{index.tsx => QRCode.tsx} (66%) diff --git a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.module.scss b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.module.scss index a97a1e43..7c466508 100644 --- a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.module.scss +++ b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.module.scss @@ -33,3 +33,31 @@ filter: blur(6px); opacity: 0.6; } + +.blurry { + filter: blur(4px); + opacity: 0.2; +} + +.blurry-loader { + position: absolute; + z-index: 10; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + + > div { + margin-bottom: 0; + font-size: 16px; + text-align: center; + } + + [data-testid='Spinner'] { // No other way to overwrite Grafana's LoadingPlaceholder unfortunately + width: 100%; + padding-top: 12px; + } + + [data-testid='Spinner'] i { // No other way to overwrite Grafana's LoadingPlaceholder unfortunately + font-size: 32px; + } +} diff --git a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx index 7b8d2afb..c489bd4c 100644 --- a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx +++ b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx @@ -13,7 +13,7 @@ import { useStore } from 'state/useStore'; import styles from './MobileAppVerification.module.scss'; import DisconnectButton from './parts/DisconnectButton/DisconnectButton'; import DownloadIcons from './parts/DownloadIcons'; -import QRCode from './parts/QRCode'; +import QRCode from './parts/QRCode/QRCode'; const cx = cn.bind(styles); @@ -21,7 +21,8 @@ type Props = { userPk: User['pk']; }; -const INTERVAL_MS = 5000; +const INTERVAL_QUEUE_QR = 50000; +const INTERVAL_POLLING = 5000; const BACKEND = 'MOBILE_APP'; const MobileAppVerification = observer(({ userPk }: Props) => { @@ -36,18 +37,29 @@ const MobileAppVerification = observer(({ userPk }: Props) => { const [disconnectingMobileApp, setDisconnectingMobileApp] = useState(false); const [errorDisconnectingMobileApp, setErrorDisconnectingMobileApp] = useState(null); const [userTimeoutId, setUserTimeoutId] = useState(undefined); + const [refreshTimeoutId, setRefreshTimeoutId] = useState(undefined); + const [isQRBlurry, setIsQRBlurry] = useState(false); - const fetchQRCode = useCallback(async () => { - setFetchingQRCode(true); - try { - // backend verification code that we receive is a JSON object that has been "stringified" - const qrCodeContent = await userStore.sendBackendConfirmationCode(userPk, BACKEND); - setQRCodeValue(qrCodeContent); - } catch (e) { - setErrorFetchingQRCode('There was an error fetching your QR code. Please try again.'); - } - setFetchingQRCode(false); - }, [userPk]); + const fetchQRCode = useCallback( + async (showLoader: boolean = true) => { + if (showLoader) { + setFetchingQRCode(true); + } + + try { + // backend verification code that we receive is a JSON object that has been "stringified" + const qrCodeContent = await userStore.sendBackendConfirmationCode(userPk, BACKEND); + setQRCodeValue(qrCodeContent); + } catch (e) { + setErrorFetchingQRCode('There was an error fetching your QR code. Please try again.'); + } + + if (showLoader) { + setFetchingQRCode(false); + } + }, + [userPk] + ); const resetState = useCallback(() => { setErrorDisconnectingMobileApp(null); @@ -66,11 +78,13 @@ const MobileAppVerification = observer(({ userPk }: Props) => { } setDisconnectingMobileApp(false); + queueRefreshQR(); pollUserProfile(); }, [userPk, resetState]); useEffect(() => { if (!isUserConnected()) { + queueRefreshQR(); pollUserProfile(); } @@ -116,13 +130,14 @@ const MobileAppVerification = observer(({ userPk }: Props) => { Sign In Open Grafana IRM mobile application and scan this code to sync it with your account. -
- +
+ + {isQRBlurry && ( +
+ +
+ )}
- - Note: the QR code is only valid for one minute. If you have issues connecting your mobile - app, try refreshing this page to generate a new code. - ); } @@ -142,13 +157,26 @@ const MobileAppVerification = observer(({ userPk }: Props) => { return !!(user || userStore.currentUser).messaging_backends[BACKEND]?.connected; } + async function queueRefreshQR(): Promise { + clearTimeout(refreshTimeoutId); + setRefreshTimeoutId(undefined); + + const user = await userStore.loadUser(userPk); + if (!isUserConnected(user)) { + setIsQRBlurry(true); + await fetchQRCode(false); + setIsQRBlurry(false); + setTimeout(() => queueRefreshQR(), INTERVAL_QUEUE_QR); + } + } + async function pollUserProfile(): Promise { clearTimeout(userTimeoutId); setUserTimeoutId(undefined); const user = await userStore.loadUser(userPk); if (!isUserConnected(user)) { - setUserTimeoutId(setTimeout(() => pollUserProfile(), INTERVAL_MS)); + setUserTimeoutId(setTimeout(() => pollUserProfile(), INTERVAL_POLLING)); } else { setMobileAppIsCurrentlyConnected(true); } diff --git a/grafana-plugin/src/containers/MobileAppVerification/parts/QRCode/QRCode.test.tsx b/grafana-plugin/src/containers/MobileAppVerification/parts/QRCode/QRCode.test.tsx index 8c3e1554..76008c27 100644 --- a/grafana-plugin/src/containers/MobileAppVerification/parts/QRCode/QRCode.test.tsx +++ b/grafana-plugin/src/containers/MobileAppVerification/parts/QRCode/QRCode.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { render } from '@testing-library/react'; -import QRCode from './'; +import QRCode from './QRCode'; describe('QRCode', () => { test('it renders properly', () => { diff --git a/grafana-plugin/src/containers/MobileAppVerification/parts/QRCode/index.tsx b/grafana-plugin/src/containers/MobileAppVerification/parts/QRCode/QRCode.tsx similarity index 66% rename from grafana-plugin/src/containers/MobileAppVerification/parts/QRCode/index.tsx rename to grafana-plugin/src/containers/MobileAppVerification/parts/QRCode/QRCode.tsx index 98ec3561..0e8e65c9 100644 --- a/grafana-plugin/src/containers/MobileAppVerification/parts/QRCode/index.tsx +++ b/grafana-plugin/src/containers/MobileAppVerification/parts/QRCode/QRCode.tsx @@ -6,10 +6,11 @@ import Block from 'components/GBlock/Block'; type Props = { value: string; + className: string; }; -const QRCode: FC = ({ value }) => ( - +const QRCode: FC = ({ value, className }) => ( + ); diff --git a/grafana-plugin/src/containers/UserSettings/UserSettings.tsx b/grafana-plugin/src/containers/UserSettings/UserSettings.tsx index 78fdad24..4c025479 100644 --- a/grafana-plugin/src/containers/UserSettings/UserSettings.tsx +++ b/grafana-plugin/src/containers/UserSettings/UserSettings.tsx @@ -49,7 +49,10 @@ const UserSettings = observer(({ id, onHide, tab = UserSettingsTab.UserInfo }: U }, []); const isModalWide = - !isDesktopOrLaptop || activeTab === UserSettingsTab.UserInfo || activeTab === UserSettingsTab.PhoneVerification; + !isDesktopOrLaptop || + activeTab === UserSettingsTab.UserInfo || + activeTab === UserSettingsTab.PhoneVerification || + activeTab === UserSettingsTab.MobileAppVerification; const [ showNotificationSettingsTab, From 73518e70c953e302d5c1a07783a1633aefef9755 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Tue, 6 Dec 2022 15:13:46 +0200 Subject: [PATCH 02/10] clear timeout on unmount --- .../containers/MobileAppVerification/MobileAppVerification.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx index c489bd4c..e1f67855 100644 --- a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx +++ b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx @@ -92,6 +92,7 @@ const MobileAppVerification = observer(({ userPk }: Props) => { return () => { if (userTimeoutId) { clearTimeout(userTimeoutId); + clearTimeout(refreshTimeoutId); } }; }, []); From d47efd12b216030015734efdd3b960d2bd3b92d0 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Tue, 6 Dec 2022 15:38:47 +0200 Subject: [PATCH 03/10] linter --- .../containers/MobileAppVerification/MobileAppVerification.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx index e1f67855..b6449296 100644 --- a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx +++ b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx @@ -41,7 +41,7 @@ const MobileAppVerification = observer(({ userPk }: Props) => { const [isQRBlurry, setIsQRBlurry] = useState(false); const fetchQRCode = useCallback( - async (showLoader: boolean = true) => { + async (showLoader = true) => { if (showLoader) { setFetchingQRCode(true); } From a34a39f4e0ef3a9947d65f99592e90ad23abd858 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Tue, 6 Dec 2022 16:37:56 +0200 Subject: [PATCH 04/10] test fix --- .../MobileAppVerification.test.tsx | 4 +- .../MobileAppVerification.test.tsx.snap | 74 ++++++++++++++++++- 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.test.tsx b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.test.tsx index 72926dd4..a073d68f 100644 --- a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.test.tsx +++ b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.test.tsx @@ -208,7 +208,7 @@ describe('MobileAppVerification', () => { render(); await waitFor(() => { - expect(loadUserMock).toHaveBeenCalledTimes(1); + expect(loadUserMock).toHaveBeenCalled(); }); }); @@ -232,7 +232,7 @@ describe('MobileAppVerification', () => { await user.click(screen.getByText('Remove')); // click the confirm button within the modal, which actually triggers the callback await waitFor(() => { - expect(loadUserMock).toHaveBeenCalledTimes(1); + expect(loadUserMock).toHaveBeenCalled(); }); }); }); diff --git a/grafana-plugin/src/containers/MobileAppVerification/__snapshots__/MobileAppVerification.test.tsx.snap b/grafana-plugin/src/containers/MobileAppVerification/__snapshots__/MobileAppVerification.test.tsx.snap index 6bcaa911..546f29bb 100644 --- a/grafana-plugin/src/containers/MobileAppVerification/__snapshots__/MobileAppVerification.test.tsx.snap +++ b/grafana-plugin/src/containers/MobileAppVerification/__snapshots__/MobileAppVerification.test.tsx.snap @@ -1,6 +1,18 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 1`] = ` +exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 1`] = `
`; + +exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 2`] = `
`; + +exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 3`] = `
`; + +exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 4`] = `
`; + +exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 5`] = `
`; + +exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 6`] = `
`; + +exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 7`] = `
`; +exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 8`] = `
`; + +exports[`MobileAppVerification it polls loadUser after disconnect 1`] = `
`; + +exports[`MobileAppVerification it polls loadUser after disconnect 2`] = `
`; + +exports[`MobileAppVerification it polls loadUser after disconnect 3`] = `
`; + +exports[`MobileAppVerification it polls loadUser after disconnect 4`] = `
`; + +exports[`MobileAppVerification it polls loadUser after disconnect 5`] = `
`; + +exports[`MobileAppVerification it polls loadUser after disconnect 6`] = `
`; + +exports[`MobileAppVerification it polls loadUser after disconnect 7`] = `
`; + +exports[`MobileAppVerification it polls loadUser on first render if not connected 1`] = `
`; + +exports[`MobileAppVerification it polls loadUser on first render if not connected 2`] = `
`; + +exports[`MobileAppVerification it polls loadUser on first render if not connected 3`] = `
`; + +exports[`MobileAppVerification it polls loadUser on first render if not connected 4`] = `
`; + exports[`MobileAppVerification it shows a QR code if the app isn't already connected 1`] = `
`; -exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 1`] = ` +exports[`MobileAppVerification it shows a QR code if the app isn't already connected 2`] = `
`; + +exports[`MobileAppVerification it shows a QR code if the app isn't already connected 3`] = `
`; + +exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 1`] = `
`; + +exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 2`] = `
`; + +exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 3`] = `
`; + +exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 4`] = `
`; + +exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 5`] = `
`; + +exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 6`] = `
`; +exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 7`] = `
`; + +exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 8`] = `
`; + exports[`MobileAppVerification it shows a loading message if it is currently fetching the QR code 1`] = `
`; -exports[`MobileAppVerification it shows an error message if there was an error disconnecting the mobile app 1`] = ` +exports[`MobileAppVerification it shows an error message if there was an error disconnecting the mobile app 1`] = `
`; + +exports[`MobileAppVerification it shows an error message if there was an error disconnecting the mobile app 2`] = `
`; + +exports[`MobileAppVerification it shows an error message if there was an error disconnecting the mobile app 3`] = `
`; + +exports[`MobileAppVerification it shows an error message if there was an error disconnecting the mobile app 4`] = `
`; + +exports[`MobileAppVerification it shows an error message if there was an error disconnecting the mobile app 5`] = `
`; + +exports[`MobileAppVerification it shows an error message if there was an error disconnecting the mobile app 6`] = `
`; +exports[`MobileAppVerification it shows an error message if there was an error disconnecting the mobile app 7`] = `
`; + exports[`MobileAppVerification it shows an error message if there was an error fetching the QR code 1`] = `
`; + +exports[`MobileAppVerification it shows an error message if there was an error fetching the QR code 2`] = `
`; From c640653faae011fb325ac26e300823cc9139a60a Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Tue, 6 Dec 2022 16:53:26 +0200 Subject: [PATCH 05/10] fixed double requests, specify timeout of 6000 in awaitFor calls --- .../MobileAppVerification.test.tsx | 18 +- .../MobileAppVerification.tsx | 22 +- .../MobileAppVerification.test.tsx.snap | 2321 ++++++++++++++++- 3 files changed, 2245 insertions(+), 116 deletions(-) diff --git a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.test.tsx b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.test.tsx index a073d68f..945fdf0d 100644 --- a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.test.tsx +++ b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.test.tsx @@ -207,9 +207,12 @@ describe('MobileAppVerification', () => { render(); - await waitFor(() => { - expect(loadUserMock).toHaveBeenCalled(); - }); + await waitFor( + () => { + expect(loadUserMock).toHaveBeenCalled(); + }, + { timeout: 6000 } + ); }); test('it polls loadUser after disconnect', async () => { @@ -231,8 +234,11 @@ describe('MobileAppVerification', () => { await user.click(button); // click the disconnect button, which opens the modal await user.click(screen.getByText('Remove')); // click the confirm button within the modal, which actually triggers the callback - await waitFor(() => { - expect(loadUserMock).toHaveBeenCalled(); - }); + await waitFor( + () => { + expect(loadUserMock).toHaveBeenCalled(); + }, + { timeout: 6000 } + ); }); }); diff --git a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx index b6449296..b19f36d9 100644 --- a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx +++ b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx @@ -78,22 +78,18 @@ const MobileAppVerification = observer(({ userPk }: Props) => { } setDisconnectingMobileApp(false); - queueRefreshQR(); - pollUserProfile(); + clearTimeouts(); + triggerTimeouts(); }, [userPk, resetState]); useEffect(() => { if (!isUserConnected()) { - queueRefreshQR(); - pollUserProfile(); + triggerTimeouts(); } // clear on unmount return () => { - if (userTimeoutId) { - clearTimeout(userTimeoutId); - clearTimeout(refreshTimeoutId); - } + clearTimeouts(); }; }, []); @@ -154,6 +150,16 @@ const MobileAppVerification = observer(({ userPk }: Props) => {
); + function clearTimeouts(): void { + clearTimeout(userTimeoutId); + clearTimeout(refreshTimeoutId); + } + + function triggerTimeouts(): void { + setTimeout(() => queueRefreshQR(), INTERVAL_QUEUE_QR); + setTimeout(() => pollUserProfile(), INTERVAL_POLLING); + } + function isUserConnected(user?: User): boolean { return !!(user || userStore.currentUser).messaging_backends[BACKEND]?.connected; } diff --git a/grafana-plugin/src/containers/MobileAppVerification/__snapshots__/MobileAppVerification.test.tsx.snap b/grafana-plugin/src/containers/MobileAppVerification/__snapshots__/MobileAppVerification.test.tsx.snap index 546f29bb..e471869c 100644 --- a/grafana-plugin/src/containers/MobileAppVerification/__snapshots__/MobileAppVerification.test.tsx.snap +++ b/grafana-plugin/src/containers/MobileAppVerification/__snapshots__/MobileAppVerification.test.tsx.snap @@ -1,18 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 1`] = `
`; - -exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 2`] = `
`; - -exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 3`] = `
`; - -exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 4`] = `
`; - -exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 5`] = `
`; - -exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 6`] = `
`; - -exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 7`] = ` +exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 1`] = `
- App connected -
- - - -
+ Sign In
- You can sync one application to your account. To setup new device please disconnect app first. + Open Grafana IRM mobile application and scan this code to sync it with your account.
- - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@@ -158,30 +2331,6 @@ exports[`MobileAppVerification if we disconnect the app, it disconnects and fetc
`; -exports[`MobileAppVerification if we disconnect the app, it disconnects and fetches a new QR code 8`] = `
`; - -exports[`MobileAppVerification it polls loadUser after disconnect 1`] = `
`; - -exports[`MobileAppVerification it polls loadUser after disconnect 2`] = `
`; - -exports[`MobileAppVerification it polls loadUser after disconnect 3`] = `
`; - -exports[`MobileAppVerification it polls loadUser after disconnect 4`] = `
`; - -exports[`MobileAppVerification it polls loadUser after disconnect 5`] = `
`; - -exports[`MobileAppVerification it polls loadUser after disconnect 6`] = `
`; - -exports[`MobileAppVerification it polls loadUser after disconnect 7`] = `
`; - -exports[`MobileAppVerification it polls loadUser on first render if not connected 1`] = `
`; - -exports[`MobileAppVerification it polls loadUser on first render if not connected 2`] = `
`; - -exports[`MobileAppVerification it polls loadUser on first render if not connected 3`] = `
`; - -exports[`MobileAppVerification it polls loadUser on first render if not connected 4`] = `
`; - exports[`MobileAppVerification it shows a QR code if the app isn't already connected 1`] = `
`; -exports[`MobileAppVerification it shows a QR code if the app isn't already connected 2`] = `
`; - -exports[`MobileAppVerification it shows a QR code if the app isn't already connected 3`] = `
`; - -exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 1`] = `
`; - -exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 2`] = `
`; - -exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 3`] = `
`; - -exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 4`] = `
`; - -exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 5`] = `
`; - -exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 6`] = ` +exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 1`] = `
`; -exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 7`] = `
`; - -exports[`MobileAppVerification it shows a loading message if it is currently disconnecting 8`] = `
`; - exports[`MobileAppVerification it shows a loading message if it is currently fetching the QR code 1`] = `
`; -exports[`MobileAppVerification it shows an error message if there was an error disconnecting the mobile app 1`] = `
`; - -exports[`MobileAppVerification it shows an error message if there was an error disconnecting the mobile app 2`] = `
`; - -exports[`MobileAppVerification it shows an error message if there was an error disconnecting the mobile app 3`] = `
`; - -exports[`MobileAppVerification it shows an error message if there was an error disconnecting the mobile app 4`] = `
`; - -exports[`MobileAppVerification it shows an error message if there was an error disconnecting the mobile app 5`] = `
`; - -exports[`MobileAppVerification it shows an error message if there was an error disconnecting the mobile app 6`] = ` +exports[`MobileAppVerification it shows an error message if there was an error disconnecting the mobile app 1`] = `
`; -exports[`MobileAppVerification it shows an error message if there was an error disconnecting the mobile app 7`] = `
`; - exports[`MobileAppVerification it shows an error message if there was an error fetching the QR code 1`] = `
`; - -exports[`MobileAppVerification it shows an error message if there was an error fetching the QR code 2`] = `
`; From c04590f74676340f7bbb9f48670c26d55c6d1ec4 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Tue, 6 Dec 2022 17:15:08 +0200 Subject: [PATCH 06/10] mark classname as optional --- .../parts/QRCode/QRCode.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/grafana-plugin/src/containers/MobileAppVerification/parts/QRCode/QRCode.tsx b/grafana-plugin/src/containers/MobileAppVerification/parts/QRCode/QRCode.tsx index 0e8e65c9..b41de79d 100644 --- a/grafana-plugin/src/containers/MobileAppVerification/parts/QRCode/QRCode.tsx +++ b/grafana-plugin/src/containers/MobileAppVerification/parts/QRCode/QRCode.tsx @@ -6,13 +6,17 @@ import Block from 'components/GBlock/Block'; type Props = { value: string; - className: string; + className?: string; }; -const QRCode: FC = ({ value, className }) => ( - - - -); +const QRCode: FC = (props: Props) => { + const { value, className = '' } = props; + + return ( + + + + ); +}; export default QRCode; From a080d8fd7df28ae55f8d85d64091a40ffb2e2ac2 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Wed, 7 Dec 2022 11:16:47 +0200 Subject: [PATCH 07/10] review --- .../MobileAppVerification/MobileAppVerification.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx index b19f36d9..bedcdf99 100644 --- a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx +++ b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx @@ -156,8 +156,8 @@ const MobileAppVerification = observer(({ userPk }: Props) => { } function triggerTimeouts(): void { - setTimeout(() => queueRefreshQR(), INTERVAL_QUEUE_QR); - setTimeout(() => pollUserProfile(), INTERVAL_POLLING); + setTimeout(queueRefreshQR, INTERVAL_QUEUE_QR); + setTimeout(pollUserProfile, INTERVAL_POLLING); } function isUserConnected(user?: User): boolean { @@ -173,7 +173,7 @@ const MobileAppVerification = observer(({ userPk }: Props) => { setIsQRBlurry(true); await fetchQRCode(false); setIsQRBlurry(false); - setTimeout(() => queueRefreshQR(), INTERVAL_QUEUE_QR); + setTimeout(queueRefreshQR, INTERVAL_QUEUE_QR); } } @@ -183,7 +183,7 @@ const MobileAppVerification = observer(({ userPk }: Props) => { const user = await userStore.loadUser(userPk); if (!isUserConnected(user)) { - setUserTimeoutId(setTimeout(() => pollUserProfile(), INTERVAL_POLLING)); + setUserTimeoutId(setTimeout(pollUserProfile, INTERVAL_POLLING)); } else { setMobileAppIsCurrentlyConnected(true); } From d85f16c9f18c00d9bd128c9984addc18f1a4b7d3 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Wed, 7 Dec 2022 13:16:03 +0200 Subject: [PATCH 08/10] more user friendly loader + use own styles rather than overwriting grafana's one --- .../MobileAppVerification.module.scss | 17 +++---- .../MobileAppVerification.tsx | 44 +++++++++++++++---- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.module.scss b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.module.scss index 7c466508..cd4af8f4 100644 --- a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.module.scss +++ b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.module.scss @@ -39,25 +39,22 @@ opacity: 0.2; } -.blurry-loader { +.qr-loader { position: absolute; z-index: 10; top: 50%; left: 50%; transform: translate(-50%, -50%); + text-align: center; - > div { - margin-bottom: 0; - font-size: 16px; + &__text { text-align: center; + margin-bottom: 12px; + display: block; } - [data-testid='Spinner'] { // No other way to overwrite Grafana's LoadingPlaceholder unfortunately - width: 100%; - padding-top: 12px; - } - - [data-testid='Spinner'] i { // No other way to overwrite Grafana's LoadingPlaceholder unfortunately + i { // Overwrite Grafana's loading icon font-size: 32px; } + } diff --git a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx index bedcdf99..ef189e86 100644 --- a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx +++ b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { Icon, LoadingPlaceholder, VerticalGroup } from '@grafana/ui'; import cn from 'classnames/bind'; @@ -21,13 +21,15 @@ type Props = { userPk: User['pk']; }; -const INTERVAL_QUEUE_QR = 50000; +const INTERVAL_MIN_THROTTLING = 500; +const INTERVAL_QUEUE_QR = 10000; const INTERVAL_POLLING = 5000; const BACKEND = 'MOBILE_APP'; const MobileAppVerification = observer(({ userPk }: Props) => { const { userStore } = useStore(); + const isMounted = useRef(false); const [mobileAppIsCurrentlyConnected, setMobileAppIsCurrentlyConnected] = useState(isUserConnected()); const [fetchingQRCode, setFetchingQRCode] = useState(!mobileAppIsCurrentlyConnected); @@ -83,12 +85,15 @@ const MobileAppVerification = observer(({ userPk }: Props) => { }, [userPk, resetState]); useEffect(() => { + isMounted.current = true; + if (!isUserConnected()) { triggerTimeouts(); } // clear on unmount return () => { + isMounted.current = false; clearTimeouts(); }; }, []); @@ -129,11 +134,7 @@ const MobileAppVerification = observer(({ userPk }: Props) => { Open Grafana IRM mobile application and scan this code to sync it with your account.
- {isQRBlurry && ( -
- -
- )} + {isQRBlurry && }
); @@ -170,10 +171,24 @@ const MobileAppVerification = observer(({ userPk }: Props) => { const user = await userStore.loadUser(userPk); if (!isUserConnected(user)) { + let didCallThrottleWithNoEffect = false; + let isRequestDone = false; + + const throttle = () => { + if (!isMounted.current || !isRequestDone) {return;} + setIsQRBlurry(false); + setTimeout(queueRefreshQR, INTERVAL_QUEUE_QR); + }; + + setTimeout(throttle, INTERVAL_MIN_THROTTLING); setIsQRBlurry(true); + await fetchQRCode(false); - setIsQRBlurry(false); - setTimeout(queueRefreshQR, INTERVAL_QUEUE_QR); + + isRequestDone = true; + if (didCallThrottleWithNoEffect) { + throttle(); + } } } @@ -190,4 +205,15 @@ const MobileAppVerification = observer(({ userPk }: Props) => { } }); +function QRLoading() { + return ( +
+ + Regenerating QR code... + + +
+ ); +} + export default MobileAppVerification; From ebc62ab69f9df5db498643e3ea3261331afd7c46 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Wed, 7 Dec 2022 13:28:42 +0200 Subject: [PATCH 09/10] added max timeout of 10s to all jest tests for cases when it needs to wait timeout value --- grafana-plugin/jest.config.js | 2 ++ .../MobileAppVerification.tsx | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/grafana-plugin/jest.config.js b/grafana-plugin/jest.config.js index f70e7d25..ed696b01 100644 --- a/grafana-plugin/jest.config.js +++ b/grafana-plugin/jest.config.js @@ -20,4 +20,6 @@ module.exports = { }, setupFilesAfterEnv: ['/jest.setup.ts'], + + testTimeout: 10000, }; diff --git a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx index ef189e86..623b7718 100644 --- a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx +++ b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx @@ -22,7 +22,7 @@ type Props = { }; const INTERVAL_MIN_THROTTLING = 500; -const INTERVAL_QUEUE_QR = 10000; +const INTERVAL_QUEUE_QR = 50000; const INTERVAL_POLLING = 5000; const BACKEND = 'MOBILE_APP'; @@ -166,6 +166,10 @@ const MobileAppVerification = observer(({ userPk }: Props) => { } async function queueRefreshQR(): Promise { + if (!isMounted.current) { + return; + } + clearTimeout(refreshTimeoutId); setRefreshTimeoutId(undefined); @@ -175,7 +179,9 @@ const MobileAppVerification = observer(({ userPk }: Props) => { let isRequestDone = false; const throttle = () => { - if (!isMounted.current || !isRequestDone) {return;} + if (!isMounted.current || !isRequestDone) { + return; + } setIsQRBlurry(false); setTimeout(queueRefreshQR, INTERVAL_QUEUE_QR); }; @@ -193,6 +199,10 @@ const MobileAppVerification = observer(({ userPk }: Props) => { } async function pollUserProfile(): Promise { + if (!isMounted.current) { + return; + } + clearTimeout(userTimeoutId); setUserTimeoutId(undefined); From 84f0c48b96476326ba90b06912596f0731fbccfb Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Wed, 7 Dec 2022 13:45:23 +0200 Subject: [PATCH 10/10] fixed edge case --- .../MobileAppVerification/MobileAppVerification.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx index 623b7718..ed634a9f 100644 --- a/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx +++ b/grafana-plugin/src/containers/MobileAppVerification/MobileAppVerification.tsx @@ -179,9 +179,14 @@ const MobileAppVerification = observer(({ userPk }: Props) => { let isRequestDone = false; const throttle = () => { - if (!isMounted.current || !isRequestDone) { + if (!isMounted.current) { return; } + if (!isRequestDone) { + didCallThrottleWithNoEffect = true; + return; + } + setIsQRBlurry(false); setTimeout(queueRefreshQR, INTERVAL_QUEUE_QR); };