Merge pull request #40 from ente-io/sidebar

Sidebar
This commit is contained in:
Vishnu Mohandas 2021-03-13 12:55:41 +05:30 committed by GitHub
commit 774da8994c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 385 additions and 52 deletions

1
.gitignore vendored
View file

@ -37,3 +37,4 @@ out_functions
out_publish
.env
/.vscode/

View file

@ -27,6 +27,7 @@
"photoswipe": "file:./thirdparty/photoswipe",
"react": "16.13.1",
"react-bootstrap": "^1.3.0",
"react-burger-menu": "^3.0.4",
"react-dom": "16.13.1",
"react-dropzone": "^11.2.4",
"react-top-loading-bar": "^2.0.1",

View file

@ -0,0 +1,26 @@
import React from 'react';
import { Button, Modal } from 'react-bootstrap';
import constants from 'utils/strings/constants';
function ChangeDisabledMessage(props) {
return (
<Modal
{...props}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Body style={{ padding: '24px' }}>
<Modal.Title id="contained-modal-title-vcenter">
{constants.SUBSCRIPTION_CHANGE_DISABLED}
</Modal.Title>
</Modal.Body>
<Modal.Footer style={{ borderTop: 'none' }}>
<Button variant="secondary" onClick={props.onHide}>
{constants.CLOSE}
</Button>
</Modal.Footer>
</Modal>
);
}
export default ChangeDisabledMessage;

View file

