diff --git a/.gitignore b/.gitignore index 1d2547d9c..3eb23ec54 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ /node_modules /.pnp .pnp.js -package-lock.json # testing /coverage diff --git a/package.json b/package.json index d098b1161..8a0d78f45 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "react-dom": "16.13.1", "react-dropzone": "^11.2.4", "react-photoswipe": "^1.3.0", + "react-top-loading-bar": "^2.0.1", "react-virtualized-auto-sizer": "^1.0.2", "react-window": "^1.8.6", "react-window-infinite-loader": "^1.0.5", @@ -44,7 +45,7 @@ "@types/yup": "^0.29.7", "babel-plugin-styled-components": "^1.11.1", "next-on-netlify": "^2.4.0", - "typescript": "^4.0.2", + "typescript": "^4.1.3", "worker-plugin": "^5.0.0" }, "standard": { diff --git a/public/fav-button.png b/public/fav-button.png new file mode 100644 index 000000000..3ec964d07 Binary files /dev/null and b/public/fav-button.png differ diff --git a/src/components/FavButton.tsx b/src/components/FavButton.tsx index bb45f7d71..3be4b5f07 100644 --- a/src/components/FavButton.tsx +++ b/src/components/FavButton.tsx @@ -8,15 +8,16 @@ const HeartUI = styled.button<{ width: ${props => props.size}px; height: ${props => props.size}px; float:right; - background: url("data:image/png;base64,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") no-repeat; + background: url("/fav-button.png") no-repeat; cursor: pointer; background-size: cover; - ${({ isClick, size }) => isClick && `background-position: -${28 * size}px 2px;transition: background 1s steps(28);`} + border: none; + ${({ isClick, size }) => isClick && `background-position: -${28 * size}px;transition: background 1s steps(28);`} `; export default function FavButton({ isClick, onClick, size }) { return ( - + ); } \ No newline at end of file diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 6d5989cf5..a46623f4d 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -161,7 +161,6 @@ export default function App({ Component, pageProps }) { ente.io | Privacy friendly alternative to Google Photos diff --git a/src/pages/gallery/components/CreateCollection.tsx b/src/pages/gallery/components/CreateCollection.tsx index 0930d77d6..d7d0bf12b 100644 --- a/src/pages/gallery/components/CreateCollection.tsx +++ b/src/pages/gallery/components/CreateCollection.tsx @@ -16,11 +16,11 @@ export default function CreateCollection(props) { if (acceptedFiles == null) return; let commonPathPrefix: string = (() => { - const A: string[] = acceptedFiles.map(files => files.path); - - let a1 = A[0], a2 = A[A.length - 1], L = a1.length, i = 0; - while (i < L && a1.charAt(i) === a2.charAt(i)) i++; - return a1.substring(0, i); + const paths: string[] = acceptedFiles.map(files => files.path); + paths.sort(); + let firstPath = paths[0], lastPath = paths[paths.length - 1], L = firstPath.length, i = 0; + while (i < L && firstPath.charAt(i) === lastPath.charAt(i)) i++; + return firstPath.substring(0, i); })(); if (commonPathPrefix) commonPathPrefix = commonPathPrefix.substr(1, commonPathPrefix.lastIndexOf('/') - 1); diff --git a/src/pages/gallery/components/PreviewCard.tsx b/src/pages/gallery/components/PreviewCard.tsx index 9addf1a85..5b567eede 100644 --- a/src/pages/gallery/components/PreviewCard.tsx +++ b/src/pages/gallery/components/PreviewCard.tsx @@ -13,10 +13,10 @@ interface IProps { const Cont = styled.div<{ disabled: boolean }>` background: #555 url(/image.svg) no-repeat center; - margin: 0 4px; - display: inline-block; - width: 192px; + display: block; + width: fit-content; height: 192px; + min-width: 100%; overflow: hidden; position: relative; cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'}; diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index 06f4f4954..0cf215817 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -7,22 +7,34 @@ import { getFile, getPreview, fetchData, + localFiles, } from 'services/fileService'; import { getData, LS_KEYS } from 'utils/storage/localStorage'; import PreviewCard from './components/PreviewCard'; -import { getActualKey } from 'utils/common/key'; +import { getActualKey, getToken } from 'utils/common/key'; import styled from 'styled-components'; import PhotoSwipe from 'components/PhotoSwipe/PhotoSwipe'; import { Options } from 'photoswipe'; import AutoSizer from 'react-virtualized-auto-sizer'; import { VariableSizeList as List } from 'react-window'; +import LoadingBar from 'react-top-loading-bar'; import Collections from './components/Collections'; -import SadFace from 'components/SadFace'; import Upload from './components/Upload'; -import { collection, fetchCollections, collectionLatestFile, getCollectionLatestFile, getFavItemIds } from 'services/collectionService'; +import { + collection, + fetchUpdatedCollections, + collectionLatestFile, + getCollectionLatestFile, + getFavItemIds, + getLocalCollections, +} from 'services/collectionService'; import constants from 'utils/strings/constants'; import { ErrorAlert } from './components/ErrorAlert'; +const DATE_CONTAINER_HEIGHT = 45; +const IMAGE_CONTAINER_HEIGHT = 200; +const NO_OF_PAGES = 2; + enum ITEM_TYPE { TIME = 'TIME', TILE = 'TILE', @@ -30,7 +42,7 @@ enum ITEM_TYPE { export enum FILE_TYPE { IMAGE, VIDEO, - OTHERS + OTHERS, } interface TimeStampListItem { @@ -68,8 +80,11 @@ const DeadCenter = styled.div` flex-direction: column; `; -const ListContainer = styled.div` - display: flex; +const ListContainer = styled.div<{ columns: number }>` + display: grid; + grid-template-columns: repeat(${(props) => props.columns}, 1fr); + grid-column-gap: 8px; + padding: 0 8px; max-width: 100%; color: #fff; @@ -87,13 +102,12 @@ const ListContainer = styled.div` `; const DateContainer = styled.div` - padding: 0 4px; + padding-top: 15px; `; export default function Gallery(props) { const router = useRouter(); const [loading, setLoading] = useState(false); - const [reload, setReload] = useState(0); const [collections, setCollections] = useState([]); const [collectionLatestFile, setCollectionLatestFile] = useState< collectionLatestFile[] @@ -108,37 +122,53 @@ export default function Gallery(props) { const fetching: { [k: number]: boolean } = {}; const [errorCode, setErrorCode] = useState(null); + const [sinceTime, setSinceTime] = useState(0); + + const [progress, setProgress] = useState(0); useEffect(() => { const key = getKey(SESSION_KEYS.ENCRYPTION_KEY); - const token = getData(LS_KEYS.USER).token; if (!key) { router.push('/'); } const main = async () => { setLoading(true); - const encryptionKey = await getActualKey(); - const collections = await fetchCollections(token, encryptionKey); - const data = await fetchData(token, collections); - const collectionLatestFile = await getCollectionLatestFile(collections, token); - const favItemIds = await getFavItemIds(data); - setCollections(collections); + const data = await localFiles(); + const collections = await getLocalCollections(); setData(data); - setCollectionLatestFile(collectionLatestFile); - setFavItemIds(favItemIds); + setCollections(collections); setLoading(false); + setProgress(80); + await syncWithRemote(); + setProgress(100); }; main(); props.setUploadButtonView(true); - }, [reload]); + }, []); - if (!data || loading) { - return ( -
- -
+ const syncWithRemote = async () => { + const token = getToken(); + const encryptionKey = await getActualKey(); + const updatedCollections = await fetchUpdatedCollections( + token, + encryptionKey ); - } + const data = await fetchData(token, updatedCollections); + const collections = await getLocalCollections(); + const collectionLatestFile = await getCollectionLatestFile( + collections, + data + ); + const favItemIds = await getFavItemIds(data); + if (updatedCollections.length > 0) { + setCollections(collections); + setData(data); + } + setCollectionLatestFile(collectionLatestFile); + setFavItemIds(favItemIds); + setSinceTime(new Date().getTime()); + props.setUploadButtonView(true); + }; const updateUrl = (index: number) => (url: string) => { data[index] = { @@ -147,7 +177,10 @@ export default function Gallery(props) { w: window.innerWidth, h: window.innerHeight, }; - if (data[index].metadata.fileType === FILE_TYPE.VIDEO && !data[index].html) { + if ( + data[index].metadata.fileType === FILE_TYPE.VIDEO && + !data[index].html + ) { data[index].html = `
@@ -158,7 +191,10 @@ export default function Gallery(props) { `; delete data[index].src; } - if (data[index].metadata.fileType === FILE_TYPE.IMAGE && !data[index].src) { + if ( + data[index].metadata.fileType === FILE_TYPE.IMAGE && + !data[index].src + ) { data[index].src = url; } setData(data); @@ -185,7 +221,7 @@ export default function Gallery(props) { const handleClose = () => { setOpen(false); - // setReload(Math.random()); + // syncWithRemote(); }; const onThumbnailClick = (index: number) => () => { @@ -225,7 +261,10 @@ export default function Gallery(props) { // ignore } } - if ((!item.src || item.src === item.msrc) && !fetching[item.dataIndex]) { + if ( + (!item.src || item.src === item.msrc) && + !fetching[item.dataIndex] + ) { fetching[item.dataIndex] = true; const url = await getFile(token, item); updateSrcUrl(item.dataIndex, url); @@ -251,6 +290,14 @@ export default function Gallery(props) { } }; + if (!data || loading) { + return ( +
+ +
+ ); + } + const selectCollection = (id?: string) => { const href = `/gallery?collection=${id || ''}`; router.push(href, undefined, { shallow: true }); @@ -287,6 +334,11 @@ export default function Gallery(props) { return ( <> + setProgress(0)} + /> setReload(Math.random())} + refetchData={syncWithRemote} setErrorCode={setErrorCode} - /> {filteredData.length ? ( @@ -322,20 +373,28 @@ export default function Gallery(props) { filteredData.forEach((item, index) => { if ( !isSameDay( - new Date(item.metadata.creationTime / 1000), + new Date( + item.metadata.creationTime / 1000 + ), new Date(currentDate) ) ) { - currentDate = item.metadata.creationTime / 1000; - const dateTimeFormat = new Intl.DateTimeFormat('en-IN', { - weekday: 'short', - year: 'numeric', - month: 'long', - day: 'numeric', - }); + currentDate = + item.metadata.creationTime / 1000; + const dateTimeFormat = new Intl.DateTimeFormat( + 'en-IN', + { + weekday: 'short', + year: 'numeric', + month: 'long', + day: 'numeric', + } + ); timeStampList.push({ itemType: ITEM_TYPE.TIME, - date: dateTimeFormat.format(currentDate), + date: dateTimeFormat.format( + currentDate + ), }); timeStampList.push({ itemType: ITEM_TYPE.TILE, @@ -345,7 +404,9 @@ export default function Gallery(props) { listItemIndex = 1; } else { if (listItemIndex < columns) { - timeStampList[timeStampList.length - 1].items.push(item); + timeStampList[ + timeStampList.length - 1 + ].items.push(item); listItemIndex++; } else { listItemIndex = 1; @@ -357,36 +418,61 @@ export default function Gallery(props) { } } }); - + const extraRowsToRender = Math.ceil( + (NO_OF_PAGES * height) / IMAGE_CONTAINER_HEIGHT + ); return ( - timeStampList[index].itemType === ITEM_TYPE.TIME - ? 30 - : 200 + timeStampList[index].itemType === + ITEM_TYPE.TIME + ? DATE_CONTAINER_HEIGHT + : IMAGE_CONTAINER_HEIGHT } height={height} width={width} itemCount={timeStampList.length} - key={`${router.query.collection}-${columns}`} + key={`${router.query.collection}-${columns}-${sinceTime}`} + overscanCount={extraRowsToRender} > {({ index, style }) => { return ( - - {timeStampList[index].itemType === - ITEM_TYPE.TIME ? ( - - {timeStampList[index].date} - - ) : ( - timeStampList[index].items.map((item, idx) => { + + {timeStampList[index] + .itemType === + ITEM_TYPE.TIME ? ( + + { + timeStampList[ + index + ].date + } + + ) : ( + timeStampList[ + index + ].items.map( + (item, idx) => { return getThumbnail( filteredData, - timeStampList[index].itemStartIndex + idx + timeStampList[ + index + ] + .itemStartIndex + + idx ); - }) - )} + } + ) + )} ); @@ -406,10 +492,10 @@ export default function Gallery(props) { /> ) : ( - -
{constants.NOTHING_HERE}
-
- )} + +
{constants.NOTHING_HERE}
+
+ )} ); } diff --git a/src/services/collectionService.ts b/src/services/collectionService.ts index 29b3b78f1..2a06a8715 100644 --- a/src/services/collectionService.ts +++ b/src/services/collectionService.ts @@ -97,32 +97,54 @@ const getCollections = async ( return await Promise.all(promises); } catch (e) { - console.log("getCollections falied- " + e); + console.log("getCollections failed- " + e); } }; -export const fetchCollections = async (token: string, key: string) => { - const collections = await getCollections(token, '0', key); - const favCollection = collections.filter(collection => collection.type === CollectionType.favorites); - await localForage.setItem('fav-collection', favCollection); +export const getLocalCollections = async (): Promise => { + const collections = await localForage.getItem('collections') as collection[] ?? []; return collections; +} +export const fetchUpdatedCollections = async (token: string, key: string) => { + const collectionUpdateTime = await localForage.getItem('collection-update-time') as string; + const updatedCollections = await getCollections(token, collectionUpdateTime ?? '0', key) || []; + const favCollection = await localForage.getItem('fav-collection') as collection[] ?? updatedCollections.filter(collection => collection.type === CollectionType.favorites); + const localCollections = await getLocalCollections(); + const allCollectionsInstances = [...localCollections, ...updatedCollections]; + var latestCollectionsInstances = new Map(); + allCollectionsInstances.forEach((collection) => { + if (!latestCollectionsInstances.has(collection.id) || latestCollectionsInstances.get(collection.id).updationTime < collection.updationTime) { + latestCollectionsInstances.set(collection.id, collection); + } + }); + let collections = []; + for (const [_, collection] of latestCollectionsInstances) { + collections.push(collection); + } + await localForage.setItem('fav-collection', favCollection); + await localForage.setItem('collections', collections); + return updatedCollections; }; -export const getCollectionLatestFile = async ( +export const getCollectionLatestFile = ( collections: collection[], - token -): Promise => { - return Promise.all( - collections.map(async collection => { - const sinceTime: string = (Number(await localForage.getItem(`${collection.id}-time`)) - 1).toString(); - const files: file[] = await getFiles([collection], sinceTime, "1", token) || []; + files: file[] +): collectionLatestFile[] => { + const latestFile = new Map(); + const collectionMap = new Map(); - return { - file: files[0], - collection, - } - })) -}; + collections.forEach(collection => collectionMap.set(Number(collection.id), collection)); + files.forEach(file => { + if (!latestFile.has(file.collectionID)) { + latestFile.set(file.collectionID, file) + } + }); + let allCollectionLatestFile: collectionLatestFile[] = []; + for (const [collectionID, file] of latestFile) { + allCollectionLatestFile.push({ collection: collectionMap.get(collectionID), file }); + } + return allCollectionLatestFile; +} export const getFavItemIds = async (files: file[]): Promise> => { diff --git a/src/services/fileService.ts b/src/services/fileService.ts index 2f1868a10..b9ae4263a 100644 --- a/src/services/fileService.ts +++ b/src/services/fileService.ts @@ -66,13 +66,23 @@ export const fetchData = async (token, collections) => { ); } +export const localFiles = async () => { + let files: Array = (await localForage.getItem('files')) || []; + return files; +} + export const fetchFiles = async ( token: string, collections: collection[] ) => { - let files: Array = (await localForage.getItem('files')) || []; - const fetchedFiles = await getFiles(collections, null, "100", token); - + let files = await localFiles(); + const collectionUpdationTime = new Map(); + let fetchedFiles = []; + for (let collection of collections) { + const files = await getFiles(collection, null, 100, token); + fetchedFiles.push(...files); + collectionUpdationTime.set(collection.id, files.length > 0 ? files.slice(-1)[0].updationTime.toString() : "0"); + } files.push(...fetchedFiles); var latestFiles = new Map(); files.forEach((file) => { @@ -82,7 +92,7 @@ export const fetchFiles = async ( } }); files = []; - for (const [_, file] of latestFiles.entries()) { + for (const [_, file] of latestFiles) { if (!file.isDeleted) files.push(file); } @@ -90,53 +100,57 @@ export const fetchFiles = async ( (a, b) => b.metadata.creationTime - a.metadata.creationTime ); await localForage.setItem('files', files); + for (let [collectionID, updationTime] of collectionUpdationTime) { + await localForage.setItem(`${collectionID}-time`, updationTime); + } + let updationTime = await localForage.getItem('collection-update-time') as number; + for (let collection of collections) { + updationTime = Math.max(updationTime, collection.updationTime); + } + await localForage.setItem('collection-update-time', updationTime); return files; }; -export const getFiles = async (collections: collection[], sinceTime: string, limit: string, token: string): Promise => { +export const getFiles = async (collection: collection, sinceTime: string, limit: number, token: string): Promise => { try { const worker = await new CryptoWorker(); let promises: Promise[] = []; - for (const index in collections) { - const collection = collections[index]; - if (collection.isDeleted) { - // TODO: Remove files in this collection from localForage and cache - continue; - } - let time = - sinceTime || (await localForage.getItem(`${collection.id}-time`)) || "0"; - let resp; - do { - resp = await HTTPService.get(`${ENDPOINT}/collections/diff`, { - collectionID: collection.id, - sinceTime: time, - limit, - }, - { - 'X-Auth-Token': token - }); - promises.push(...resp.data.diff.map( - async (file: file) => { - if (!file.isDeleted) { - - file.key = await worker.decryptB64( - file.encryptedKey, - file.keyDecryptionNonce, - collection.key - ); - file.metadata = await worker.decryptMetadata(file); - } - return file; - } - )); - - if (resp.data.diff.length) { - time = resp.data.diff.slice(-1)[0].updationTime.toString(); - } - } while (resp.data.diff.length); - await localForage.setItem(`${collection.id}-time`, time); + if (collection.isDeleted) { + // TODO: Remove files in this collection from localForage and cache + return; } - return Promise.all(promises); + let time = + sinceTime || (await localForage.getItem(`${collection.id}-time`)) || "0"; + let resp; + do { + resp = await HTTPService.get(`${ENDPOINT}/collections/diff`, { + collectionID: collection.id, + sinceTime: time, + limit: limit.toString(), + }, + { + 'X-Auth-Token': token + }); + promises.push(...resp.data.diff.map( + async (file: file) => { + if (!file.isDeleted) { + + file.key = await worker.decryptB64( + file.encryptedKey, + file.keyDecryptionNonce, + collection.key + ); + file.metadata = await worker.decryptMetadata(file); + } + return file; + } + )); + + if (resp.data.diff.length) { + time = resp.data.diff.slice(-1)[0].updationTime.toString(); + } + } while (resp.data.diff.length === limit); + return await Promise.all(promises); } catch (e) { console.log("Get files failed" + e); } diff --git a/src/utils/common/key.ts b/src/utils/common/key.ts index ecd680b74..2ffa4cc6b 100644 --- a/src/utils/common/key.ts +++ b/src/utils/common/key.ts @@ -16,5 +16,5 @@ export const getActualKey = async () => { } export const getToken = () => { - return getData(LS_KEYS.USER).token; + return getData(LS_KEYS.USER)?.token; } diff --git a/yarn.lock b/yarn.lock index 87db1dc57..a1a24cbcc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1673,6 +1673,11 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +attr-accept@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b" + integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg== + axios@^0.20.0: version "0.20.0" resolved "https://registry.yarnpkg.com/axios/-/axios-0.20.0.tgz#057ba30f04884694993a8cd07fa394cff11c50bd" @@ -2848,6 +2853,11 @@ execa@^4.0.2: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +exif-js@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/exif-js/-/exif-js-2.3.0.tgz#9d10819bf571f873813e7640241255ab9ce1a814" + integrity sha1-nRCBm/Vx+HOBPnZAJBJVq5zhqBQ= + expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" @@ -2948,6 +2958,13 @@ figgy-pudding@^3.5.1: resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== +file-selector@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.2.4.tgz#7b98286f9dbb9925f420130ea5ed0a69238d4d80" + integrity sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA== + dependencies: + tslib "^2.0.3" + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -4923,6 +4940,15 @@ react-dom@16.13.1: prop-types "^15.6.2" scheduler "^0.19.1" +react-dropzone@^11.2.4: + version "11.3.0" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-11.3.0.tgz#516561c5003e0c0f7d63bd5621f410b1b3496ab3" + integrity sha512-5ffIOi5Uf1X52m4fN8QdcRuAX88nQPfmx6HTTIfF9I3W9Ss1SvRDl/ruZmFf53K7+g3TSaIgVw6a9EK7XoDwHw== + dependencies: + attr-accept "^2.2.1" + file-selector "^0.2.2" + prop-types "^15.7.2" + react-fast-compare@^2.0.1: version "2.0.4" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" @@ -4967,6 +4993,11 @@ react-refresh@0.8.3: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f" integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg== +react-top-loading-bar@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/react-top-loading-bar/-/react-top-loading-bar-2.0.1.tgz#c8805ad9c1068766fdd3cadd414e67cfdf1878e9" + integrity sha512-wkRlK9Rte4TU817GDcjlsCoDOxrrnvsNvK609FKyio0EIrmmqjQDz5DB8HbN88CHNZBy5Lh/OBALc03ioWFPuQ== + react-transition-group@^4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9" @@ -5851,6 +5882,11 @@ tslib@^1.10.0, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== +tslib@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" + integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== + tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" @@ -5884,10 +5920,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" - integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== +typescript@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" + integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== uncontrollable@^7.0.0: version "7.1.1"