Merge branch 'master' into handle_subscription_errors
This commit is contained in:
commit
49d981e86a
12 changed files with 299 additions and 141 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,7 +4,6 @@
|
|||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
package-lock.json
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
|
|
@ -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": {
|
||||
|
|
BIN
public/fav-button.png
Normal file
BIN
public/fav-button.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
File diff suppressed because one or more lines are too long
|
@ -161,7 +161,6 @@ export default function App({ Component, pageProps }) {
|
|||
<FullScreenDropZone
|
||||
closeModal={closeUploadModal}
|
||||
showModal={showUploadModal}
|
||||
uploadModalView={uploadModalView}
|
||||
>
|
||||
<Head>
|
||||
<title>ente.io | Privacy friendly alternative to Google Photos</title>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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'};
|
||||
|
|
|
@ -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<collection[]>([]);
|
||||
const [collectionLatestFile, setCollectionLatestFile] = useState<
|
||||
collectionLatestFile[]
|
||||
|
@ -108,37 +122,53 @@ export default function Gallery(props) {
|
|||
const fetching: { [k: number]: boolean } = {};
|
||||
const [errorCode, setErrorCode] = useState<number>(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 (
|
||||
<div className='text-center'>
|
||||
<Spinner animation='border' variant='primary' />
|
||||
</div>
|
||||
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 = `
|
||||
<div class="video-loading">
|
||||
<img src="${url}" />
|
||||
|
@ -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 (
|
||||
<div className="text-center">
|
||||
<Spinner animation="border" variant="primary" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<>
|
||||
<ErrorAlert errorCode={errorCode} />
|
||||
<LoadingBar
|
||||
color="#007bff"
|
||||
progress={progress}
|
||||
onLoaderFinished={() => setProgress(0)}
|
||||
/>
|
||||
<Collections
|
||||
collections={collections}
|
||||
selected={router.query.collection?.toString()}
|
||||
|
@ -297,9 +349,8 @@ export default function Gallery(props) {
|
|||
closeUploadModal={props.closeUploadModal}
|
||||
showUploadModal={props.showUploadModal}
|
||||
collectionLatestFile={collectionLatestFile}
|
||||
refetchData={() => setReload(Math.random())}
|
||||
refetchData={syncWithRemote}
|
||||
setErrorCode={setErrorCode}
|
||||
|
||||
/>
|
||||
{filteredData.length ? (
|
||||
<Container>
|
||||
|
@ -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 (
|
||||
<List
|
||||
itemSize={(index) =>
|
||||
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 (
|
||||
<ListItem style={style}>
|
||||
<ListContainer>
|
||||
{timeStampList[index].itemType ===
|
||||
ITEM_TYPE.TIME ? (
|
||||
<DateContainer>
|
||||
{timeStampList[index].date}
|
||||
</DateContainer>
|
||||
) : (
|
||||
timeStampList[index].items.map((item, idx) => {
|
||||
<ListContainer
|
||||
columns={
|
||||
timeStampList[index]
|
||||
.itemType ===
|
||||
ITEM_TYPE.TIME
|
||||
? 1
|
||||
: columns
|
||||
}
|
||||
>
|
||||
{timeStampList[index]
|
||||
.itemType ===
|
||||
ITEM_TYPE.TIME ? (
|
||||
<DateContainer>
|
||||
{
|
||||
timeStampList[
|
||||
index
|
||||
].date
|
||||
}
|
||||
</DateContainer>
|
||||
) : (
|
||||
timeStampList[
|
||||
index
|
||||
].items.map(
|
||||
(item, idx) => {
|
||||
return getThumbnail(
|
||||
filteredData,
|
||||
timeStampList[index].itemStartIndex + idx
|
||||
timeStampList[
|
||||
index
|
||||
]
|
||||
.itemStartIndex +
|
||||
idx
|
||||
);
|
||||
})
|
||||
)}
|
||||
}
|
||||
)
|
||||
)}
|
||||
</ListContainer>
|
||||
</ListItem>
|
||||
);
|
||||
|
@ -406,10 +492,10 @@ export default function Gallery(props) {
|
|||
/>
|
||||
</Container>
|
||||
) : (
|
||||
<DeadCenter>
|
||||
<div>{constants.NOTHING_HERE}</div>
|
||||
</DeadCenter>
|
||||
)}
|
||||
<DeadCenter>
|
||||
<div>{constants.NOTHING_HERE}</div>
|
||||
</DeadCenter>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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<collection[]> => {
|
||||
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<string, collection>();
|
||||
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<collectionLatestFile[]> => {
|
||||
return Promise.all(
|
||||
collections.map(async collection => {
|
||||
const sinceTime: string = (Number(await localForage.getItem<string>(`${collection.id}-time`)) - 1).toString();
|
||||
const files: file[] = await getFiles([collection], sinceTime, "1", token) || [];
|
||||
files: file[]
|
||||
): collectionLatestFile[] => {
|
||||
const latestFile = new Map<number, file>();
|
||||
const collectionMap = new Map<number, collection>();
|
||||
|
||||
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<Set<number>> => {
|
||||
|
||||
|
|
|
@ -66,13 +66,23 @@ export const fetchData = async (token, collections) => {
|
|||
);
|
||||
}
|
||||
|
||||
export const localFiles = async () => {
|
||||
let files: Array<file> = (await localForage.getItem<file[]>('files')) || [];
|
||||
return files;
|
||||
}
|
||||
|
||||
export const fetchFiles = async (
|
||||
token: string,
|
||||
collections: collection[]
|
||||
) => {
|
||||
let files: Array<file> = (await localForage.getItem<file[]>('files')) || [];
|
||||
const fetchedFiles = await getFiles(collections, null, "100", token);
|
||||
|
||||
let files = await localFiles();
|
||||
const collectionUpdationTime = new Map<string, string>();
|
||||
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<string, file>();
|
||||
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<file[]> => {
|
||||
export const getFiles = async (collection: collection, sinceTime: string, limit: number, token: string): Promise<file[]> => {
|
||||
try {
|
||||
const worker = await new CryptoWorker();
|
||||
let promises: Promise<file>[] = [];
|
||||
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<string>(`${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<string>(`${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);
|
||||
}
|
||||
|
|
|
@ -16,5 +16,5 @@ export const getActualKey = async () => {
|
|||
}
|
||||
|
||||
export const getToken = () => {
|
||||
return getData(LS_KEYS.USER).token;
|
||||
return getData(LS_KEYS.USER)?.token;
|
||||
}
|
||||
|
|
44
yarn.lock
44
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"
|
||||
|
|
Loading…
Add table
Reference in a new issue