diff --git a/package.json b/package.json index 40d20c20..0743367e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@boomerang-io/carbon-addons-boomerang-react", "description": "Carbon Addons for Boomerang apps", - "version": "2.0.12", + "version": "2.0.14", "author": { "name": "Tim Bula", "email": "timrbula@gmail.com" @@ -65,12 +65,13 @@ "match-sorter": "6.3.1", "react-autosuggest": "10.1.0", "react-modal": "3.15.1", + "react-query": "3.34.12", "react-toastify": "9.0.7", "window-or-global": "1.0.1" }, "peerDependencies": { "@carbon/icons-react": "^10.38.0", - "axios": ">=0.26.0", + "axios": ">= 0.26.0 < 1" "carbon-components": "^10.25.0", "carbon-components-react": "^7.25.0", "carbon-icons": "^7.0.7", @@ -78,6 +79,7 @@ "prop-types": "^15.7.2", "react": "^17.0.0 || ^16.14.0", "react-dom": "^17.0.0 || ^16.14.0", + "react-query": "3.34.12", "react-router-dom": "^5.3.0", "yup": ">=0.32.11" }, @@ -112,7 +114,7 @@ "@testing-library/dom": "^8.11.2", "@testing-library/jest-dom": "^5.11.6", "@testing-library/react": "12.1.2", - "axios": "0.26.0", + "axios": "0.27.2" "axios-mock-adapter": "^1.20.0", "babel-plugin-dev-expression": "^0.2.3", "browserslist-config-carbon": "^10.6.1", diff --git a/src/components/PrivacyStatement/PrivacyStatement.js b/src/components/PrivacyStatement/PrivacyStatement.js index ca0d2080..7ce89a12 100644 --- a/src/components/PrivacyStatement/PrivacyStatement.js +++ b/src/components/PrivacyStatement/PrivacyStatement.js @@ -1,10 +1,11 @@ -import React, { useState, useEffect } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; -import axios from 'axios'; +import { useQuery, useMutation } from 'react-query'; import dompurify from 'dompurify'; import { Accordion, AccordionItem, Button } from 'carbon-components-react'; import { ModalHeader, ModalBody, ModalFooter } from 'carbon-components-react'; import { settings } from 'carbon-components'; +import { serviceUrl, resolver } from '../../config/servicesConfig'; import window from 'window-or-global'; import HeaderMenuModalWrapper from '../../internal/HeaderMenuModalWrapper'; @@ -39,44 +40,41 @@ PrivacyStatement.defaultProps = { }; function PrivacyStatement({ baseServiceUrl, platformName, platformEmail }) { - const [statement, setStatement] = useState(); - const [error, setError] = useState(); - const [alertError, setAlertError] = useState(); + const statementUrl = serviceUrl.getStatement({ baseServiceUrl }); - useEffect(() => { - axios(`${baseServiceUrl}/users/consents`) - .then((response) => setStatement(response.data)) - .catch((err) => setError(err)); - }, [baseServiceUrl]); + const { data: statement, error: statementError } = useQuery({ + queryKey: statementUrl, + queryFn: resolver.query(statementUrl), + }); - function handleSubmit({ closeAlertModal, closeModal }) { - axios - .put(`${baseServiceUrl}/users/consent`, { - version: statement.version, - hasConsented: false, - }) - .then(() => { - notify( - , - { containerId: `${prefix}--bmrg-header-notifications` } - ); - // want to clear out any error state - setError(false); - setAlertError(false); - closeAlertModal(); - closeModal(); - if (window.location) { - window.location.reload(true); - } - }) - .catch((err) => { - setAlertError(err); - closeAlertModal(); - }); + const { mutateAsync: mutateUserConsent, error: mutateUserConsentError } = useMutation(resolver.putUserConsent); + + const error = statementError || mutateUserConsentError; + + async function handleSubmit({ closeAlertModal, closeModal }) { + const body = { + hasConsented: false, + version: statement.version, + } + + try { + await mutateUserConsent({ baseServiceUrl, body }); + notify( + , + { containerId: `${prefix}--bmrg-header-notifications` } + ); + closeAlertModal(); + closeModal(); + if (window.location) { + window.location.reload(true); + } + } catch { + closeAlertModal(); + } } // TOOD: decide to do something if there is an error or not @@ -94,13 +92,8 @@ function PrivacyStatement({ baseServiceUrl, platformName, platformEmail }) { return ( <> { - closeModal(); - setError(''); - setAlertError(''); - }} - label={`Effective as of ${statement ? formatDateTimestamp(statement.effectiveDate) : '' - }`} + closeModal={closeModal} + label={`Effective as of ${statement ? formatDateTimestamp(statement.effectiveDate) : ''}`} title="Privacy Statement" /> @@ -130,9 +123,9 @@ function PrivacyStatement({ baseServiceUrl, platformName, platformEmail }) { .

- {alertError && ( + {mutateUserConsentError && (

- Failed to recieve deletion request. Please try again. + Failed to receive deletion request. Please try again.

)} diff --git a/src/components/PrivacyStatement/PrivacyStatement.spec.js b/src/components/PrivacyStatement/PrivacyStatement.spec.js index 8d8a2332..cf2ae2a4 100644 --- a/src/components/PrivacyStatement/PrivacyStatement.spec.js +++ b/src/components/PrivacyStatement/PrivacyStatement.spec.js @@ -1,4 +1,5 @@ import React from 'react'; +import { QueryClient, QueryClientProvider } from "react-query"; import { render, fireEvent, waitFor } from '@testing-library/react'; import PrivacyStatement from './PrivacyStatement.js'; @@ -10,6 +11,10 @@ import { PRIVACY_DATA } from './constants'; const baseServiceUrl = 'http://boomerang.com'; +const queryClient = new QueryClient({ + defaultOptions: { queries: { refetchOnWindowFocus: false }, mutations: { throwOnError: true } }, +}); + test('Privacy Statement error', async () => { /** * Simulate deleting account @@ -29,7 +34,9 @@ test('Privacy Statement error', async () => { mock.onPut(`${baseServiceUrl}/users/consent`).networkError(); const { getByText, getByRole, findByRole } = render( - + + + ); const btn = getByRole('button', { name: /Privacy Statement/i }); @@ -42,7 +49,7 @@ test('Privacy Statement error', async () => { fireEvent.click(confirmButton); await waitFor(() => - expect(getByText(/Failed to recieve deletion request. Please try again./i)).toBeInTheDocument() + expect(getByText(/Failed to receive deletion request. Please try again./i)).toBeInTheDocument() ); }); @@ -55,7 +62,11 @@ test('Privacy Statement success', async () => { mock.onGet(`${baseServiceUrl}/users/consents`).reply(200, PRIVACY_DATA); mock.onPut(`${baseServiceUrl}/users/consent`).reply(200); - const { getByRole, findByRole } = render(); + const { getByRole, findByRole } = render( + + + + ); const btn = getByRole('button', { name: /Privacy Statement/i }); fireEvent.click(btn); diff --git a/src/components/PrivacyStatement/PrivacyStatement.stories.js b/src/components/PrivacyStatement/PrivacyStatement.stories.js index bd4201d5..bf6f1493 100644 --- a/src/components/PrivacyStatement/PrivacyStatement.stories.js +++ b/src/components/PrivacyStatement/PrivacyStatement.stories.js @@ -8,6 +8,7 @@ // /* eslint-disable no-console */ import React from 'react'; +import { QueryClient, QueryClientProvider } from "react-query"; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; @@ -16,6 +17,10 @@ import { PRIVACY_DATA } from './constants'; const mock = new MockAdapter(axios); +const queryClient = new QueryClient({ + defaultOptions: { queries: { refetchOnWindowFocus: false }, mutations: { throwOnError: true } }, +}); + const props = () => ({ baseServiceUrl: 'http://ibm.com', }); @@ -26,7 +31,11 @@ export default { export const Default = () => { mock.onGet('http://ibm.com/users/consents').reply(200, PRIVACY_DATA); - return ; + return ( + + + + ); }; Default.story = { diff --git a/src/components/ProfileSettings/ProfileSettings.js b/src/components/ProfileSettings/ProfileSettings.js index 842acda9..378aa643 100644 --- a/src/components/ProfileSettings/ProfileSettings.js +++ b/src/components/ProfileSettings/ProfileSettings.js @@ -1,6 +1,6 @@ -import React, { useCallback, useState, useEffect } from 'react'; +import React , { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; -import axios from 'axios'; +import { useQuery, useMutation, useQueryClient } from 'react-query'; import { Button, Checkbox, @@ -18,6 +18,7 @@ import ErrorMessage from '../ErrorMessage'; import HeaderMenuUser from '../HeaderMenuUser'; import notify from '../Notifications/notify'; import ToastNotification from '../Notifications/ToastNotification'; +import { serviceUrl, resolver } from '../../config/servicesConfig'; import { settings } from 'carbon-components'; import sortBy from 'lodash.sortby'; @@ -30,68 +31,55 @@ ProfileSettings.propTypes = { }; function ProfileSettings({ baseServiceUrl, src, userName }) { + const queryClient = useQueryClient(); + const [initialTeams, setInitialTeams] = useState([]); const [teams, setTeams] = useState([]); - const [disableModal, setDisableModal] = useState([]); - const [isLoading, setIsLoading] = useState(); - const [isSubmitting, setIsSubmitting] = useState(); - const [isLoadingError, setIsLoadingError] = useState(); - const [isSubmitError, setIsSubmitError] = useState(); - - const fetchTeams = useCallback( - ({ showLoading }) => { - if (showLoading) { - setIsLoading(true); - } - axios(`${baseServiceUrl}/launchpad/user`) - .then((response) => { - const teams = response.data.lowerLevelGroups; - setTeams(teams); - setDisableModal(response.data.lowerLevelGroups === undefined); - setInitialTeams(teams); - }) - .catch((err) => setIsLoadingError(err)) - .then(() => { - setIsLoading(false); - }); - }, - [baseServiceUrl] + + const userUrl = serviceUrl.getLaunchpadUser({ baseServiceUrl }); + const profileUrl = serviceUrl.resourceUserProfile({ baseServiceUrl }); + + const { data: user, isLoading: userIsLoading, error: userError } = useQuery({ + queryKey: userUrl, + queryFn: resolver.query(userUrl), + }); + + const { mutateAsync: mutateUserProfile, isLoading: mutateUserProfileIsLoading, error: mutateUserProfileError } = useMutation(resolver.patchUserProfile, + { + onSuccess: () => { + queryClient.invalidateQueries(userUrl); + queryClient.invalidateQueries(profileUrl); + }, + } ); + + const disableModal = user?.lowerLevelGroups === undefined; useEffect(() => { - fetchTeams({ showLoading: true }); - }, [fetchTeams]); - - function handleCloseModal({ closeModal }) { - setIsLoadingError(false); - setIsSubmitError(false); - closeModal(); - fetchTeams({ showLoading: false }); - } + const teams = user?.lowerLevelGroups ?? []; + setInitialTeams(teams); + setTeams(teams); + }, [user]); - function handleSubmit({ closeModal }) { - setIsSubmitting(true); - setIsSubmitError(false); - axios - .patch(`${baseServiceUrl}/users/profile`, { - lowerLevelGroups: teams, - }) - .then(() => { - setIsSubmitting(false); - notify( - , - { containerId: `${prefix}--bmrg-header-notifications` } - ); - handleCloseModal({ closeModal }); - }) - .catch((err) => { - setIsSubmitting(false); - setIsSubmitError(true); - }); + async function handleSubmit({ closeModal }) { + const body = { + lowerLevelGroups: teams, + } + + try { + await mutateUserProfile({ baseServiceUrl, body }); + notify( + , + { containerId: `${prefix}--bmrg-header-notifications` } + ); + closeModal(); + } catch { + // noop + } } const visibleTeamCount = teams?.filter((team) => team?.visible)?.length ?? 0; @@ -127,13 +115,14 @@ function ProfileSettings({ baseServiceUrl, src, userName }) { setTeams(updatedTeams); } - if (disableModal) + if (disableModal) { return (

{userName ? userName : ''}

); + } return ( handleCloseModal({ closeModal })} + closeModal={closeModal} title={`User profile - ${userName}`} /> @@ -161,9 +150,9 @@ function ProfileSettings({ baseServiceUrl, src, userName }) { sensitive demos). You will not be able to access or view unchecked Teams from the sidebar, and cannot add items to them from Catalog.

- {isLoading ? ( + {userIsLoading ? ( - ) : isLoadingError ? ( + ) : userError ? ( ) : teams?.length > 0 ? ( @@ -209,7 +198,7 @@ function ProfileSettings({ baseServiceUrl, src, userName }) { )}
- {isSubmitError && ( + {mutateUserProfileError && (
)} -
diff --git a/src/components/ProfileSettings/ProfileSettings.spec.js b/src/components/ProfileSettings/ProfileSettings.spec.js index 974d78c2..3d93a0bb 100644 --- a/src/components/ProfileSettings/ProfileSettings.spec.js +++ b/src/components/ProfileSettings/ProfileSettings.spec.js @@ -1,5 +1,6 @@ import React from 'react'; import { render, fireEvent, waitFor } from '@testing-library/react'; +import { QueryClient, QueryClientProvider } from "react-query"; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import { axe } from 'jest-axe'; @@ -8,13 +9,19 @@ import { PROFILE_SETTINGS_DATA } from './constants'; const baseServiceUrl = 'http://boomerang.com'; +const queryClient = new QueryClient({ + defaultOptions: { queries: { refetchOnWindowFocus: false }, mutations: { throwOnError: true } }, +}); + test('Profile Settings success', async () => { const mock = new MockAdapter(axios); mock.onGet(`${baseServiceUrl}/launchpad/user`).reply(200, PROFILE_SETTINGS_DATA); mock.onPatch(`${baseServiceUrl}/users/profile`).networkError(); const { findByText, getByLabelText, getByText, queryByText } = render( - + + + ); const userBtn = getByText('Boomerang Joe'); @@ -46,7 +53,9 @@ mock.onPatch(`${baseServiceUrl}/users/profile`).reply(500); test('Profile Settings error', async () => { const { findByText, findByLabelText, getByText } = render( - + + + ); const userBtn = getByText('Boomerang Joe'); @@ -63,7 +72,9 @@ test('Profile Settings error', async () => { test('Profile Settings accessibility', async () => { const { container } = render( - + + + ); const results = await axe(container); expect(results).toHaveNoViolations(); diff --git a/src/components/ProfileSettings/ProfileSettings.stories.js b/src/components/ProfileSettings/ProfileSettings.stories.js index 69e116e6..c41cd699 100644 --- a/src/components/ProfileSettings/ProfileSettings.stories.js +++ b/src/components/ProfileSettings/ProfileSettings.stories.js @@ -1,26 +1,32 @@ import React from 'react'; +import { QueryClient, QueryClientProvider } from "react-query"; import axios from 'axios'; import ProfileSettings from './ProfileSettings'; import MockAdapter from 'axios-mock-adapter'; import { PROFILE_SETTINGS_DATA } from './constants'; -const mock = new MockAdapter(axios); -mock.onGet('https://ibm.com/launchpad/user').reply(200, PROFILE_SETTINGS_DATA); -mock.onPatch('https://ibm.com/users/profile').reply(200); +const queryClient = new QueryClient({ + defaultOptions: { queries: { refetchOnWindowFocus: false }, mutations: { throwOnError: true } }, +}); export default { title: 'ProfileSettings', }; export const Default = () => { + const mock = new MockAdapter(axios); + mock.onGet('https://ibm.com/launchpad/user').reply(200, PROFILE_SETTINGS_DATA); + mock.onPatch('https://ibm.com/users/profile').reply(200); return ( -
- -
+ +
+ +
+
); }; diff --git a/src/components/UIShell/UIShell.js b/src/components/UIShell/UIShell.js index 1cb18a96..fc2dfd87 100644 --- a/src/components/UIShell/UIShell.js +++ b/src/components/UIShell/UIShell.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; - +import { QueryClientProvider } from "react-query"; import Header from '../Header'; // Using default export import HeaderMenuButton from '../HeaderMenuButton'; import HeaderMenuLink from '../HeaderMenuLink'; @@ -10,6 +10,7 @@ import Feedback from '../Feedback'; import PrivacyStatement from '../PrivacyStatement'; import SignOut from '../SignOut'; import GdprRedirectModal from '../GdprRedirectModal'; +import { queryClient } from "../../config/servicesConfig"; UIShell.propTypes = { /** @@ -214,7 +215,7 @@ function UIShell({ renderPrivacyStatement === false || features?.['consent.enabled'] === false; return ( - <> +
) : null} - + ); } diff --git a/src/config/servicesConfig.js b/src/config/servicesConfig.js new file mode 100644 index 00000000..d43e447f --- /dev/null +++ b/src/config/servicesConfig.js @@ -0,0 +1,19 @@ +import axios from "axios"; +import { QueryClient } from "react-query"; + +export const queryClient = new QueryClient({ + defaultOptions: { queries: { refetchOnWindowFocus: false }, mutations: { throwOnError: true } }, +}); + +export const serviceUrl = { + getStatement: ({ baseServiceUrl }) => `${baseServiceUrl}/users/consents`, + getLaunchpadUser: ({ baseServiceUrl }) => `${baseServiceUrl}/launchpad/user`, + resourceUserConsent: ({ baseServiceUrl }) => `${baseServiceUrl}/users/consent`, + resourceUserProfile: ({ baseServiceUrl }) => `${baseServiceUrl}/users/profile`, +} + +export const resolver = { + query: (url, config) => () => axios.get(url, config).then((response) => response.data), + patchUserProfile: ({ baseServiceUrl, body }) => axios.patch(serviceUrl.resourceUserProfile({ baseServiceUrl }), body), + putUserConsent: ({ baseServiceUrl, body }) => axios.put(serviceUrl.resourceUserConsent({ baseServiceUrl }), body), +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index fc844b07..122727bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1154,6 +1154,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.6.2": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" + integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/standalone@^7.4.5": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.18.9.tgz#27d9bccac590df9e2d34b632e5d600f68b3fe581" @@ -5373,7 +5380,7 @@ bfj@^7.0.2: hoopy "^0.1.4" tryer "^1.0.1" -big-integer@^1.6.7: +big-integer@^1.6.16, big-integer@^1.6.7: version "1.6.51" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== @@ -5533,6 +5540,20 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" +broadcast-channel@^3.4.1: + version "3.7.0" + resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.7.0.tgz#2dfa5c7b4289547ac3f6705f9c00af8723889937" + integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg== + dependencies: + "@babel/runtime" "^7.7.2" + detect-node "^2.1.0" + js-sha3 "0.8.0" + microseconds "0.2.0" + nano-time "1.0.0" + oblivious-set "1.0.0" + rimraf "3.0.2" + unload "2.2.0" + brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" @@ -7310,7 +7331,7 @@ detect-node-es@^1.1.0: resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== -detect-node@^2.0.4: +detect-node@^2.0.4, detect-node@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== @@ -11190,6 +11211,11 @@ js-file-download@0.4.12: resolved "https://registry.yarnpkg.com/js-file-download/-/js-file-download-0.4.12.tgz#10c70ef362559a5b23cdbdc3bd6f399c3d91d821" integrity sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg== +js-sha3@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + js-string-escape@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" @@ -12047,7 +12073,7 @@ marksy@^8.0.0: he "^1.2.0" marked "^0.3.12" -match-sorter@6.3.1: +match-sorter@6.3.1, match-sorter@^6.0.2: version "6.3.1" resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.1.tgz#98cc37fda756093424ddf3cbc62bfe9c75b92bda" integrity sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw== @@ -12268,6 +12294,11 @@ micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" +microseconds@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39" + integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA== + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -12569,6 +12600,13 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916" integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA== +nano-time@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef" + integrity sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA== + dependencies: + big-integer "^1.6.16" + nanoclone@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4" @@ -13175,6 +13213,11 @@ objectorarray@^1.0.5: resolved "https://registry.yarnpkg.com/objectorarray/-/objectorarray-1.0.5.tgz#2c05248bbefabd8f43ad13b41085951aac5e68a5" integrity sha512-eJJDYkhJFFbBBAxeh8xW+weHlkI28n2ZdQV/J/DNfWfSKlGEf2xcfAbZTv3riEXHAhL9SVOTs2pRmXiSTf78xg== +oblivious-set@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566" + integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw== + obuf@^1.0.0, obuf@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" @@ -15151,6 +15194,15 @@ react-popper@^1.3.7: typed-styles "^0.0.7" warning "^4.0.2" +react-query@3.34.12: + version "3.34.12" + resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.34.12.tgz#dcaaf7b629f0868aae8afef9fb7692f6ea7643bf" + integrity sha512-flDdudQVH4CqE+kNYYYyo4g2Yjek3H/36G3b9bK5oe26jD5gFnx+PPwnq0gayq5z2dcSfr2z4+drvuyeZ3A5QQ== + dependencies: + "@babel/runtime" "^7.5.5" + broadcast-channel "^3.4.1" + match-sorter "^6.0.2" + react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" @@ -15891,6 +15943,13 @@ rfdc@^1.3.0: resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== +rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + rimraf@^2.5.4, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -15898,13 +15957,6 @@ rimraf@^2.5.4, rimraf@^2.6.3: dependencies: glob "^7.1.3" -rimraf@^3.0.0, rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -17946,6 +17998,14 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== +unload@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7" + integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA== + dependencies: + "@babel/runtime" "^7.6.2" + detect-node "^2.0.4" + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"