@ -1,5 +1,6 @@
import React from 'react';
import styled from 'styled-components';
import constants from 'utils/strings/constants';
export const getColor = (props) => {
if (props.isDragActive) {
@ -43,6 +44,8 @@ type Props = React.PropsWithChildren<{
isDragActive;
onDragLeave;
onDragEnter;
setNavbarIconView;
navbarIconView;
}>;
export default function FullScreenDropZone(props: Props) {
@ -54,7 +57,7 @@ export default function FullScreenDropZone(props: Props) {
onDragLeave={props.onDragLeave}
isDragActive={props.isDragActive}
>
drop to backup your files
{constants.UPLOAD_DROPZONE_MESSAGE}
</Overlay>
)}
{props.children}

View file

@ -0,0 +1,95 @@
import React, { useEffect, useState } from 'react';
import { slide as Menu } from 'react-burger-menu';
import { Button } from 'react-bootstrap';
import ConfirmLogout from 'components/ConfirmLogout';
import Spinner from 'react-bootstrap/Spinner';
import subscriptionService, {
Subscription,
} from 'services/subscriptionService';
import ChangeDisabledMessage from './ChangeDisabledMessage';
import constants from 'utils/strings/constants';
import { logoutUser } from 'services/userService';
import { getData, LS_KEYS } from 'utils/storage/localStorage';
import { getToken } from 'utils/common/key';
interface Props {
setNavbarIconView;
}
export default function Sidebar(props: Props) {
const [logoutModalView, setLogoutModalView] = useState(false);
const [
changeDisabledMessageModalView,
setChangeDisabledMessageModalView,
] = useState(false);
function showLogoutModal() {
setLogoutModalView(true);
}
function closeLogoutModal() {
setLogoutModalView(false);
}
const [usage, SetUsage] = useState<string>(null);
const subscription: Subscription = getData(LS_KEYS.SUBSCRIPTION);
const [isOpen, setIsOpen] = useState(false);
useEffect(() => {
const main = async () => {
if (!isOpen) {
return;
}
const usage = await subscriptionService.getUsage();
SetUsage(usage);
};
main();
}, [isOpen]);
const logout = async () => {
setLogoutModalView(false);
setIsOpen(false);
props.setNavbarIconView(false);
logoutUser();
};
return (
<Menu
isOpen={isOpen}
onStateChange={(state) => setIsOpen(state.isOpen)}
itemListElement="div"
>
<div style={{ outline: 'none' }}>
<h4 style={{ marginBottom: '12px' }}>{constants.SUBSCRIPTION_PLAN}</h4>
{
subscription?.productID == "free" ? constants.FREE_SUBSCRIPTION_INFO(subscription?.expiryTime) : constants.PAID_SUBSCRIPTION_INFO(subscription?.expiryTime)
}
</div>
<div style={{ outline: 'none', marginTop: '40px' }}>
<h4 style={{ marginBottom: '12px' }}>{constants.USAGE_DETAILS}</h4>
<div>
{usage ? (
constants.USAGE_INFO(
usage,
Math.ceil(
Number(
subscriptionService.convertBytesToGBs(
subscription?.storage
)
)
)
)
) : (
<Spinner animation="border" />
)}
</div>
</div>
<>
<ConfirmLogout
show={logoutModalView}
onHide={closeLogoutModal}
logout={logout}
/>
<h4 style={{ cursor: 'pointer', color: '#F96C6C', marginTop: '40px' }} onClick={showLogoutModal}>
logout
</h4>
</>
</Menu>
);
}

View file

@ -2,23 +2,19 @@ import React, { useCallback, useEffect, useState } from 'react';
import styled, { createGlobalStyle } from 'styled-components';
import Navbar from 'components/Navbar';
import constants from 'utils/strings/constants';
import Button from 'react-bootstrap/Button';
import Spinner from 'react-bootstrap/Spinner';
import { clearKeys } from 'utils/storage/sessionStorage';
import { clearData, getData, LS_KEYS } from 'utils/storage/localStorage';
import { getData, LS_KEYS } from 'utils/storage/localStorage';
import { useRouter } from 'next/router';
import Container from 'components/Container';
import PowerSettings from 'components/power_settings';
import Head from 'next/head';
import 'bootstrap/dist/css/bootstrap.min.css';
import 'photoswipe/dist/photoswipe.css';
import localForage from 'utils/storage/localForage';
import UploadButton from 'pages/gallery/components/UploadButton';
import FullScreenDropZone from 'components/FullScreenDropZone';
import { sentryInit } from '../utils/sentry';
import ConfirmLogout from 'components/ConfirmLogout';
import { useDropzone } from 'react-dropzone';
import Sidebar from 'components/Sidebar';
const GlobalStyles = createGlobalStyle`
html, body {
@ -81,7 +77,10 @@ const GlobalStyles = createGlobalStyle`
width:90vw;
max-width:960px!important;
}
.modal .modal-header, .modal .modal-footer {
.modal {
z-index: 2000;
}
.modal .modal-header, .modal .modal-footer {
border-color: #444 !important;
}
.modal .modal-header .close {
@ -112,12 +111,9 @@ const GlobalStyles = createGlobalStyle`
background-size: cover;
border: none;
}
.btn-primary ,.btn:focus {
.btn-primary {
background: #2dc262;
border-color: #29a354;
padding: 8px;
padding-left: 24px;
padding-right: 24px;
}
.btn-primary:hover {
background-color: #29a354;
@ -147,6 +143,25 @@ const GlobalStyles = createGlobalStyle`
width: 500px;
max-width:100%;
}
.bm-burger-button {
position: fixed;
width: 28px;
height: 20px;
left: 36px;
margin-top: 30px;
}
.bm-burger-bars {
background: #bdbdbd;
}
.bm-menu {
background: #131313;
padding: 2.5em 1.5em 0;
font-size: 1.15em;
color:#fff
}
.bm-cross {
background: #fff;
}
`;
const Image = styled.img`
@ -165,16 +180,9 @@ export default function App({ Component, pageProps, err }) {
const router = useRouter();
const [user, setUser] = useState();
const [loading, setLoading] = useState(false);
const [uploadButtonView, setUploadButtonView] = useState(false);
const [navbarIconView, setNavbarIconView] = useState(false);
const [uploadModalView, setUploadModalView] = useState(false);
const [logoutModalView, setLogoutModalView] = useState(false);
function showLogoutModal() {
setLogoutModalView(true);
}
function closeLogoutModal() {
setLogoutModalView(false);
}
function closeUploadModal() {
setUploadModalView(false);
}
@ -204,16 +212,6 @@ export default function App({ Component, pageProps, err }) {
});
}, []);
const logout = async () => {
setLogoutModalView(false);
clearKeys();
clearData();
setUploadButtonView(false);
localForage.clear();
const cache = await caches.delete('thumbs');
router.push('/');
};
const onDropAccepted = useCallback(() => {
showUploadModal();
setIsDragActive(false);
@ -234,25 +232,18 @@ export default function App({ Component, pageProps, err }) {
isDragActive={isDragActive}
onDragEnter={onDragEnter}
onDragLeave={onDragLeave}
navbarIconView={navbarIconView}
setNavbarIconView={setNavbarIconView}
>
<Head>
<title>{constants.TITLE}</title>
<script async src={`https://sa.ente.io/latest.js`} />
</Head>
<GlobalStyles />
<div style={{ display: navbarIconView ? 'block' : 'none' }}>
<Sidebar setNavbarIconView={setNavbarIconView} />
</div>
<Navbar>
{user && (
<>
<ConfirmLogout
show={logoutModalView}
onHide={closeLogoutModal}
logout={logout}
/>
<Button variant="link" onClick={showLogoutModal}>
<PowerSettings />
</Button>
</>
)}
<FlexContainer>
<Image
style={{ height: '24px' }}
@ -260,7 +251,7 @@ export default function App({ Component, pageProps, err }) {
src="/icon.svg"
/>
</FlexContainer>
{uploadButtonView && <UploadButton openFileUploader={open} />}
{navbarIconView && <UploadButton openFileUploader={open} />}
</Navbar>
{loading ? (
<Container>
@ -275,7 +266,7 @@ export default function App({ Component, pageProps, err }) {
uploadModalView={uploadModalView}
showUploadModal={showUploadModal}
closeUploadModal={closeUploadModal}
setUploadButtonView={setUploadButtonView}
setUploadButtonView={setNavbarIconView}
err={err}
/>
)}

View file

@ -12,6 +12,7 @@ import * as Yup from 'yup';
import { keyAttributes } from 'types';
import { setKey, SESSION_KEYS, getKey } from 'utils/storage/sessionStorage';
import CryptoWorker from 'utils/crypto/cryptoWorker';
import { logoutUser } from 'services/userService';
const Image = styled.img`
width: 200px;
@ -87,9 +88,14 @@ export default function Credentials() {
return (
<Container>
{/* <Image alt="vault" src="/vault.png" /> */}
<Card style={{ minWidth: '320px', padding: '40px 30px' }} className="text-center">
<Card
style={{ minWidth: '320px', padding: '40px 30px' }}
className="text-center"
>
<Card.Body>
<Card.Title style={{ marginBottom: '24px' }}>{constants.ENTER_PASSPHRASE}</Card.Title>
<Card.Title style={{ marginBottom: '24px' }}>
{constants.ENTER_PASSPHRASE}
</Card.Title>
<Formik<formValues>
initialValues={{ passphrase: '' }}
onSubmit={verifyPassphrase}
@ -119,7 +125,7 @@ export default function Credentials() {
onBlur={handleBlur('passphrase')}
isInvalid={Boolean(
touched.passphrase &&
errors.passphrase
errors.passphrase
)}
disabled={loading}
autoFocus={true}
@ -131,6 +137,12 @@ export default function Credentials() {
<Button block type="submit" disabled={loading}>
{constants.VERIFY_PASSPHRASE}
</Button>
<br />
<div>
<a href="#" onClick={logoutUser}>
{constants.LOGOUT}
</a>
</div>
</Form>
)}
</Formik>

View file

@ -9,7 +9,7 @@ import { LS_KEYS, getData, setData } from 'utils/storage/localStorage';
import { useRouter } from 'next/router';
import { Formik, FormikHelpers } from 'formik';
import * as Yup from 'yup';
import { verifyOtt, getOtt } from 'services/userService';
import { verifyOtt, getOtt, logoutUser } from 'services/userService';
const Image = styled.img`
width: 350px;
@ -54,6 +54,7 @@ export default function Verify() {
id: resp.data.id,
});
setData(LS_KEYS.KEY_ATTRIBUTES, resp.data.keyAttributes);
setData(LS_KEYS.SUBSCRIPTION, resp.data.subscription);
if (resp.data.keyAttributes?.encryptedKey) {
router.push('/credentials');
} else {
@ -84,7 +85,11 @@ export default function Verify() {
<Container>
<Card style={{ minWidth: '300px' }} className="text-center">
<Card.Body>
<Card.Title style={{ fontWeight: 'bold', marginBottom: '24px' }}>{constants.VERIFY_EMAIL}</Card.Title>
<Card.Title
style={{ fontWeight: 'bold', marginBottom: '24px' }}
>
{constants.VERIFY_EMAIL}
</Card.Title>
{constants.EMAIL_SENT({ email })}
{constants.CHECK_INBOX}
<br />
@ -136,6 +141,13 @@ export default function Verify() {
<span>{constants.SENDING}</span>
)}
{resend === 2 && <span>{constants.SENT}</span>}
<br />
<br />
<div>
<a href="#" onClick={logoutUser}>
{constants.CHANGE_EMAIL}
</a>
</div>
</Form>
)}
</Formik>

View file

@ -0,0 +1,37 @@
import { getEndpoint } from 'utils/common/apiUtil';
import { getToken } from 'utils/common/key';
import HTTPService from './HTTPService';
const ENDPOINT = getEndpoint();
export interface Subscription {
id: number;
userID: number;
productID: string;
storage: number;
originalTransactionID: string;
expiryTime: number;
paymentProvider: string;
}
class SubscriptionService {
async getUsage() {
try {
const response = await HTTPService.get(
`${ENDPOINT}/billing/usage`,
{ startTime: 0, endTime: Date.now() * 1000 },
{
'X-Auth-Token': getToken(),
}
);
return this.convertBytesToGBs(response.data.usage);
} catch (e) {
console.error('error getting usage', e);
}
}
public convertBytesToGBs(bytes): string {
return (bytes / (1024 * 1024 * 1024)).toFixed(2);
}
}
export default new SubscriptionService();

View file

@ -1,6 +1,10 @@
import HTTPService from './HTTPService';
import { keyAttributes } from 'types';
import { getEndpoint } from 'utils/common/apiUtil';
import { clearKeys } from 'utils/storage/sessionStorage';
import router from 'next/router';
import { clearData } from 'utils/storage/localStorage';
import localForage from 'localforage';
const ENDPOINT = getEndpoint();
@ -36,3 +40,11 @@ export const putAttributes = (
}
);
};
export const logoutUser = async () => {
clearKeys();
clearData();
localForage.clear();
const cache = await caches.delete('thumbs');
router.push('/');
};

View file

@ -2,6 +2,7 @@ export enum LS_KEYS {
USER = 'user',
SESSION = 'session',
KEY_ATTRIBUTES = 'keyAttributes',
SUBSCRIPTION = 'subscription',
}
export const setData = (key: LS_KEYS, value: object) => {

View file

@ -23,7 +23,7 @@ const englishConstants = {
),
CHECK_INBOX: 'please check your inbox (and spam) to complete verification',
ENTER_OTT: 'verification code',
RESEND_MAIL: 'did not get email?',
RESEND_MAIL: 'resend?',
VERIFY: 'verify',
UNKNOWN_ERROR: 'something went wrong, please try again',
INVALID_CODE: 'invalid verification code',
@ -81,11 +81,17 @@ const englishConstants = {
INSTALL_MOBILE_APP: () => (
<div>
install our{' '}
<a href="https://play.google.com/store/apps/details?id=io.ente.photos" target="_blank">
<a
href="https://play.google.com/store/apps/details?id=io.ente.photos"
target="_blank"
>
android
</a>{' '}
or{' '}
<a href="https://apps.apple.com/in/app/ente-photos/id1542026904" target="_blank">
<a
href="https://apps.apple.com/in/app/ente-photos/id1542026904"
target="_blank"
>
ios app{' '}
</a>
to automatically backup all your photos
@ -94,6 +100,34 @@ const englishConstants = {
LOGOUT: 'logout',
LOGOUT_WARNING: 'sure you want to logout?',
CANCEL: 'cancel',
SUBSCRIPTION_CHANGE_DISABLED:
'sorry, this operation is currently not supported on the web, please check your mobile app',
SUBSCRIPTION_PLAN: 'subscription plan',
USAGE_DETAILS: 'usage',
FREE_SUBSCRIPTION_INFO: (expiryTime) => (
<>
<p>
you are on the <strong>free</strong> plan {' '} that expires on {' '}
{new Date(expiryTime / 1000).toLocaleDateString("en-US", { year: 'numeric', month: 'long', day: 'numeric' })}
</p>
</>
),
PAID_SUBSCRIPTION_INFO: (expiryTime) => (
<>
<p>
your subscription will renew on {' '}
{new Date(expiryTime / 1000).toLocaleDateString("en-US", { year: 'numeric', month: 'long', day: 'numeric' })}
</p>
</>
),
USAGE_INFO: (usage, quota) => (
<p>
you have used {usage} GB out of your {quota} GB quota
</p>
),
UPLOAD_DROPZONE_MESSAGE: 'drop to backup your files',
CHANGE: 'change',
CHANGE_EMAIL: 'change email ?',
};
export default englishConstants;

108
yarn.lock
View file

@ -2597,6 +2597,11 @@ ally.js@1.4.1:
css.escape "^1.5.0"
platform "1.3.3"
amdefine@>=0.0.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=
anser@1.4.9:
version "1.4.9"
resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.9.tgz#1f85423a5dcf8da4631a341665ff675b96845760"
@ -2725,11 +2730,25 @@ assign-symbols@^1.0.0:
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
ast-transform@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/ast-transform/-/ast-transform-0.0.0.tgz#74944058887d8283e189d954600947bc98fe0062"
integrity sha1-dJRAWIh9goPhidlUYAlHvJj+AGI=
dependencies:
escodegen "~1.2.0"
esprima "~1.0.4"
through "~2.3.4"
ast-types@0.13.2:
version "0.13.2"
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48"
integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA==
ast-types@^0.7.0:
version "0.7.8"
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.7.8.tgz#902d2e0d60d071bdcd46dc115e1809ed11c138a9"
integrity sha1-kC0uDWDQcb3NRtwRXhgJ7RHBOKk=
async-each@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
@ -2929,6 +2948,13 @@ brorand@^1.0.1, brorand@^1.1.0:
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
browser-resolve@^1.8.1:
version "1.11.3"
resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6"
integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==
dependencies:
resolve "1.1.7"
browserify-aes@^1.0.0, browserify-aes@^1.0.4:
version "1.2.0"
resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
@ -2960,6 +2986,15 @@ browserify-des@^1.0.0:
inherits "^2.0.1"
safe-buffer "^5.1.2"
browserify-optional@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/browserify-optional/-/browserify-optional-1.0.1.tgz#1e13722cfde0d85f121676c2a72ced533a018869"
integrity sha1-HhNyLP3g2F8SFnbCpyztUzoBiGk=
dependencies:
ast-transform "0.0.0"
ast-types "^0.7.0"
browser-resolve "^1.8.1"
browserify-rsa@^4.0.0, browserify-rsa@^4.0.1:
version "4.1.0"
resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d"
@ -3943,6 +3978,17 @@ escape-string-regexp@^1.0.5:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
escodegen@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.2.0.tgz#09de7967791cc958b7f89a2ddb6d23451af327e1"
integrity sha1-Cd55Z3kcyVi3+Jot220jRRrzJ+E=
dependencies:
esprima "~1.0.4"
estraverse "~1.5.0"
esutils "~1.0.0"
optionalDependencies:
source-map "~0.1.30"
eslint-scope@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"
@ -3951,6 +3997,11 @@ eslint-scope@^4.0.3:
esrecurse "^4.1.0"
estraverse "^4.1.1"
esprima@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.0.4.tgz#9f557e08fc3b4d26ece9dd34f8fbf476b62585ad"
integrity sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=
esrecurse@^4.1.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
@ -3968,6 +4019,11 @@ estraverse@^5.2.0:
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880"
integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==
estraverse@~1.5.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.5.1.tgz#867a3e8e58a9f84618afb6c2ddbcd916b7cbaf71"
integrity sha1-hno+jlip+EYYr7bC3bzZFrfLr3E=
estree-walker@^0.6.0, estree-walker@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362"
@ -3978,11 +4034,21 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
esutils@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.0.0.tgz#8151d358e20c8acc7fb745e7472c0025fe496570"
integrity sha1-gVHTWOIMisx/t0XnRywAJf5JZXA=
etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
eve@~0.5.1:
version "0.5.4"
resolved "https://registry.yarnpkg.com/eve/-/eve-0.5.4.tgz#67d080b9725291d7e389e34c26860dd97f1debaa"
integrity sha1-Z9CAuXJSkdfjieNMJoYN2X8d66o=
event-target-shim@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
@ -6368,6 +6434,17 @@ react-bootstrap@^1.3.0:
uncontrollable "^7.0.0"
warning "^4.0.3"
react-burger-menu@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/react-burger-menu/-/react-burger-menu-3.0.4.tgz#936418aa6d35da52a6a9436ffb2b33432a56ea8e"
integrity sha512-6+zufJT7ZFeUGg3apJEzRjirkwH80aLcZSp9OqDwRGwvfM4a18vzmNNT3cr4WDl+CQ5PtR268l743VAb65yCdw==
dependencies:
browserify-optional "^1.0.0"
classnames "^2.2.6"
eve "~0.5.1"
prop-types "^15.7.2"
snapsvg-cjs "0.0.6"
react-dom@16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f"
@ -6615,6 +6692,11 @@ resolve-url@^0.2.1:
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
resolve@1.1.7:
version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
resolve@^1.3.2, resolve@^1.8.1:
version "1.20.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
@ -6895,6 +6977,20 @@ snapdragon@^0.8.1:
source-map-resolve "^0.5.0"
use "^3.1.0"
snapsvg-cjs@0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/snapsvg-cjs/-/snapsvg-cjs-0.0.6.tgz#3b2f56af2573d3d364c3ed5bf8885745f4d2dde1"
integrity sha1-Oy9WryVz09Nkw+1b+IhXRfTS3eE=
dependencies:
snapsvg "0.5.1"
snapsvg@0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/snapsvg/-/snapsvg-0.5.1.tgz#0caf52c79189a290746fc446cc5e863f6bdddfe3"
integrity sha1-DK9Sx5GJopB0b8RGzF6GP2vd3+M=
dependencies:
eve "~0.5.1"
source-list-map@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
@ -6946,6 +7042,13 @@ source-map@^0.5.0, source-map@^0.5.6:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
source-map@~0.1.30:
version "0.1.43"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346"
integrity sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=
dependencies:
amdefine ">=0.0.4"
split-string@^3.0.1, split-string@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
@ -7219,6 +7322,11 @@ through2@^2.0.0:
readable-stream "~2.3.6"
xtend "~4.0.1"
through@~2.3.4:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
timers-browserify@^2.0.4:
version "2.0.12"
resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee"